├── page ├── src │ ├── vite-env.d.ts │ ├── App.css │ ├── index.tsx │ ├── index.css │ ├── assets │ │ └── solid.svg │ └── App.tsx ├── uno.config.ts ├── vite.config.ts ├── tsconfig.node.json ├── .gitignore ├── index.html ├── package.json ├── tsconfig.json ├── README.md ├── public │ └── vite.svg └── pnpm-lock.yaml ├── tasks.sh ├── package.json ├── .gitignore ├── pnpm-lock.yaml ├── .github └── workflows │ └── page.yml ├── README.md ├── LICENSE ├── Cargo.toml ├── tasks.mjs └── src ├── lib.rs └── worker ├── db.rs └── main.rs /page/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tasks.sh: -------------------------------------------------------------------------------- 1 | cargo build -r 2 | rm -rf .data 3 | rm -rf *.jsonl 4 | 5 | node tasks.mjs 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "chalk": "5.3.0", 4 | "tiny-async-pool": "2.1.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /page/uno.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "unocss"; 2 | 3 | export default defineConfig({ 4 | // ...UnoCSS options 5 | }); 6 | -------------------------------------------------------------------------------- /page/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | padding: 1rem; 3 | } 4 | 5 | .apexcharts-menu.apexcharts-menu-open { 6 | background: #101010 !important; 7 | } -------------------------------------------------------------------------------- /page/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import solid from "vite-plugin-solid"; 3 | import UnoCSS from "unocss/vite"; 4 | 5 | export default defineConfig({ 6 | plugins: [solid(), UnoCSS()], 7 | base: "/rust-storage-bench/" 8 | }); 9 | -------------------------------------------------------------------------------- /page/src/index.tsx: -------------------------------------------------------------------------------- 1 | /* @refresh reload */ 2 | 3 | import "./index.css"; 4 | import "virtual:uno.css"; 5 | 6 | import { render } from "solid-js/web"; 7 | import App from "./App"; 8 | 9 | const root = document.getElementById("root") 10 | 11 | render(() => , root!) 12 | -------------------------------------------------------------------------------- /page/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /page/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | 8 | font-synthesis: none; 9 | text-rendering: optimizeLegibility; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | -------------------------------------------------------------------------------- /page/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | .vite 26 | -------------------------------------------------------------------------------- /page/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Rust Storage Engine Benchmark 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /page/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "page", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "solid-apexcharts": "0.3.2", 13 | "solid-js": "1.8.5" 14 | }, 15 | "devDependencies": { 16 | "typescript": "5.2.2", 17 | "unocss": "0.58.0", 18 | "vite": "4", 19 | "vite-plugin-solid": "2.7.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | .data 17 | *.jsonl 18 | node_modules 19 | .results 20 | -------------------------------------------------------------------------------- /page/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["es2022", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "preserve", 16 | "jsxImportSource": "solid-js", 17 | 18 | /* Linting */ 19 | "strict": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "noFallthroughCasesInSwitch": true 23 | }, 24 | "include": ["src"], 25 | "references": [{ "path": "./tsconfig.node.json" }] 26 | } 27 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | chalk: 12 | specifier: 5.3.0 13 | version: 5.3.0 14 | tiny-async-pool: 15 | specifier: 2.1.0 16 | version: 2.1.0 17 | 18 | packages: 19 | 20 | chalk@5.3.0: 21 | resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} 22 | engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} 23 | 24 | tiny-async-pool@2.1.0: 25 | resolution: {integrity: sha512-ltAHPh/9k0STRQqaoUX52NH4ZQYAJz24ZAEwf1Zm+HYg3l9OXTWeqWKyYsHu40wF/F0rxd2N2bk5sLvX2qlSvg==} 26 | 27 | snapshots: 28 | 29 | chalk@5.3.0: {} 30 | 31 | tiny-async-pool@2.1.0: {} 32 | -------------------------------------------------------------------------------- /.github/workflows/page.yml: -------------------------------------------------------------------------------- 1 | name: Deploy page 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: pnpm/action-setup@v2 14 | with: 15 | version: 9 16 | - name: Use Node.js 22 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: 22 20 | cache: pnpm 21 | cache-dependency-path: page/pnpm-lock.yaml 22 | - name: Install dependencies 23 | run: pnpm i 24 | working-directory: page 25 | - name: Generate page 26 | run: pnpm build 27 | working-directory: page 28 | - name: Deploy page 29 | uses: JamesIves/github-pages-deploy-action@v4 30 | with: 31 | folder: page/dist 32 | -------------------------------------------------------------------------------- /page/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```bash 4 | $ npm install # or pnpm install or yarn install 5 | ``` 6 | 7 | ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) 8 | 9 | ## Available Scripts 10 | 11 | In the project directory, you can run: 12 | 13 | ### `npm run dev` 14 | 15 | Runs the app in the development mode.
16 | Open [http://localhost:5173](http://localhost:5173) to view it in the browser. 17 | 18 | ### `npm run build` 19 | 20 | Builds the app for production to the `dist` folder.
21 | It correctly bundles Solid in production mode and optimizes the build for the best performance. 22 | 23 | The build is minified and the filenames include the hashes.
24 | Your app is ready to be deployed! 25 | 26 | ## Deployment 27 | 28 | Learn more about deploying your application with the [documentations](https://vitejs.dev/guide/static-deploy.html) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-storage-bench 2 | 3 | Benchmarking Rust storage engines: 4 | 5 | - fjall Δ ★ (https://github.com/fjall-rs/fjall) 6 | - jammdb Ω (https://github.com/pjtatlow/jammdb) 7 | - nebari Ω (https://github.com/khonsulabs/nebari) 8 | - persy Ω ★ (https://persy.rs) 9 | - redb Ω ★ (https://www.redb.org) 10 | - sled Ψ (https://sled.rs) 11 | 12 | Non-Rust (bindings): 13 | 14 | - rocksdb Δ (https://rocksdb.org/) 15 | - heed Ω (https://github.com/meilisearch/heed) 16 | 17 | --- 18 | 19 | - Δ LSM based 20 | - Ω B-tree based 21 | - Ψ Hybrid (Bw-Tree, ...) 22 | - ★ has reached 1.0 23 | 24 | ## Example usage 25 | 26 | ``` 27 | cargo build -r 28 | alias bencher='cargo run --bin daemon -r --' 29 | 30 | bencher --out task_e_fjall_lcs.jsonl --workload task-e --backend fjall --minutes 5 --key-size 8 --value-size 256 --items 1000 --cache-size 1000000 31 | ``` 32 | 33 | ## Run many benchmarks 34 | 35 | ``` 36 | node tasks.mjs <...filter> 37 | ``` 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 marvin-j97 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-storage-bench" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [[bin]] 9 | name = "worker" 10 | path = "src/worker/main.rs" 11 | 12 | [features] 13 | default = ["heed", "rocksdb"] 14 | rocksdb = ["dep:rocksdb"] 15 | heed = ["dep:heed"] 16 | 17 | # [target.'cfg(not(target_env = "msvc"))'.dependencies] 18 | # jemallocator = "0.3.2" 19 | 20 | [dependencies] 21 | clap = { version = "4.4.10", features = ["derive"] } 22 | # bloodstone = { package = "sled", version = "1.0.0-alpha.121" } 23 | sled = { version = "0.34.7", features = ["compression"] } 24 | fjall = { version = "1.4.0" } 25 | nanoid = "0.4.0" 26 | rand = "0.8.5" 27 | sysinfo = { version = "0.30.1", features = ["serde"] } 28 | serde = { version = "1.0.193", features = ["derive", "rc"] } 29 | serde_json = "1.0.108" 30 | fs_extra = "1.3.0" 31 | env_logger = "0.10.1" 32 | log = { version = "0.4.20", features = ["release_max_level_trace"] } 33 | persy = { version = "1.5.0", features = ["background_ops"] } 34 | jammdb = "0.11.0" 35 | zipf = "7.0.1" 36 | redb = "2.1.1" 37 | nebari = "0.5.5" 38 | heed = { version = "0.20.0", optional = true } 39 | rocksdb = { version = "0.22.0", optional = true, default-features = false, features = [ 40 | "lz4", 41 | ] } 42 | -------------------------------------------------------------------------------- /page/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /page/src/assets/solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tasks.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | import { spawn } from "node:child_process"; 4 | import { existsSync, mkdirSync, rmSync } from "node:fs"; 5 | import { resolve } from "node:path"; 6 | 7 | import chalk from "chalk"; 8 | import asyncPool from "tiny-async-pool"; 9 | 10 | if (existsSync(".data")) { 11 | rmSync(".data", { 12 | recursive: true, 13 | }); 14 | } 15 | 16 | /* const CLEAN_DATA_FOLDER_AFTER_EACH_TASK = true; */ 17 | const PARALLELISM = 1; 18 | 19 | const steps = [ 20 | { 21 | tasks: ["d", "e", "f", "g", "h"], 22 | backends: ["fjall_lcs", "fjall_stcs", "persy", "redb", "sled"], 23 | minutes: 5, 24 | outFolder: ".results/nosync/5m/low_cache", 25 | fsync: false, 26 | valueSize: 128, 27 | cacheSize: 128_000, 28 | }, 29 | { 30 | tasks: ["d", "e", "f", "g", "h"], 31 | backends: ["fjall_lcs", "fjall_stcs", "persy", "redb", "sled"], 32 | minutes: 5, 33 | outFolder: ".results/nosync/5m/high_cache", 34 | fsync: false, 35 | valueSize: 128, 36 | cacheSize: 32_000_000, 37 | }, 38 | ] 39 | 40 | for (const config of steps) { 41 | let tasks = []; 42 | 43 | for (const task of config.tasks) { 44 | for (const backend of config.backends) { 45 | const args = [ 46 | ...(config.fsync ? ["--fsync"] : []), 47 | ...["--threads", "1"], 48 | ...["--minutes", config.minutes], 49 | ...["--key-size", 8], 50 | ...["--value-size", config.valueSize], 51 | ...["--items", 100], 52 | ...["--cache-size", config.cacheSize], 53 | ...["--lsm-block-size", config.blockSize ?? 4_096], 54 | ]; 55 | 56 | const folder = resolve(config.outFolder, `task_${task}`); 57 | const out = resolve(folder, `${backend}.jsonl`); 58 | mkdirSync(folder, { recursive: true }); 59 | 60 | if (backend === "fjall_stcs") { 61 | args.push("--lsm-compaction", "tiered"); 62 | } 63 | 64 | const be = backend.startsWith("fjall_") ? "fjall" : backend; 65 | 66 | args.push( 67 | ...["--out", out], 68 | ...["--workload", `task-${task}`], 69 | ...["--backend", be] 70 | ); 71 | 72 | tasks.push( 73 | args 74 | ); 75 | } 76 | } 77 | 78 | console.log("Running tasks", tasks.map(x => x.join(" "))); 79 | 80 | async function processTask(task) { 81 | await new Promise((resolve, reject) => { 82 | const args = ["run", "-r", "--", ...task]; 83 | 84 | console.error( 85 | chalk.blueBright(`Spawning: cargo ${args.join(" ")}`) 86 | ); 87 | 88 | const childProcess = spawn("cargo", args, { 89 | shell: true, 90 | stdio: "pipe" 91 | }); 92 | childProcess.stdout.on("data", (buf) => console.log( 93 | chalk.grey(`${String(buf)}`) 94 | )); 95 | childProcess.stderr.on("data", (buf) => console.error( 96 | chalk.yellow(`${String(buf)}`) 97 | )); 98 | 99 | // @ts-ignore 100 | childProcess.on('exit', () => resolve()); 101 | childProcess.on('error', reject); 102 | }); 103 | 104 | // TODO: need to only delete subfolder of specific task 105 | // TODO: also each invocation needs its own .data subfolder... 106 | /* if (CLEAN_DATA_FOLDER_AFTER_EACH_TASK) { 107 | if (existsSync(".data")) { 108 | rmSync(".data", { 109 | recursive: true, 110 | }); 111 | } 112 | } */ 113 | 114 | return task; 115 | } 116 | 117 | // Filter out sled, if fsync, because it doesn't actually call fsync??? UNLESS it's Workload C (read-only) 118 | if (config.fsync) { 119 | tasks = tasks.filter(args => ["sled", "bloodstone"].every(term => (args.join(" ")).includes("task_c") || !(args.join(" ")).includes(term))); 120 | } 121 | else { 122 | // Filter out jammdb & nebari, if !fsync, because they always fsync??? UNLESS it's Workload C (read-only) 123 | tasks = tasks.filter(args => ["jamm", "nebari"].every(term => (args.join(" ")).includes("task_c") || !(args.join(" ")).includes(term))); 124 | } 125 | 126 | for await (const name of asyncPool(PARALLELISM, tasks, processTask)) { 127 | console.log( 128 | chalk.greenBright(`${name.join(" ")} done`) 129 | ); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use clap::{Parser, ValueEnum}; 2 | use serde::Serialize; 3 | 4 | #[derive(Copy, Eq, PartialEq, Debug, Clone, ValueEnum, Serialize)] 5 | #[clap(rename_all = "kebab_case")] 6 | pub enum Backend { 7 | Sled, 8 | // Bloodstone, 9 | Fjall, 10 | Persy, 11 | JammDb, 12 | Redb, 13 | Nebari, 14 | 15 | #[cfg(feature = "heed")] 16 | Heed, 17 | 18 | #[cfg(feature = "rocksdb")] 19 | RocksDb, 20 | } 21 | 22 | impl std::fmt::Display for Backend { 23 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 24 | write!( 25 | f, 26 | "{}", 27 | match self { 28 | Self::Sled => "sled 0.34.7", 29 | // Self::Bloodstone => "sled 1.0.0-alpha.118", 30 | Self::Fjall => "fjall 1.4.0", 31 | Self::Persy => "persy 1.5.0", 32 | Self::JammDb => "jammdb 0.11.0", 33 | Self::Redb => "redb 2.1.1", 34 | Self::Nebari => "nebari 0.5.5", 35 | 36 | #[cfg(feature = "heed")] 37 | Self::Heed => "heed 0.20.0", 38 | 39 | #[cfg(feature = "rocksdb")] 40 | Self::RocksDb => "rocksdb 0.22.0", 41 | } 42 | ) 43 | } 44 | } 45 | 46 | #[derive(Copy, Debug, Clone, ValueEnum, Serialize, PartialEq, Eq)] 47 | #[clap(rename_all = "kebab_case")] 48 | pub enum Workload { 49 | /// Workload A: Update heavy workload 50 | /// 51 | /// Application example: Session store recording recent actions 52 | TaskA, 53 | 54 | /// Workload B: Read mostly workload 55 | /// 56 | /// Application example: photo tagging; add a tag is an update, but most operations are to read tags 57 | TaskB, 58 | 59 | /// Workload C: Read only 60 | /// 61 | /// Application example: user profile cache, where profiles are constructed elsewhere (e.g., Hadoop) 62 | TaskC, 63 | 64 | /// Workload D: Read latest workload with light inserts 65 | /// 66 | /// Application example: user status updates; people want to read the latest 67 | TaskD, 68 | 69 | /// Workload E: Read latest workload with heavy inserts 70 | /// 71 | /// Application example: Event logging, getting the latest events 72 | TaskE, 73 | 74 | /// Workload F: Read zipfian workload with light inserts 75 | TaskF, 76 | 77 | /// Workload G: Read zipfian workload with heavy inserts 78 | TaskG, 79 | } 80 | 81 | #[derive(Clone, Debug, Eq, PartialEq, clap::ValueEnum)] 82 | pub enum LsmCompaction { 83 | Leveled, 84 | Tiered, 85 | } 86 | 87 | impl std::fmt::Display for LsmCompaction { 88 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 89 | write!( 90 | f, 91 | "{}", 92 | match self { 93 | Self::Leveled => "LCS", 94 | Self::Tiered => "STCS", 95 | } 96 | ) 97 | } 98 | } 99 | 100 | /// CLI argument parse 101 | #[derive(Clone, Parser, Debug)] 102 | #[command(author = "marvin-j97", version = env!("CARGO_PKG_VERSION"), about = "Rust KV-store profiler")] 103 | #[command(propagate_version = true)] 104 | pub struct Args { 105 | #[arg(long, value_enum)] 106 | pub backend: Backend, 107 | 108 | #[arg(long, value_enum)] 109 | pub workload: Workload, 110 | 111 | #[arg(long, default_value_t = 1)] 112 | pub threads: u8, 113 | 114 | #[arg(long)] 115 | pub items: u32, 116 | 117 | #[arg(long)] 118 | pub key_size: u8, 119 | 120 | #[arg(long)] 121 | pub value_size: u32, 122 | 123 | /// Block size for LSM-trees 124 | #[arg(long, default_value_t = 4_096)] 125 | pub lsm_block_size: u16, 126 | 127 | /// Compaction for LSM-trees 128 | #[arg(long, value_enum, default_value_t = LsmCompaction::Leveled)] 129 | pub lsm_compaction: LsmCompaction, 130 | 131 | /// Intermittenly flush sled to keep memory usage sane 132 | /// This is hopefully a temporary workaround 133 | #[arg(long, default_value_t = false)] 134 | pub sled_flush: bool, 135 | 136 | #[arg(long, default_value_t = 16_000_000)] 137 | pub cache_size: u32, 138 | 139 | #[arg(long, default_value = "log.jsonl")] 140 | pub out: String, 141 | 142 | #[arg(long, default_value_t = false)] 143 | pub snapshot_heap: bool, 144 | 145 | #[arg(long, default_value_t = false)] 146 | pub fsync: bool, 147 | 148 | #[arg(long, default_value_t = 1)] 149 | pub minutes: u16, 150 | } 151 | -------------------------------------------------------------------------------- /src/worker/db.rs: -------------------------------------------------------------------------------- 1 | use crate::Args; 2 | use nebari::{io::fs::StdFile, tree::Unversioned}; 3 | use redb::TableDefinition; 4 | use std::{ 5 | sync::{atomic::AtomicU64, Arc}, 6 | time::Instant, 7 | }; 8 | 9 | #[derive(Clone)] 10 | pub struct DatabaseWrapper { 11 | pub inner: GenericDatabase, 12 | pub write_ops: Arc, 13 | pub read_ops: Arc, 14 | pub delete_ops: Arc, 15 | pub scan_ops: Arc, 16 | 17 | pub write_latency: Arc, 18 | pub read_latency: Arc, 19 | } 20 | 21 | impl std::ops::Deref for DatabaseWrapper { 22 | type Target = GenericDatabase; 23 | 24 | fn deref(&self) -> &Self::Target { 25 | &self.inner 26 | } 27 | } 28 | 29 | #[derive(Clone)] 30 | pub enum GenericDatabase { 31 | Fjall { 32 | keyspace: fjall::Keyspace, 33 | db: fjall::PartitionHandle, 34 | }, 35 | Sled(sled::Db), 36 | // Bloodstone(bloodstone::Db), 37 | Jamm(jammdb::DB), 38 | Persy(persy::Persy), 39 | Redb(Arc), 40 | Nebari { 41 | roots: nebari::Roots, 42 | tree: nebari::Tree, 43 | }, 44 | 45 | #[cfg(feature = "heed")] 46 | Heed { 47 | db: heed::Database, 48 | env: heed::Env, 49 | }, 50 | 51 | #[cfg(feature = "rocksdb")] 52 | RocksDb(Arc), 53 | } 54 | 55 | const TABLE: TableDefinition<&[u8], Vec> = TableDefinition::new("data"); 56 | 57 | impl DatabaseWrapper { 58 | pub fn insert(&self, key: &[u8], value: &[u8], durable: bool, args: Arc) { 59 | match &self.inner { 60 | #[cfg(feature = "rocksdb")] 61 | GenericDatabase::RocksDb(db) => { 62 | let start = Instant::now(); 63 | 64 | db.put(key, value).unwrap(); 65 | 66 | if durable { 67 | db.flush_wal(true).unwrap(); 68 | } 69 | 70 | self.write_latency.fetch_add( 71 | start.elapsed().as_micros() as u64, 72 | std::sync::atomic::Ordering::Relaxed, 73 | ); 74 | } 75 | 76 | #[cfg(feature = "heed")] 77 | GenericDatabase::Heed { env, db } => { 78 | let start = Instant::now(); 79 | 80 | let mut wtxn = env.write_txn().unwrap(); 81 | db.put(&mut wtxn, key, value).unwrap(); 82 | 83 | wtxn.commit().unwrap(); 84 | 85 | self.write_latency.fetch_add( 86 | start.elapsed().as_micros() as u64, 87 | std::sync::atomic::Ordering::Relaxed, 88 | ); 89 | } 90 | GenericDatabase::Nebari { roots: _, tree } => { 91 | if !durable { 92 | log::warn!("WARNING: Nebari does not support eventual durability"); 93 | } 94 | 95 | let key = key.to_vec(); 96 | let value = key.to_vec(); 97 | 98 | let start = Instant::now(); 99 | 100 | tree.set(key, value).unwrap(); 101 | 102 | self.write_latency.fetch_add( 103 | start.elapsed().as_micros() as u64, 104 | std::sync::atomic::Ordering::Relaxed, 105 | ); 106 | } 107 | GenericDatabase::Fjall { keyspace, db } => { 108 | let start = Instant::now(); 109 | 110 | db.insert(key, value).unwrap(); 111 | 112 | if durable { 113 | keyspace.persist(fjall::FlushMode::SyncAll).unwrap(); 114 | } 115 | 116 | self.write_latency.fetch_add( 117 | start.elapsed().as_micros() as u64, 118 | std::sync::atomic::Ordering::Relaxed, 119 | ); 120 | } 121 | GenericDatabase::Sled(db) => { 122 | let start = Instant::now(); 123 | 124 | db.insert(key, value).unwrap(); 125 | 126 | if durable { 127 | db.flush().unwrap(); 128 | } 129 | 130 | self.write_latency.fetch_add( 131 | start.elapsed().as_micros() as u64, 132 | std::sync::atomic::Ordering::Relaxed, 133 | ); 134 | } 135 | // GenericDatabase::Bloodstone(db) => { 136 | // let start = Instant::now(); 137 | 138 | // db.insert(key, value).unwrap(); 139 | 140 | // if durable { 141 | // db.flush().unwrap(); 142 | // } else if args.sled_flush { 143 | // // NOTE: TODO: OOM Workaround 144 | // // Intermittenly flush sled to keep memory usage sane 145 | // // This is hopefully a temporary workaround 146 | // if self.write_ops.load(std::sync::atomic::Ordering::Relaxed) % 5_000_000 == 0 { 147 | // db.flush().unwrap(); 148 | // } 149 | // } 150 | 151 | // self.write_latency.fetch_add( 152 | // start.elapsed().as_micros() as u64, 153 | // std::sync::atomic::Ordering::Relaxed, 154 | // ); 155 | // } 156 | GenericDatabase::Jamm(db) => { 157 | if !durable { 158 | log::warn!("WARNING: JammDB does not support eventual durability",); 159 | } 160 | 161 | let start = Instant::now(); 162 | 163 | let tx = db.tx(true).unwrap(); 164 | let bucket = tx.get_bucket("data").unwrap(); 165 | bucket.put(key, value).unwrap(); 166 | tx.commit().unwrap(); 167 | 168 | self.write_latency.fetch_add( 169 | start.elapsed().as_micros() as u64, 170 | std::sync::atomic::Ordering::Relaxed, 171 | ); 172 | } 173 | GenericDatabase::Persy(db) => { 174 | use persy::{PersyId, TransactionConfig}; 175 | 176 | let key = String::from_utf8_lossy(key); 177 | let key = key.to_string(); 178 | 179 | let start = Instant::now(); 180 | 181 | let mut tx = db 182 | .begin_with(TransactionConfig::new().set_background_sync(!durable)) 183 | .unwrap(); 184 | let id = tx.insert("data", value).unwrap(); 185 | 186 | tx.put::("primary", key, id).unwrap(); 187 | let prepared = tx.prepare().unwrap(); 188 | 189 | prepared.commit().unwrap(); 190 | 191 | self.write_latency.fetch_add( 192 | start.elapsed().as_micros() as u64, 193 | std::sync::atomic::Ordering::Relaxed, 194 | ); 195 | } 196 | GenericDatabase::Redb(db) => { 197 | use redb::Durability::{Eventual, Immediate}; 198 | 199 | let start = Instant::now(); 200 | 201 | let mut write_txn = db.begin_write().unwrap(); 202 | 203 | write_txn.set_durability(if durable { Immediate } else { Eventual }); 204 | 205 | { 206 | let mut table = write_txn.open_table(TABLE).unwrap(); 207 | table.insert(key, value.to_vec()).unwrap(); 208 | } 209 | write_txn.commit().unwrap(); 210 | 211 | self.write_latency.fetch_add( 212 | start.elapsed().as_micros() as u64, 213 | std::sync::atomic::Ordering::Relaxed, 214 | ); 215 | } 216 | } 217 | 218 | self.write_ops 219 | .fetch_add(1, std::sync::atomic::Ordering::Relaxed); 220 | } 221 | 222 | pub fn get(&self, key: &[u8]) -> Option> { 223 | let start = Instant::now(); 224 | 225 | let item = match &self.inner { 226 | #[cfg(feature = "rocksdb")] 227 | GenericDatabase::RocksDb(db) => db.get(key).unwrap().map(|x| x.to_vec()), 228 | 229 | #[cfg(feature = "heed")] 230 | GenericDatabase::Heed { db, env } => { 231 | let rtxn = env.read_txn().unwrap(); 232 | let ret = db.get(&rtxn, key).unwrap(); 233 | ret.map(|x| x.to_vec()) 234 | } 235 | 236 | GenericDatabase::Nebari { roots: _, tree } => { 237 | let item = tree.get(key).unwrap(); 238 | item.map(|x| x.to_vec()) 239 | } 240 | GenericDatabase::Fjall { keyspace: _, db } => db.get(key).unwrap().map(|x| x.to_vec()), 241 | GenericDatabase::Sled(db) => db.get(key).unwrap().map(|x| x.to_vec()), 242 | // GenericDatabase::Bloodstone(db) => db.get(key).unwrap().map(|x| x.to_vec()), 243 | GenericDatabase::Jamm(db) => { 244 | let tx = db.tx(false).unwrap(); 245 | let bucket = tx.get_bucket("data").unwrap(); 246 | bucket.get(key).map(|item| item.kv().value().into()) 247 | } 248 | GenericDatabase::Persy(db) => { 249 | let key = String::from_utf8_lossy(key); 250 | 251 | let mut read_id = db 252 | .get::("primary", &key.to_string()) 253 | .unwrap(); 254 | if let Some(id) = read_id.next() { 255 | db.read("data", &id).unwrap() 256 | } else { 257 | None 258 | } 259 | } 260 | GenericDatabase::Redb(db) => { 261 | let read_txn = db.begin_read().unwrap(); 262 | let table = read_txn.open_table(TABLE).unwrap(); 263 | table.get(key).unwrap().map(|x| x.value()) 264 | } 265 | }; 266 | 267 | self.read_latency.fetch_add( 268 | start.elapsed().as_micros() as u64, 269 | std::sync::atomic::Ordering::Relaxed, 270 | ); 271 | 272 | self.read_ops 273 | .fetch_add(1, std::sync::atomic::Ordering::Relaxed); 274 | 275 | item 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /page/src/App.tsx: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | 3 | import { ApexChartProps, SolidApexCharts } from 'solid-apexcharts'; 4 | import { For, Show, createSignal, onMount } from 'solid-js' 5 | 6 | const thousandsFormatter = Intl.NumberFormat(undefined, { 7 | maximumFractionDigits: 0, 8 | }); 9 | const formatThousands = (n: number) => thousandsFormatter.format(n); 10 | 11 | export function readFile(file: File): Promise { 12 | const fileReader = new FileReader(); 13 | 14 | const promise = new Promise((resolve, reject) => { 15 | fileReader.onloadend = (ev) => { 16 | resolve(ev.target!.result!.toString()); 17 | }; 18 | fileReader.onerror = (error) => { 19 | reject(error); 20 | }; 21 | }); 22 | 23 | fileReader.readAsText(file); 24 | 25 | return promise; 26 | } 27 | 28 | export function parseJsonl(text: string): T[] { 29 | return text 30 | .split("\n") 31 | .filter(Boolean) 32 | .map((line) => JSON.parse(line)); 33 | } 34 | 35 | type HistoryEntry = MetricEntry; 36 | 37 | type MetricEntry = { 38 | cpu: number; 39 | mem_mib: number; 40 | time_micro: number; 41 | du_mib: number; 42 | disk_mib_w: number; 43 | disk_mib_r: number; 44 | space_amp: number; 45 | write_amp?: number; 46 | dataset_size?: number; 47 | write_ops: number 48 | read_ops: number 49 | delete_ops: number 50 | scan_ops: number; 51 | 52 | avg_write_latency: number; 53 | avg_read_latency: number; 54 | }; 55 | 56 | const chartOptions: ApexChartProps["options"]["chart"] = { 57 | background: "#030712", 58 | animations: { 59 | enabled: false, 60 | }, 61 | /* toolbar: { 62 | show: true, 63 | tools: { 64 | download: true, 65 | reset: true, 66 | pan: true, 67 | selection: true, 68 | zoomout: true, 69 | zoom: true, 70 | } 71 | }, 72 | zoom: { 73 | enabled: true 74 | }, */ 75 | } 76 | 77 | const xaxisOptions: ApexChartProps["options"]["xaxis"] = { 78 | axisBorder: { 79 | show: true, 80 | }, 81 | type: "numeric", 82 | labels: { 83 | style: { 84 | colors: "white" 85 | }, 86 | formatter: (value) => `${Math.floor(+value)}s` 87 | } 88 | } 89 | 90 | const colors = [ 91 | "#a78bfa", "#38bdf8", "#4ade80", "#fbbf24", 92 | "#f87171", "#f472b6", "#777777", "#fafafa", 93 | ]; 94 | 95 | const baseOptions: ApexChartProps["options"] = { 96 | grid: { 97 | show: false, 98 | }, 99 | tooltip: { 100 | enabled: false, 101 | }, 102 | dataLabels: { 103 | enabled: false 104 | }, 105 | legend: { 106 | position: "top", 107 | horizontalAlign: 'right', 108 | labels: { 109 | colors: "white" 110 | } 111 | } 112 | } 113 | 114 | function LineChart(props: { xaxis?: ApexChartProps["options"]["xaxis"]; yaxis?: ApexChartProps["options"]["yaxis"], title: string, yFormatter: (val: number) => string, series: { name: string, data: { x: number, y: number }[] }[] }) { 115 | const options = () => ({ 116 | ...baseOptions, 117 | title: { 118 | text: props.title, 119 | style: { 120 | color: "white" 121 | } 122 | }, 123 | stroke: { 124 | colors: ["#aaffff"], 125 | width: 2 126 | }, 127 | chart: { 128 | id: 'mem', 129 | ...chartOptions, 130 | }, 131 | xaxis: { 132 | ...xaxisOptions, 133 | ...props.xaxis, 134 | }, 135 | yaxis: { 136 | axisBorder: { 137 | show: true, 138 | }, 139 | labels: { 140 | style: { 141 | colors: "white" 142 | }, 143 | formatter: props.yFormatter, 144 | }, 145 | ...props.yaxis, 146 | }, 147 | } satisfies ApexChartProps["options"]); 148 | 149 | const series = () => ({ 150 | list: [ 151 | ...props.series.map(({ name, data }, idx) => { 152 | 153 | return { 154 | name, 155 | data: data.map(({ x, y }) => ({ 156 | x, 157 | y, 158 | })), 159 | color: colors[idx % colors.length] 160 | } satisfies ApexAxisChartSeries[0] 161 | }), 162 | ] satisfies ApexAxisChartSeries 163 | }); 164 | 165 | return 171 | } 172 | 173 | function WriteLatencyHistory(props: { series: HistoryEntry[][] }) { 174 | const series = () => props.series.map((series, idx) => { 175 | const metrics = series.slice(2); 176 | const start = metrics[0].time_micro; 177 | 178 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 179 | 180 | return { 181 | name: setupInfo.backend, 182 | data: metrics.map(({ time_micro, avg_write_latency }) => ({ 183 | x: (time_micro - start) / 1000 / 1000, 184 | y: avg_write_latency, 185 | })), 186 | color: colors[idx % colors.length] 187 | } satisfies ApexAxisChartSeries[0] 188 | }); 189 | 190 | return `${n.toFixed(1)}µs`} 192 | title="Average write latency (lower is better)" 193 | series={series()} 194 | />; 195 | } 196 | 197 | function ReadLatencyHistory(props: { series: HistoryEntry[][] }) { 198 | const series = () => props.series.map((series, idx) => { 199 | const metrics = series.slice(2); 200 | const start = metrics[0].time_micro; 201 | 202 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 203 | 204 | return { 205 | name: setupInfo.backend, 206 | data: metrics.map(({ time_micro, avg_read_latency }) => ({ 207 | x: (time_micro - start) / 1000 / 1000, 208 | y: avg_read_latency, 209 | })), 210 | color: colors[idx % colors.length] 211 | } satisfies ApexAxisChartSeries[0] 212 | }); 213 | 214 | return `${n.toFixed(1)}µs`} 216 | title="Average read latency (lower is better)" 217 | series={series()} 218 | />; 219 | } 220 | 221 | function WriteAmpHistory(props: { series: HistoryEntry[][] }) { 222 | const series = () => props.series.map((series, idx) => { 223 | const metrics = series.slice(2); 224 | const start = metrics[0].time_micro; 225 | 226 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 227 | 228 | return { 229 | name: setupInfo.backend, 230 | data: metrics.map(({ time_micro, du_mib, disk_mib_w, write_amp }) => ({ 231 | x: (time_micro - start) / 1000 / 1000, 232 | y: write_amp ?? 233 | // TODO: remove 234 | (disk_mib_w / du_mib), 235 | })), 236 | color: colors[idx % colors.length] 237 | } satisfies ApexAxisChartSeries[0] 238 | }); 239 | 240 | return `${(n).toFixed(1)}x`} 242 | title="Write amplification (lower is better)" 243 | series={series()} 244 | yaxis={{ 245 | min: 0, 246 | max: (n) => Math.min(1_000, n), 247 | }} 248 | />; 249 | } 250 | 251 | function DatasetSizeHistory(props: { series: HistoryEntry[][] }) { 252 | const series = () => props.series.map((series, idx) => { 253 | const metrics = series.slice(2); 254 | const start = metrics[0].time_micro; 255 | 256 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 257 | 258 | return { 259 | name: setupInfo.backend, 260 | data: metrics.map(({ time_micro, dataset_size }) => ({ 261 | x: (time_micro - start) / 1000 / 1000, 262 | y: (dataset_size ?? 0) / 1_024 / 1_024 263 | })), 264 | color: colors[idx % colors.length] 265 | } satisfies ApexAxisChartSeries[0] 266 | }); 267 | 268 | return `${formatThousands(n)} MiB`} 270 | title="True data set size (higher is better)" 271 | series={series()} 272 | />; 273 | } 274 | 275 | function DiskWritesCumulative(props: { series: HistoryEntry[][] }) { 276 | const series = () => props.series.map((series, idx) => { 277 | const metrics = series.slice(2); 278 | const start = metrics[0].time_micro; 279 | 280 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 281 | 282 | return { 283 | name: setupInfo.backend, 284 | data: metrics.map(({ time_micro, disk_mib_w }) => ({ 285 | x: (time_micro - start) / 1000 / 1000, 286 | y: disk_mib_w, 287 | })), 288 | color: colors[idx % colors.length] 289 | } satisfies ApexAxisChartSeries[0] 290 | }); 291 | 292 | return `${formatThousands(n)} MiB`} 294 | title="Written bytes cumulative" 295 | series={series()} 296 | />; 297 | } 298 | 299 | function WriteHistory(props: { series: HistoryEntry[][] }) { 300 | const series = () => props.series.map((series, idx) => { 301 | const metrics = series.slice(2); 302 | const start = metrics[0].time_micro; 303 | 304 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 305 | 306 | return { 307 | name: setupInfo.backend, 308 | data: metrics.map(({ time_micro, write_ops }) => ({ 309 | x: (time_micro - start) / 1000 / 1000, 310 | y: write_ops, 311 | })), 312 | color: colors[idx % colors.length] 313 | } satisfies ApexAxisChartSeries[0] 314 | }); 315 | 316 | return `${n} ops`} 318 | title="Write ops cumulative (higher is better)" 319 | series={series()} 320 | />; 321 | } 322 | 323 | function ReadHistory(props: { series: HistoryEntry[][] }) { 324 | const series = () => props.series.map((series, idx) => { 325 | const metrics = series.slice(2); 326 | const start = metrics[0].time_micro; 327 | 328 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 329 | 330 | return { 331 | name: setupInfo.backend, 332 | data: metrics.map(({ time_micro, read_ops }) => ({ 333 | x: (time_micro - start) / 1000 / 1000, 334 | y: read_ops, 335 | })), 336 | color: colors[idx % colors.length] 337 | } satisfies ApexAxisChartSeries[0] 338 | }); 339 | 340 | return `${n} ops`} 342 | title="Read ops cumulative (higher is better)" 343 | series={series()} 344 | />; 345 | } 346 | 347 | function SpaceAmpHistory(props: { series: HistoryEntry[][] }) { 348 | const series = () => props.series.map((series, idx) => { 349 | const metrics = series.slice(2); 350 | const start = metrics[0].time_micro; 351 | 352 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 353 | 354 | return { 355 | name: setupInfo.backend, 356 | data: metrics.map(({ time_micro, space_amp }) => ({ 357 | x: (time_micro - start) / 1000 / 1000, 358 | y: space_amp, 359 | })), 360 | color: colors[idx % colors.length] 361 | } satisfies ApexAxisChartSeries[0] 362 | }); 363 | 364 | return `${(n).toFixed(1)}x`} 366 | title="Space amplification (lower is better)" 367 | series={series()} 368 | yaxis={{ 369 | min: 1, 370 | max: (n) => Math.min(5, n), 371 | }} 372 | />; 373 | } 374 | 375 | function DiskSpaceUsageHistory(props: { series: HistoryEntry[][] }) { 376 | const series = () => props.series.map((series, idx) => { 377 | const metrics = series.slice(2); 378 | const start = metrics[0].time_micro; 379 | 380 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 381 | 382 | return { 383 | name: setupInfo.backend, 384 | data: metrics.map(({ time_micro, du_mib }) => ({ 385 | x: (time_micro - start) / 1000 / 1000, 386 | y: du_mib, 387 | })), 388 | color: colors[idx % colors.length] 389 | } satisfies ApexAxisChartSeries[0] 390 | }); 391 | 392 | return `${formatThousands(n)} MiB`} 394 | title="Disk space usage" 395 | series={series()} 396 | />; 397 | } 398 | 399 | function MemoryUsageHistory(props: { series: HistoryEntry[][] }) { 400 | const series = () => props.series.map((series, idx) => { 401 | const metrics = series.slice(2); 402 | const start = metrics[0].time_micro; 403 | 404 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 405 | 406 | return { 407 | name: setupInfo.backend, 408 | data: metrics.map(({ time_micro, mem_mib }) => ({ 409 | x: (time_micro - start) / 1000 / 1000, 410 | y: mem_mib, 411 | })), 412 | color: colors[idx % colors.length] 413 | } satisfies ApexAxisChartSeries[0] 414 | }); 415 | 416 | return `${formatThousands(n)} MiB`} 418 | title="Memory pressure (lower is better)" 419 | series={series()} 420 | />; 421 | } 422 | 423 | function CpuUsageHistory(props: { series: HistoryEntry[][] }) { 424 | const series = () => props.series.map((series, idx) => { 425 | const metrics = series.slice(2); 426 | const start = metrics[0].time_micro; 427 | 428 | const setupInfo = series[1] as unknown as { backend: string, workload: string }; 429 | 430 | return { 431 | name: setupInfo.backend, 432 | data: metrics.map(({ time_micro, cpu }) => ({ 433 | x: (time_micro - start) / 1000 / 1000, 434 | y: cpu, 435 | })), 436 | color: colors[idx % colors.length] 437 | } satisfies ApexAxisChartSeries[0] 438 | }); 439 | 440 | return `${n} %`} 442 | title="CPU usage (lower is better)" 443 | series={series()} 444 | />; 445 | } 446 | 447 | function PerformanceChart(props: { title: string, values: { backend: string, value: number }[] }) { 448 | const series = () => ({ 449 | list: [ 450 | { 451 | name: "ops", 452 | data: [ 453 | ...props.values.map(({ backend, value }, idx) => ({ 454 | x: backend, 455 | y: value, 456 | fillColor: colors[idx % colors.length], 457 | })) 458 | ].sort((a, b) => b.y - a.y), 459 | } 460 | ] satisfies ApexAxisChartSeries, 461 | }); 462 | 463 | const options = () => ({ 464 | ...baseOptions, 465 | title: { 466 | text: `${props.title} (higher is better)`, 467 | style: { 468 | color: "white" 469 | } 470 | }, 471 | stroke: { 472 | show: false, 473 | }, 474 | chart: { 475 | id: 'mem', 476 | ...chartOptions, 477 | }, 478 | xaxis: { 479 | categories: series().list[0].data.map(({ x }) => x), 480 | type: "category", 481 | labels: { 482 | style: { 483 | colors: "white", 484 | } 485 | } 486 | }, 487 | yaxis: { 488 | axisBorder: { 489 | show: true, 490 | }, 491 | labels: { 492 | style: { 493 | colors: "white" 494 | }, 495 | formatter: (value) => `${formatThousands(value)} ops` 496 | }, 497 | }, 498 | } satisfies ApexChartProps["options"]); 499 | 500 | 501 | 502 | return 508 | } 509 | 510 | type OpsObject = { backend: string, write_ops: number; read_ops: number; scan_ops: number; delete_ops: number }; 511 | 512 | function App() { 513 | const [items, setItems] = createSignal<(HistoryEntry)[][]>([]); 514 | const [ops, setOps] = createSignal([]); 515 | 516 | async function handleFileUpload(file: File) { 517 | 518 | await readFile(file) 519 | .then((text) => { 520 | const items = parseJsonl(text); 521 | setItems(x => [...x, items]); 522 | 523 | setOps(x => { 524 | const copy = structuredClone(x); 525 | let item = items.at(-1)!; 526 | return [...copy, item]; 527 | }); 528 | }) 529 | .catch((error) => { 530 | console.error(error); 531 | }); 532 | } 533 | 534 | onMount(() => { 535 | const handler = async (ev: DragEvent) => { 536 | ev.preventDefault(); 537 | 538 | setItems([]); 539 | 540 | const fileList = [...(ev.dataTransfer?.files ?? [])]; 541 | fileList.sort((a, b) => a.name.localeCompare(b.name)); 542 | 543 | for (const file of fileList) { 544 | await handleFileUpload(file); 545 | } 546 | }; 547 | 548 | document.addEventListener("drop", handler); 549 | document.addEventListener("dragover", (ev) => ev.preventDefault()); 550 | }); 551 | 552 | const sysInfo = () => items()[0][0] as unknown as { 553 | cpu: string; 554 | mem: number; 555 | os: string; 556 | kernel: string; 557 | }; 558 | 559 | const runtimeSecs = () => { 560 | if (!items().length) { 561 | return 0; 562 | } 563 | return (items().at(0)!.at(-1)!.time_micro - items().at(0)!.at(0)!.time_micro) / 1000 / 1000 564 | }; 565 | 566 | return ( 567 | <> 568 |

Rust Storage Engine Benchmark

569 | 0} fallback={"Drag a .jsonl file here!!"}> 570 |
571 |
572 |
573 | System: {sysInfo().os} - {sysInfo().cpu} - {(sysInfo().mem / 1024 / 1024 / 1024).toFixed(2)} GB 574 |
575 |
576 |
577 | 578 | {item => { 579 | const setupInfo = () => item[1] as unknown as { backend: string, workload: string }; 580 | 581 | return
582 |
583 | Backend: {setupInfo().backend} - Workload: {setupInfo().workload} - Runtime: {(runtimeSecs() / 60).toFixed(2)} min 584 |
585 |
586 | }} 587 |
588 |
589 |
590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 |
598 |
599 | 600 | 601 |
602 |
603 | 604 | 605 |
606 |
607 |
608 | 0}> 609 |
610 | ({ 614 | backend, 615 | value 616 | })) 617 | } 618 | /> 619 | ({ 623 | backend, 624 | value 625 | })) 626 | } 627 | /> 628 | ({ 632 | backend, 633 | value 634 | })) 635 | } 636 | /> 637 | ({ 641 | backend, 642 | value 643 | })) 644 | } 645 | /> 646 |
647 |
648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | { 660 | ops().slice().sort((a, b) => { 661 | if (b.read_ops > b.write_ops) { 662 | return b.read_ops - a.read_ops; 663 | } 664 | return b.write_ops - a.write_ops; 665 | }).map(({ backend, write_ops, read_ops, scan_ops, delete_ops }) => 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | ) 674 | } 675 | 676 |
BackendWritesReadsScansDeletes
{backend}{formatThousands(write_ops)}{formatThousands(read_ops)}{formatThousands(scan_ops)}{formatThousands(delete_ops)}
677 |
678 |
679 | 680 | ) 681 | } 682 | 683 | export default App 684 | -------------------------------------------------------------------------------- /src/worker/main.rs: -------------------------------------------------------------------------------- 1 | mod db; 2 | 3 | use crate::db::DatabaseWrapper; 4 | use clap::Parser; 5 | use db::GenericDatabase; 6 | use rand::distributions::Distribution; 7 | use rand::Rng; 8 | use rust_storage_bench::{Args, Backend, Workload}; 9 | use std::fs::{create_dir_all, remove_dir_all}; 10 | use std::io::Write; 11 | use std::path::Path; 12 | use std::sync::Arc; 13 | use std::time::Duration; 14 | use sysinfo::Pid; 15 | use zipf::ZipfDistribution; 16 | 17 | /// Gets the unix timestamp as a duration 18 | pub fn unix_timestamp() -> std::time::Duration { 19 | let now = std::time::SystemTime::now(); 20 | 21 | // NOTE: Unwrap is trivial 22 | #[allow(clippy::unwrap_used)] 23 | now.duration_since(std::time::SystemTime::UNIX_EPOCH) 24 | .unwrap() 25 | } 26 | 27 | fn start_killer(min: u64) { 28 | std::thread::spawn(move || { 29 | std::thread::sleep(Duration::from_secs(min * 60)); 30 | std::process::exit(0); 31 | }); 32 | } 33 | 34 | /* 35 | #[cfg(not(target_env = "msvc"))] 36 | use jemallocator::Jemalloc; 37 | 38 | #[cfg(not(target_env = "msvc"))] 39 | #[global_allocator] 40 | static GLOBAL: Jemalloc = Jemalloc; 41 | */ 42 | 43 | fn main() { 44 | env_logger::Builder::from_default_env().init(); 45 | 46 | let args = Arc::new(Args::parse()); 47 | 48 | eprintln!("Workload: {:?}", args.workload); 49 | eprintln!("Backend : {:?}", args.backend); 50 | eprintln!("Threads : {}", args.threads); 51 | eprintln!("# items : {}", args.items); 52 | 53 | if args.workload != Workload::TaskC { 54 | if args.fsync && (args.backend == Backend::Sled/*|| args.backend == Backend::Bloodstone*/) { 55 | panic!("Sled doesn't fsync..."); 56 | } 57 | } 58 | 59 | let data_dir = Path::new(".data").join(match args.backend { 60 | Backend::Fjall => match args.lsm_compaction { 61 | rust_storage_bench::LsmCompaction::Leveled => "fjall_lcs".to_owned(), 62 | rust_storage_bench::LsmCompaction::Tiered => "fjall_stcs".to_owned(), 63 | }, 64 | be => be.to_string(), 65 | }); 66 | 67 | if data_dir.exists() { 68 | remove_dir_all(&data_dir).unwrap(); 69 | } 70 | 71 | let db = match args.backend { 72 | #[cfg(feature = "rocksdb")] 73 | Backend::RocksDb => { 74 | create_dir_all(&data_dir).unwrap(); 75 | 76 | let mut opts = rocksdb::Options::default(); 77 | opts.set_manual_wal_flush(true); 78 | opts.create_if_missing(true); 79 | 80 | let db = rocksdb::DB::open(&opts, &data_dir).unwrap(); 81 | GenericDatabase::RocksDb(Arc::new(db)) 82 | } 83 | 84 | #[cfg(feature = "heed")] 85 | Backend::Heed => { 86 | create_dir_all(&data_dir).unwrap(); 87 | 88 | let env = unsafe { 89 | heed::EnvOpenOptions::new() 90 | .map_size(8_000_000_000) 91 | .open(&data_dir) 92 | .unwrap() 93 | }; 94 | 95 | let mut wtxn = env.write_txn().unwrap(); 96 | let db = env.create_database(&mut wtxn, None).unwrap(); 97 | wtxn.commit().unwrap(); 98 | 99 | GenericDatabase::Heed { db, env } 100 | } 101 | Backend::Fjall => { 102 | use fjall::{ 103 | compaction::{Levelled, SizeTiered, Strategy}, 104 | BlockCache, PartitionCreateOptions, 105 | }; 106 | 107 | let compaction_strategy: Arc = match args.lsm_compaction { 108 | rust_storage_bench::LsmCompaction::Leveled => Arc::new(Levelled::default()), 109 | rust_storage_bench::LsmCompaction::Tiered => Arc::new(SizeTiered::default()), 110 | }; 111 | 112 | let config = fjall::Config::new(&data_dir) 113 | .fsync_ms(if args.fsync { None } else { Some(1_000) }) 114 | .block_cache(BlockCache::with_capacity_bytes(args.cache_size.into()).into()); 115 | 116 | let create_opts = 117 | PartitionCreateOptions::default().block_size(args.lsm_block_size.into()); 118 | 119 | let keyspace = config.open().unwrap(); 120 | let db = keyspace.open_partition("data", create_opts).unwrap(); 121 | db.set_compaction_strategy(compaction_strategy); 122 | 123 | GenericDatabase::Fjall { keyspace, db } 124 | } 125 | Backend::Sled => GenericDatabase::Sled( 126 | sled::Config::new() 127 | .path(&data_dir) 128 | .flush_every_ms(if args.fsync { None } else { Some(1_000) }) 129 | .cache_capacity(args.cache_size as u64) 130 | .open() 131 | .unwrap(), 132 | ), 133 | // Backend::Bloodstone => GenericDatabase::Bloodstone( 134 | // bloodstone::Config::new() 135 | // .cache_capacity_bytes(args.cache_size as usize) 136 | // .path(&data_dir) 137 | // .open() 138 | // .unwrap(), 139 | // ), 140 | Backend::JammDb => { 141 | create_dir_all(&data_dir).unwrap(); 142 | 143 | let db = jammdb::DB::open(data_dir.join("data.db")).unwrap(); 144 | let tx = db.tx(true).unwrap(); 145 | let _ = tx.create_bucket("data").unwrap(); 146 | tx.commit().unwrap(); 147 | 148 | GenericDatabase::Jamm(db) 149 | } 150 | 151 | Backend::Persy => { 152 | use persy::{Config, Persy, PersyId, ValueMode}; 153 | 154 | create_dir_all(&data_dir).unwrap(); 155 | 156 | Persy::create(data_dir.join("data.persy")).unwrap(); 157 | 158 | let mut cfg = Config::default(); 159 | cfg.change_cache_size(args.cache_size.into()); 160 | let db = Persy::open(data_dir.join("data.persy"), cfg).unwrap(); 161 | 162 | let mut tx = db.begin().unwrap(); 163 | tx.create_segment("data").unwrap(); 164 | tx.create_index::("primary", ValueMode::Replace) 165 | .unwrap(); 166 | let prepared = tx.prepare().unwrap(); 167 | prepared.commit().unwrap(); 168 | 169 | GenericDatabase::Persy(db) 170 | } 171 | Backend::Redb => { 172 | create_dir_all(&data_dir).unwrap(); 173 | 174 | GenericDatabase::Redb(Arc::new( 175 | redb::Builder::new() 176 | .set_cache_size(args.cache_size as usize) 177 | .create(data_dir.join("my_db.redb")) 178 | .unwrap(), 179 | )) 180 | } 181 | Backend::Nebari => { 182 | use nebari::{ 183 | tree::{Root, Unversioned}, 184 | Config, 185 | }; 186 | 187 | create_dir_all(&data_dir).unwrap(); 188 | 189 | let roots = Config::default_for(data_dir.join("db.nebari")) 190 | .open() 191 | .unwrap(); 192 | let tree = roots.tree(Unversioned::tree("data")).unwrap(); 193 | 194 | GenericDatabase::Nebari { roots, tree } 195 | } 196 | }; 197 | 198 | let db = DatabaseWrapper { 199 | inner: db, 200 | write_ops: Default::default(), 201 | read_ops: Default::default(), 202 | delete_ops: Default::default(), 203 | scan_ops: Default::default(), 204 | read_latency: Default::default(), 205 | write_latency: Default::default(), 206 | }; 207 | 208 | { 209 | let db = db.clone(); 210 | let args = args.clone(); 211 | 212 | std::thread::spawn(move || { 213 | use std::sync::atomic::Ordering::Relaxed; 214 | 215 | let backend = match args.backend { 216 | Backend::Fjall => format!("{} {}", args.backend, args.lsm_compaction), 217 | _ => args.backend.to_string(), 218 | }; 219 | 220 | let mut sys = sysinfo::System::new_all(); 221 | sys.refresh_all(); 222 | 223 | let pid = std::process::id(); 224 | let pid = Pid::from(pid as usize); 225 | 226 | let mut file_writer = std::fs::File::create(&args.out).unwrap(); 227 | 228 | { 229 | let json = serde_json::json!({ 230 | "time_micro": unix_timestamp().as_micros(), 231 | "type": "system", 232 | "os": sysinfo::System::long_os_version(), 233 | "kernel": sysinfo::System::kernel_version(), 234 | "cpu": sys.global_cpu_info().brand(), 235 | "mem": sys.total_memory(), 236 | }); 237 | 238 | writeln!( 239 | &mut file_writer, 240 | "{}", 241 | serde_json::to_string(&json).unwrap() 242 | ) 243 | .unwrap(); 244 | } 245 | 246 | { 247 | let json = serde_json::json!({ 248 | "time_micro": unix_timestamp().as_micros(), 249 | "type": "setup", 250 | "backend": backend.to_string(), 251 | "workload": args.workload, 252 | "threads": args.threads, 253 | "items": args.items, 254 | "value_size": args.value_size, 255 | "cache_size_in_bytes": args.cache_size 256 | }); 257 | 258 | writeln!( 259 | &mut file_writer, 260 | "{}", 261 | serde_json::to_string(&json).unwrap() 262 | ) 263 | .unwrap(); 264 | } 265 | 266 | let mut prev_write_ops = 0; 267 | let mut prev_read_ops = 0; 268 | 269 | loop { 270 | if let Ok(du_bytes) = fs_extra::dir::get_size(&data_dir) { 271 | sys.refresh_all(); 272 | 273 | let cpu = sys.global_cpu_info().cpu_usage(); 274 | 275 | let proc = sys.processes(); 276 | let child = proc.get(&pid).unwrap(); 277 | 278 | let mem = child.memory() as f32; 279 | let disk = child.disk_usage(); 280 | 281 | let write_ops = db.write_ops.load(Relaxed); 282 | let read_ops = db.read_ops.load(Relaxed); 283 | 284 | let dataset_size_bytes = 285 | write_ops as f64 * (args.key_size as f64 + args.value_size as f64); 286 | 287 | let space_amp = du_bytes as f64 / dataset_size_bytes; 288 | 289 | let write_amp = disk.total_written_bytes as f64 / dataset_size_bytes; 290 | 291 | let accumulated_write_latency = db 292 | .write_latency 293 | .fetch_min(0, std::sync::atomic::Ordering::Release); 294 | 295 | let accumulated_read_latency = db 296 | .read_latency 297 | .fetch_min(0, std::sync::atomic::Ordering::Release); 298 | 299 | let write_ops_since = write_ops - prev_write_ops; 300 | let read_ops_since = read_ops - prev_read_ops; 301 | 302 | let avg_write_latency = accumulated_write_latency / write_ops_since.max(1); 303 | let avg_read_latency = accumulated_read_latency / read_ops_since.max(1); 304 | 305 | let json = serde_json::json!({ 306 | "backend": backend, 307 | "type": "metrics", 308 | "time_micro": unix_timestamp().as_micros(), 309 | "write_ops": write_ops, 310 | "read_ops": read_ops, 311 | "delete_ops": db.delete_ops, 312 | "scan_ops": db.scan_ops, 313 | "cpu": cpu, 314 | "mem_bytes": mem, 315 | "mem_mib": mem / 1024.0 / 1024.0, 316 | "disk_bytes_w": disk.total_written_bytes, 317 | "disk_bytes_r": disk.total_read_bytes, 318 | "disk_mib_w": (disk.total_written_bytes as f32) / 1024.0 / 1024.0, 319 | "disk_mib_r": (disk.total_read_bytes as f32) / 1024.0 / 1024.0, 320 | "du_bytes": du_bytes, 321 | "du_mib": (du_bytes as f32) / 1024.0 / 1024.0, 322 | "space_amp": space_amp, 323 | "write_amp": write_amp, 324 | "dataset_size": dataset_size_bytes, 325 | "avg_write_latency": avg_write_latency, 326 | "avg_read_latency": avg_read_latency, 327 | }); 328 | 329 | prev_write_ops = write_ops; 330 | prev_read_ops = read_ops; 331 | 332 | writeln!( 333 | &mut file_writer, 334 | "{}", 335 | serde_json::to_string(&json).unwrap() 336 | ) 337 | .unwrap(); 338 | } 339 | 340 | // As minutes increase, decrease granularity 341 | // to keep log files low(ish) 342 | let sec = args.minutes as f32 / 2.0; 343 | let duration = Duration::from_secs_f32(sec); 344 | std::thread::sleep(duration); 345 | } 346 | }); 347 | } 348 | 349 | match args.workload { 350 | Workload::TaskA => { 351 | let users = args.threads; 352 | 353 | { 354 | let mut rng = rand::thread_rng(); 355 | 356 | for idx in 0..users { 357 | let user_id = format!("user{idx:0>2}"); 358 | 359 | for x in 0..args.items { 360 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 361 | for _ in 0..args.value_size { 362 | val.push(rng.gen::()); 363 | } 364 | 365 | let key = format!("{user_id}:{x:0>10}"); 366 | let key = key.as_bytes(); 367 | 368 | db.insert(key, &val, false, args.clone()); 369 | } 370 | } 371 | } 372 | 373 | let threads = (0..users) 374 | .map(|idx| { 375 | let args = args.clone(); 376 | let db = db.clone(); 377 | let user_id = format!("user{idx:0>2}"); 378 | 379 | std::thread::spawn(move || { 380 | let mut rng = rand::thread_rng(); 381 | 382 | let zipf = ZipfDistribution::new((args.items - 1) as usize, 0.99).unwrap(); 383 | 384 | loop { 385 | let x = zipf.sample(&mut rng); 386 | let key = format!("{user_id}:{x:0>10}"); 387 | let key = key.as_bytes(); 388 | 389 | let choice: f32 = rng.gen_range(0.0..1.0); 390 | 391 | if choice > 0.5 { 392 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 393 | for _ in 0..args.value_size { 394 | val.push(rng.gen::()); 395 | } 396 | 397 | db.insert(key, &val, args.fsync, args.clone()); 398 | } else { 399 | db.get(key).unwrap(); 400 | } 401 | } 402 | }) 403 | }) 404 | .collect::>(); 405 | 406 | start_killer(args.minutes.into()); 407 | 408 | for t in threads { 409 | t.join().unwrap(); 410 | } 411 | } 412 | 413 | Workload::TaskB => { 414 | let users = args.threads; 415 | 416 | { 417 | let mut rng = rand::thread_rng(); 418 | 419 | for idx in 0..users { 420 | let user_id = format!("user{idx:0>2}"); 421 | 422 | for x in 0..args.items { 423 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 424 | for _ in 0..args.value_size { 425 | val.push(rng.gen::()); 426 | } 427 | 428 | let key = format!("{user_id}:{x:0>10}"); 429 | let key = key.as_bytes(); 430 | 431 | db.insert(key, &val, false, args.clone()); 432 | } 433 | } 434 | } 435 | 436 | let threads = (0..users) 437 | .map(|idx| { 438 | let args = args.clone(); 439 | let db = db.clone(); 440 | let user_id = format!("user{idx:0>2}"); 441 | 442 | std::thread::spawn(move || { 443 | let mut rng = rand::thread_rng(); 444 | 445 | let zipf = ZipfDistribution::new((args.items - 1) as usize, 0.99).unwrap(); 446 | 447 | loop { 448 | let x = zipf.sample(&mut rng); 449 | let key = format!("{user_id}:{x:0>10}"); 450 | let key = key.as_bytes(); 451 | 452 | let choice: f32 = rng.gen_range(0.0..1.0); 453 | 454 | if choice > 0.95 { 455 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 456 | for _ in 0..args.value_size { 457 | val.push(rng.gen::()); 458 | } 459 | 460 | db.insert(key, &val, args.fsync, args.clone()); 461 | } else { 462 | db.get(key).unwrap(); 463 | } 464 | } 465 | }) 466 | }) 467 | .collect::>(); 468 | 469 | start_killer(args.minutes.into()); 470 | 471 | for t in threads { 472 | t.join().unwrap(); 473 | } 474 | } 475 | 476 | Workload::TaskC => { 477 | let mut rng = rand::thread_rng(); 478 | 479 | for x in 0..args.items { 480 | let key = (x as u64).to_be_bytes(); 481 | 482 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 483 | for _ in 0..args.value_size { 484 | val.push(rng.gen::()); 485 | } 486 | 487 | db.insert(&key, &val, false, args.clone()); 488 | } 489 | 490 | start_killer(args.minutes.into()); 491 | 492 | let zipf = ZipfDistribution::new((args.items - 1) as usize, 0.99).unwrap(); 493 | 494 | loop { 495 | let x = zipf.sample(&mut rng); 496 | let key = (x as u64).to_be_bytes(); 497 | 498 | db.get(&key).unwrap(); 499 | } 500 | } 501 | 502 | Workload::TaskD => { 503 | let users = args.threads; 504 | 505 | { 506 | let mut rng = rand::thread_rng(); 507 | 508 | for idx in 0..users { 509 | let user_id = format!("user{idx:0>2}"); 510 | 511 | for x in 0..args.items { 512 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 513 | for _ in 0..args.value_size { 514 | val.push(rng.gen::()); 515 | } 516 | 517 | let key = format!("{user_id}:{x:0>10}"); 518 | let key = key.as_bytes(); 519 | 520 | db.insert(key, &val, false, args.clone()); 521 | } 522 | } 523 | } 524 | 525 | let threads = (0..users) 526 | .map(|idx| { 527 | let args = args.clone(); 528 | let db = db.clone(); 529 | let user_id = format!("user{idx:0>2}"); 530 | 531 | std::thread::spawn(move || { 532 | let mut rng = rand::thread_rng(); 533 | let mut records = args.items; 534 | 535 | loop { 536 | let choice: f32 = rng.gen_range(0.0..1.0); 537 | 538 | if choice > 0.95 { 539 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 540 | for _ in 0..args.value_size { 541 | val.push(rng.gen::()); 542 | } 543 | 544 | let key = format!("{user_id}:{records:0>10}"); 545 | let key = key.as_bytes(); 546 | 547 | db.insert(key, &val, args.fsync, args.clone()); 548 | records += 1; 549 | } else { 550 | let key = format!("{user_id}:{:0>10}", records - 1); 551 | let key = key.as_bytes(); 552 | 553 | db.get(key).unwrap(); 554 | } 555 | } 556 | }) 557 | }) 558 | .collect::>(); 559 | 560 | start_killer(args.minutes.into()); 561 | 562 | for t in threads { 563 | t.join().unwrap(); 564 | } 565 | } 566 | 567 | Workload::TaskE => { 568 | let users = args.threads; 569 | 570 | { 571 | let mut rng = rand::thread_rng(); 572 | 573 | for idx in 0..users { 574 | let user_id = format!("user{idx:0>2}"); 575 | 576 | for x in 0..args.items { 577 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 578 | for _ in 0..args.value_size { 579 | val.push(rng.gen::()); 580 | } 581 | 582 | let key = format!("{user_id}:{x:0>10}"); 583 | let key = key.as_bytes(); 584 | 585 | db.insert(key, &val, false, args.clone()); 586 | } 587 | } 588 | } 589 | 590 | let threads = (0..users) 591 | .map(|idx| { 592 | let args = args.clone(); 593 | let db = db.clone(); 594 | let user_id = format!("user{idx:0>2}"); 595 | 596 | std::thread::spawn(move || { 597 | let mut rng = rand::thread_rng(); 598 | let mut records = args.items; 599 | 600 | loop { 601 | let choice: f32 = rng.gen_range(0.0..1.0); 602 | 603 | if choice < 0.95 { 604 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 605 | for _ in 0..args.value_size { 606 | val.push(rng.gen::()); 607 | } 608 | 609 | let key = format!("{user_id}:{records:0>10}"); 610 | let key = key.as_bytes(); 611 | 612 | db.insert(key, &val, args.fsync, args.clone()); 613 | records += 1; 614 | } else { 615 | let key = format!("{user_id}:{:0>10}", records - 1); 616 | let key = key.as_bytes(); 617 | 618 | db.get(key).unwrap(); 619 | } 620 | } 621 | }) 622 | }) 623 | .collect::>(); 624 | 625 | start_killer(args.minutes.into()); 626 | 627 | for t in threads { 628 | t.join().unwrap(); 629 | } 630 | } 631 | 632 | Workload::TaskF => { 633 | let users = args.threads; 634 | 635 | { 636 | let mut rng = rand::thread_rng(); 637 | 638 | for idx in 0..users { 639 | let user_id = format!("user{idx:0>2}"); 640 | 641 | for x in 0..args.items { 642 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 643 | for _ in 0..args.value_size { 644 | val.push(rng.gen::()); 645 | } 646 | 647 | let key = format!("{user_id:0>2}:{x:0>10}"); 648 | let key = key.as_bytes(); 649 | 650 | db.insert(key, &val, false, args.clone()); 651 | } 652 | } 653 | } 654 | 655 | let threads = (0..users) 656 | .map(|idx| { 657 | let args = args.clone(); 658 | let db = db.clone(); 659 | let user_id = format!("user{idx:0>2}"); 660 | 661 | std::thread::spawn(move || { 662 | let mut rng = rand::thread_rng(); 663 | let mut records = args.items; 664 | 665 | loop { 666 | let choice: f32 = rng.gen_range(0.0..1.0); 667 | 668 | if choice > 0.95 { 669 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 670 | for _ in 0..args.value_size { 671 | val.push(rng.gen::()); 672 | } 673 | 674 | let key = format!("{user_id}:{records:0>10}"); 675 | let key = key.as_bytes(); 676 | 677 | db.insert(key, &val, args.fsync, args.clone()); 678 | records += 1; 679 | } else { 680 | let zipf = 681 | ZipfDistribution::new((records - 1) as usize, 0.99).unwrap(); 682 | let x = zipf.sample(&mut rng); 683 | 684 | let key = format!("{user_id}:{x:0>10}"); 685 | let key = key.as_bytes(); 686 | 687 | db.get(key).unwrap(); 688 | } 689 | } 690 | }) 691 | }) 692 | .collect::>(); 693 | 694 | start_killer(args.minutes.into()); 695 | 696 | for t in threads { 697 | t.join().unwrap(); 698 | } 699 | } 700 | 701 | Workload::TaskG => { 702 | let users = args.threads; 703 | 704 | { 705 | let mut rng = rand::thread_rng(); 706 | 707 | for idx in 0..users { 708 | let user_id = format!("user{idx:0>2}"); 709 | 710 | for x in 0..args.items { 711 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 712 | for _ in 0..args.value_size { 713 | val.push(rng.gen::()); 714 | } 715 | 716 | let key = format!("{user_id}:{x:0>10}"); 717 | let key = key.as_bytes(); 718 | 719 | db.insert(key, &val, false, args.clone()); 720 | } 721 | } 722 | } 723 | 724 | let threads = (0..users) 725 | .map(|idx| { 726 | let args = args.clone(); 727 | let db = db.clone(); 728 | let user_id = format!("user{idx:0>2}"); 729 | 730 | std::thread::spawn(move || { 731 | let mut rng = rand::thread_rng(); 732 | let mut records = args.items; 733 | 734 | loop { 735 | let choice: f32 = rng.gen_range(0.0..1.0); 736 | 737 | if choice < 0.95 { 738 | let mut val: Vec = Vec::with_capacity(args.value_size as usize); 739 | for _ in 0..args.value_size { 740 | val.push(rng.gen::()); 741 | } 742 | 743 | let key = format!("{user_id}:{records:0>10}"); 744 | let key = key.as_bytes(); 745 | 746 | db.insert(key, &val, args.fsync, args.clone()); 747 | records += 1; 748 | } else { 749 | let zipf = 750 | ZipfDistribution::new((records - 1) as usize, 0.99).unwrap(); 751 | let x = zipf.sample(&mut rng); 752 | 753 | let key = format!("{user_id}:{x:0>10}"); 754 | let key = key.as_bytes(); 755 | 756 | db.get(key).unwrap(); 757 | } 758 | } 759 | }) 760 | }) 761 | .collect::>(); 762 | 763 | start_killer(args.minutes.into()); 764 | 765 | for t in threads { 766 | t.join().unwrap(); 767 | } 768 | } 769 | } 770 | } 771 | -------------------------------------------------------------------------------- /page/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | solid-apexcharts: 9 | specifier: 0.3.2 10 | version: 0.3.2(apexcharts@3.44.0)(solid-js@1.8.5) 11 | solid-js: 12 | specifier: 1.8.5 13 | version: 1.8.5 14 | 15 | devDependencies: 16 | typescript: 17 | specifier: 5.2.2 18 | version: 5.2.2 19 | unocss: 20 | specifier: 0.58.0 21 | version: 0.58.0(postcss@8.4.31)(vite@4.5.0) 22 | vite: 23 | specifier: '4' 24 | version: 4.5.0 25 | vite-plugin-solid: 26 | specifier: 2.7.2 27 | version: 2.7.2(solid-js@1.8.5)(vite@4.5.0) 28 | 29 | packages: 30 | 31 | /@ampproject/remapping@2.2.1: 32 | resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} 33 | engines: {node: '>=6.0.0'} 34 | dependencies: 35 | '@jridgewell/gen-mapping': 0.3.3 36 | '@jridgewell/trace-mapping': 0.3.20 37 | dev: true 38 | 39 | /@antfu/install-pkg@0.1.1: 40 | resolution: {integrity: sha512-LyB/8+bSfa0DFGC06zpCEfs89/XoWZwws5ygEa5D+Xsm3OfI+aXQ86VgVG7Acyef+rSZ5HE7J8rrxzrQeM3PjQ==} 41 | dependencies: 42 | execa: 5.1.1 43 | find-up: 5.0.0 44 | dev: true 45 | 46 | /@antfu/utils@0.7.6: 47 | resolution: {integrity: sha512-pvFiLP2BeOKA/ZOS6jxx4XhKzdVLHDhGlFEaZ2flWWYf2xOqVniqpk38I04DFRyz+L0ASggl7SkItTc+ZLju4w==} 48 | dev: true 49 | 50 | /@babel/code-frame@7.23.5: 51 | resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} 52 | engines: {node: '>=6.9.0'} 53 | dependencies: 54 | '@babel/highlight': 7.23.4 55 | chalk: 2.4.2 56 | dev: true 57 | 58 | /@babel/compat-data@7.23.5: 59 | resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} 60 | engines: {node: '>=6.9.0'} 61 | dev: true 62 | 63 | /@babel/core@7.23.5: 64 | resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==} 65 | engines: {node: '>=6.9.0'} 66 | dependencies: 67 | '@ampproject/remapping': 2.2.1 68 | '@babel/code-frame': 7.23.5 69 | '@babel/generator': 7.23.5 70 | '@babel/helper-compilation-targets': 7.22.15 71 | '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5) 72 | '@babel/helpers': 7.23.5 73 | '@babel/parser': 7.23.5 74 | '@babel/template': 7.22.15 75 | '@babel/traverse': 7.23.5 76 | '@babel/types': 7.23.5 77 | convert-source-map: 2.0.0 78 | debug: 4.3.4 79 | gensync: 1.0.0-beta.2 80 | json5: 2.2.3 81 | semver: 6.3.1 82 | transitivePeerDependencies: 83 | - supports-color 84 | dev: true 85 | 86 | /@babel/generator@7.23.5: 87 | resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==} 88 | engines: {node: '>=6.9.0'} 89 | dependencies: 90 | '@babel/types': 7.23.5 91 | '@jridgewell/gen-mapping': 0.3.3 92 | '@jridgewell/trace-mapping': 0.3.20 93 | jsesc: 2.5.2 94 | dev: true 95 | 96 | /@babel/helper-annotate-as-pure@7.22.5: 97 | resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} 98 | engines: {node: '>=6.9.0'} 99 | dependencies: 100 | '@babel/types': 7.23.5 101 | dev: true 102 | 103 | /@babel/helper-compilation-targets@7.22.15: 104 | resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==} 105 | engines: {node: '>=6.9.0'} 106 | dependencies: 107 | '@babel/compat-data': 7.23.5 108 | '@babel/helper-validator-option': 7.23.5 109 | browserslist: 4.22.1 110 | lru-cache: 5.1.1 111 | semver: 6.3.1 112 | dev: true 113 | 114 | /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.23.5): 115 | resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==} 116 | engines: {node: '>=6.9.0'} 117 | peerDependencies: 118 | '@babel/core': ^7.0.0 119 | dependencies: 120 | '@babel/core': 7.23.5 121 | '@babel/helper-annotate-as-pure': 7.22.5 122 | '@babel/helper-environment-visitor': 7.22.20 123 | '@babel/helper-function-name': 7.23.0 124 | '@babel/helper-member-expression-to-functions': 7.23.0 125 | '@babel/helper-optimise-call-expression': 7.22.5 126 | '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5) 127 | '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 128 | '@babel/helper-split-export-declaration': 7.22.6 129 | semver: 6.3.1 130 | dev: true 131 | 132 | /@babel/helper-environment-visitor@7.22.20: 133 | resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} 134 | engines: {node: '>=6.9.0'} 135 | dev: true 136 | 137 | /@babel/helper-function-name@7.23.0: 138 | resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} 139 | engines: {node: '>=6.9.0'} 140 | dependencies: 141 | '@babel/template': 7.22.15 142 | '@babel/types': 7.23.5 143 | dev: true 144 | 145 | /@babel/helper-hoist-variables@7.22.5: 146 | resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} 147 | engines: {node: '>=6.9.0'} 148 | dependencies: 149 | '@babel/types': 7.23.5 150 | dev: true 151 | 152 | /@babel/helper-member-expression-to-functions@7.23.0: 153 | resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} 154 | engines: {node: '>=6.9.0'} 155 | dependencies: 156 | '@babel/types': 7.23.5 157 | dev: true 158 | 159 | /@babel/helper-module-imports@7.18.6: 160 | resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} 161 | engines: {node: '>=6.9.0'} 162 | dependencies: 163 | '@babel/types': 7.23.5 164 | dev: true 165 | 166 | /@babel/helper-module-imports@7.22.15: 167 | resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} 168 | engines: {node: '>=6.9.0'} 169 | dependencies: 170 | '@babel/types': 7.23.5 171 | dev: true 172 | 173 | /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5): 174 | resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} 175 | engines: {node: '>=6.9.0'} 176 | peerDependencies: 177 | '@babel/core': ^7.0.0 178 | dependencies: 179 | '@babel/core': 7.23.5 180 | '@babel/helper-environment-visitor': 7.22.20 181 | '@babel/helper-module-imports': 7.22.15 182 | '@babel/helper-simple-access': 7.22.5 183 | '@babel/helper-split-export-declaration': 7.22.6 184 | '@babel/helper-validator-identifier': 7.22.20 185 | dev: true 186 | 187 | /@babel/helper-optimise-call-expression@7.22.5: 188 | resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} 189 | engines: {node: '>=6.9.0'} 190 | dependencies: 191 | '@babel/types': 7.23.5 192 | dev: true 193 | 194 | /@babel/helper-plugin-utils@7.22.5: 195 | resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} 196 | engines: {node: '>=6.9.0'} 197 | dev: true 198 | 199 | /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.5): 200 | resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} 201 | engines: {node: '>=6.9.0'} 202 | peerDependencies: 203 | '@babel/core': ^7.0.0 204 | dependencies: 205 | '@babel/core': 7.23.5 206 | '@babel/helper-environment-visitor': 7.22.20 207 | '@babel/helper-member-expression-to-functions': 7.23.0 208 | '@babel/helper-optimise-call-expression': 7.22.5 209 | dev: true 210 | 211 | /@babel/helper-simple-access@7.22.5: 212 | resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} 213 | engines: {node: '>=6.9.0'} 214 | dependencies: 215 | '@babel/types': 7.23.5 216 | dev: true 217 | 218 | /@babel/helper-skip-transparent-expression-wrappers@7.22.5: 219 | resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} 220 | engines: {node: '>=6.9.0'} 221 | dependencies: 222 | '@babel/types': 7.23.5 223 | dev: true 224 | 225 | /@babel/helper-split-export-declaration@7.22.6: 226 | resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} 227 | engines: {node: '>=6.9.0'} 228 | dependencies: 229 | '@babel/types': 7.23.5 230 | dev: true 231 | 232 | /@babel/helper-string-parser@7.23.4: 233 | resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} 234 | engines: {node: '>=6.9.0'} 235 | dev: true 236 | 237 | /@babel/helper-validator-identifier@7.22.20: 238 | resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} 239 | engines: {node: '>=6.9.0'} 240 | dev: true 241 | 242 | /@babel/helper-validator-option@7.23.5: 243 | resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} 244 | engines: {node: '>=6.9.0'} 245 | dev: true 246 | 247 | /@babel/helpers@7.23.5: 248 | resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==} 249 | engines: {node: '>=6.9.0'} 250 | dependencies: 251 | '@babel/template': 7.22.15 252 | '@babel/traverse': 7.23.5 253 | '@babel/types': 7.23.5 254 | transitivePeerDependencies: 255 | - supports-color 256 | dev: true 257 | 258 | /@babel/highlight@7.23.4: 259 | resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} 260 | engines: {node: '>=6.9.0'} 261 | dependencies: 262 | '@babel/helper-validator-identifier': 7.22.20 263 | chalk: 2.4.2 264 | js-tokens: 4.0.0 265 | dev: true 266 | 267 | /@babel/parser@7.23.5: 268 | resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==} 269 | engines: {node: '>=6.0.0'} 270 | hasBin: true 271 | dependencies: 272 | '@babel/types': 7.23.5 273 | dev: true 274 | 275 | /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5): 276 | resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} 277 | engines: {node: '>=6.9.0'} 278 | peerDependencies: 279 | '@babel/core': ^7.0.0-0 280 | dependencies: 281 | '@babel/core': 7.23.5 282 | '@babel/helper-plugin-utils': 7.22.5 283 | dev: true 284 | 285 | /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5): 286 | resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} 287 | engines: {node: '>=6.9.0'} 288 | peerDependencies: 289 | '@babel/core': ^7.0.0-0 290 | dependencies: 291 | '@babel/core': 7.23.5 292 | '@babel/helper-plugin-utils': 7.22.5 293 | dev: true 294 | 295 | /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.5): 296 | resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==} 297 | engines: {node: '>=6.9.0'} 298 | peerDependencies: 299 | '@babel/core': ^7.0.0-0 300 | dependencies: 301 | '@babel/core': 7.23.5 302 | '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5) 303 | '@babel/helper-plugin-utils': 7.22.5 304 | '@babel/helper-simple-access': 7.22.5 305 | dev: true 306 | 307 | /@babel/plugin-transform-typescript@7.23.5(@babel/core@7.23.5): 308 | resolution: {integrity: sha512-2fMkXEJkrmwgu2Bsv1Saxgj30IXZdJ+84lQcKKI7sm719oXs0BBw2ZENKdJdR1PjWndgLCEBNXJOri0fk7RYQA==} 309 | engines: {node: '>=6.9.0'} 310 | peerDependencies: 311 | '@babel/core': ^7.0.0-0 312 | dependencies: 313 | '@babel/core': 7.23.5 314 | '@babel/helper-annotate-as-pure': 7.22.5 315 | '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5) 316 | '@babel/helper-plugin-utils': 7.22.5 317 | '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.5) 318 | dev: true 319 | 320 | /@babel/preset-typescript@7.23.3(@babel/core@7.23.5): 321 | resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==} 322 | engines: {node: '>=6.9.0'} 323 | peerDependencies: 324 | '@babel/core': ^7.0.0-0 325 | dependencies: 326 | '@babel/core': 7.23.5 327 | '@babel/helper-plugin-utils': 7.22.5 328 | '@babel/helper-validator-option': 7.23.5 329 | '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5) 330 | '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5) 331 | '@babel/plugin-transform-typescript': 7.23.5(@babel/core@7.23.5) 332 | dev: true 333 | 334 | /@babel/template@7.22.15: 335 | resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} 336 | engines: {node: '>=6.9.0'} 337 | dependencies: 338 | '@babel/code-frame': 7.23.5 339 | '@babel/parser': 7.23.5 340 | '@babel/types': 7.23.5 341 | dev: true 342 | 343 | /@babel/traverse@7.23.5: 344 | resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==} 345 | engines: {node: '>=6.9.0'} 346 | dependencies: 347 | '@babel/code-frame': 7.23.5 348 | '@babel/generator': 7.23.5 349 | '@babel/helper-environment-visitor': 7.22.20 350 | '@babel/helper-function-name': 7.23.0 351 | '@babel/helper-hoist-variables': 7.22.5 352 | '@babel/helper-split-export-declaration': 7.22.6 353 | '@babel/parser': 7.23.5 354 | '@babel/types': 7.23.5 355 | debug: 4.3.4 356 | globals: 11.12.0 357 | transitivePeerDependencies: 358 | - supports-color 359 | dev: true 360 | 361 | /@babel/types@7.23.5: 362 | resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==} 363 | engines: {node: '>=6.9.0'} 364 | dependencies: 365 | '@babel/helper-string-parser': 7.23.4 366 | '@babel/helper-validator-identifier': 7.22.20 367 | to-fast-properties: 2.0.0 368 | dev: true 369 | 370 | /@esbuild/android-arm64@0.18.20: 371 | resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} 372 | engines: {node: '>=12'} 373 | cpu: [arm64] 374 | os: [android] 375 | requiresBuild: true 376 | dev: true 377 | optional: true 378 | 379 | /@esbuild/android-arm@0.18.20: 380 | resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} 381 | engines: {node: '>=12'} 382 | cpu: [arm] 383 | os: [android] 384 | requiresBuild: true 385 | dev: true 386 | optional: true 387 | 388 | /@esbuild/android-x64@0.18.20: 389 | resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} 390 | engines: {node: '>=12'} 391 | cpu: [x64] 392 | os: [android] 393 | requiresBuild: true 394 | dev: true 395 | optional: true 396 | 397 | /@esbuild/darwin-arm64@0.18.20: 398 | resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} 399 | engines: {node: '>=12'} 400 | cpu: [arm64] 401 | os: [darwin] 402 | requiresBuild: true 403 | dev: true 404 | optional: true 405 | 406 | /@esbuild/darwin-x64@0.18.20: 407 | resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} 408 | engines: {node: '>=12'} 409 | cpu: [x64] 410 | os: [darwin] 411 | requiresBuild: true 412 | dev: true 413 | optional: true 414 | 415 | /@esbuild/freebsd-arm64@0.18.20: 416 | resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} 417 | engines: {node: '>=12'} 418 | cpu: [arm64] 419 | os: [freebsd] 420 | requiresBuild: true 421 | dev: true 422 | optional: true 423 | 424 | /@esbuild/freebsd-x64@0.18.20: 425 | resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} 426 | engines: {node: '>=12'} 427 | cpu: [x64] 428 | os: [freebsd] 429 | requiresBuild: true 430 | dev: true 431 | optional: true 432 | 433 | /@esbuild/linux-arm64@0.18.20: 434 | resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} 435 | engines: {node: '>=12'} 436 | cpu: [arm64] 437 | os: [linux] 438 | requiresBuild: true 439 | dev: true 440 | optional: true 441 | 442 | /@esbuild/linux-arm@0.18.20: 443 | resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} 444 | engines: {node: '>=12'} 445 | cpu: [arm] 446 | os: [linux] 447 | requiresBuild: true 448 | dev: true 449 | optional: true 450 | 451 | /@esbuild/linux-ia32@0.18.20: 452 | resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} 453 | engines: {node: '>=12'} 454 | cpu: [ia32] 455 | os: [linux] 456 | requiresBuild: true 457 | dev: true 458 | optional: true 459 | 460 | /@esbuild/linux-loong64@0.18.20: 461 | resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} 462 | engines: {node: '>=12'} 463 | cpu: [loong64] 464 | os: [linux] 465 | requiresBuild: true 466 | dev: true 467 | optional: true 468 | 469 | /@esbuild/linux-mips64el@0.18.20: 470 | resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} 471 | engines: {node: '>=12'} 472 | cpu: [mips64el] 473 | os: [linux] 474 | requiresBuild: true 475 | dev: true 476 | optional: true 477 | 478 | /@esbuild/linux-ppc64@0.18.20: 479 | resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} 480 | engines: {node: '>=12'} 481 | cpu: [ppc64] 482 | os: [linux] 483 | requiresBuild: true 484 | dev: true 485 | optional: true 486 | 487 | /@esbuild/linux-riscv64@0.18.20: 488 | resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} 489 | engines: {node: '>=12'} 490 | cpu: [riscv64] 491 | os: [linux] 492 | requiresBuild: true 493 | dev: true 494 | optional: true 495 | 496 | /@esbuild/linux-s390x@0.18.20: 497 | resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} 498 | engines: {node: '>=12'} 499 | cpu: [s390x] 500 | os: [linux] 501 | requiresBuild: true 502 | dev: true 503 | optional: true 504 | 505 | /@esbuild/linux-x64@0.18.20: 506 | resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} 507 | engines: {node: '>=12'} 508 | cpu: [x64] 509 | os: [linux] 510 | requiresBuild: true 511 | dev: true 512 | optional: true 513 | 514 | /@esbuild/netbsd-x64@0.18.20: 515 | resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} 516 | engines: {node: '>=12'} 517 | cpu: [x64] 518 | os: [netbsd] 519 | requiresBuild: true 520 | dev: true 521 | optional: true 522 | 523 | /@esbuild/openbsd-x64@0.18.20: 524 | resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} 525 | engines: {node: '>=12'} 526 | cpu: [x64] 527 | os: [openbsd] 528 | requiresBuild: true 529 | dev: true 530 | optional: true 531 | 532 | /@esbuild/sunos-x64@0.18.20: 533 | resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} 534 | engines: {node: '>=12'} 535 | cpu: [x64] 536 | os: [sunos] 537 | requiresBuild: true 538 | dev: true 539 | optional: true 540 | 541 | /@esbuild/win32-arm64@0.18.20: 542 | resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} 543 | engines: {node: '>=12'} 544 | cpu: [arm64] 545 | os: [win32] 546 | requiresBuild: true 547 | dev: true 548 | optional: true 549 | 550 | /@esbuild/win32-ia32@0.18.20: 551 | resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} 552 | engines: {node: '>=12'} 553 | cpu: [ia32] 554 | os: [win32] 555 | requiresBuild: true 556 | dev: true 557 | optional: true 558 | 559 | /@esbuild/win32-x64@0.18.20: 560 | resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} 561 | engines: {node: '>=12'} 562 | cpu: [x64] 563 | os: [win32] 564 | requiresBuild: true 565 | dev: true 566 | optional: true 567 | 568 | /@iconify/types@2.0.0: 569 | resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} 570 | dev: true 571 | 572 | /@iconify/utils@2.1.12: 573 | resolution: {integrity: sha512-7vf3Uk6H7TKX4QMs2gbg5KR1X9J0NJzKSRNWhMZ+PWN92l0t6Q3tj2ZxLDG07rC3ppWBtTtA4FPmkQphuEmdsg==} 574 | dependencies: 575 | '@antfu/install-pkg': 0.1.1 576 | '@antfu/utils': 0.7.6 577 | '@iconify/types': 2.0.0 578 | debug: 4.3.4 579 | kolorist: 1.8.0 580 | local-pkg: 0.4.3 581 | transitivePeerDependencies: 582 | - supports-color 583 | dev: true 584 | 585 | /@jridgewell/gen-mapping@0.3.3: 586 | resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} 587 | engines: {node: '>=6.0.0'} 588 | dependencies: 589 | '@jridgewell/set-array': 1.1.2 590 | '@jridgewell/sourcemap-codec': 1.4.15 591 | '@jridgewell/trace-mapping': 0.3.20 592 | dev: true 593 | 594 | /@jridgewell/resolve-uri@3.1.1: 595 | resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} 596 | engines: {node: '>=6.0.0'} 597 | dev: true 598 | 599 | /@jridgewell/set-array@1.1.2: 600 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 601 | engines: {node: '>=6.0.0'} 602 | dev: true 603 | 604 | /@jridgewell/sourcemap-codec@1.4.15: 605 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 606 | dev: true 607 | 608 | /@jridgewell/trace-mapping@0.3.20: 609 | resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} 610 | dependencies: 611 | '@jridgewell/resolve-uri': 3.1.1 612 | '@jridgewell/sourcemap-codec': 1.4.15 613 | dev: true 614 | 615 | /@nodelib/fs.scandir@2.1.5: 616 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 617 | engines: {node: '>= 8'} 618 | dependencies: 619 | '@nodelib/fs.stat': 2.0.5 620 | run-parallel: 1.2.0 621 | dev: true 622 | 623 | /@nodelib/fs.stat@2.0.5: 624 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 625 | engines: {node: '>= 8'} 626 | dev: true 627 | 628 | /@nodelib/fs.walk@1.2.8: 629 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 630 | engines: {node: '>= 8'} 631 | dependencies: 632 | '@nodelib/fs.scandir': 2.1.5 633 | fastq: 1.15.0 634 | dev: true 635 | 636 | /@polka/url@1.0.0-next.24: 637 | resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==} 638 | dev: true 639 | 640 | /@rollup/pluginutils@5.1.0: 641 | resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} 642 | engines: {node: '>=14.0.0'} 643 | peerDependencies: 644 | rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 645 | peerDependenciesMeta: 646 | rollup: 647 | optional: true 648 | dependencies: 649 | '@types/estree': 1.0.5 650 | estree-walker: 2.0.2 651 | picomatch: 2.3.1 652 | dev: true 653 | 654 | /@types/babel__core@7.20.5: 655 | resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} 656 | dependencies: 657 | '@babel/parser': 7.23.5 658 | '@babel/types': 7.23.5 659 | '@types/babel__generator': 7.6.7 660 | '@types/babel__template': 7.4.4 661 | '@types/babel__traverse': 7.20.4 662 | dev: true 663 | 664 | /@types/babel__generator@7.6.7: 665 | resolution: {integrity: sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==} 666 | dependencies: 667 | '@babel/types': 7.23.5 668 | dev: true 669 | 670 | /@types/babel__template@7.4.4: 671 | resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} 672 | dependencies: 673 | '@babel/parser': 7.23.5 674 | '@babel/types': 7.23.5 675 | dev: true 676 | 677 | /@types/babel__traverse@7.20.4: 678 | resolution: {integrity: sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==} 679 | dependencies: 680 | '@babel/types': 7.23.5 681 | dev: true 682 | 683 | /@types/estree@1.0.5: 684 | resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} 685 | dev: true 686 | 687 | /@unocss/astro@0.58.0(vite@4.5.0): 688 | resolution: {integrity: sha512-df+tEFO5eKXjQOwSWQhS9IdjD0sfLHLtn8U09sEKR2Nmh5CvpwyBxmvLQgOCilPou7ehmyKfsyGRLZg7IMp+Ew==} 689 | peerDependencies: 690 | vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 691 | peerDependenciesMeta: 692 | vite: 693 | optional: true 694 | dependencies: 695 | '@unocss/core': 0.58.0 696 | '@unocss/reset': 0.58.0 697 | '@unocss/vite': 0.58.0(vite@4.5.0) 698 | vite: 4.5.0 699 | transitivePeerDependencies: 700 | - rollup 701 | dev: true 702 | 703 | /@unocss/cli@0.58.0: 704 | resolution: {integrity: sha512-rhsrDBxAVueygMcAbMkbuvsHbBL2rG6N96LllYwHn16FLgOE3Sf4JW1/LlNjQje3BtwMMtbSCCAeu2SryFhzbw==} 705 | engines: {node: '>=14'} 706 | hasBin: true 707 | dependencies: 708 | '@ampproject/remapping': 2.2.1 709 | '@rollup/pluginutils': 5.1.0 710 | '@unocss/config': 0.58.0 711 | '@unocss/core': 0.58.0 712 | '@unocss/preset-uno': 0.58.0 713 | cac: 6.7.14 714 | chokidar: 3.5.3 715 | colorette: 2.0.20 716 | consola: 3.2.3 717 | fast-glob: 3.3.2 718 | magic-string: 0.30.5 719 | pathe: 1.1.1 720 | perfect-debounce: 1.0.0 721 | transitivePeerDependencies: 722 | - rollup 723 | dev: true 724 | 725 | /@unocss/config@0.58.0: 726 | resolution: {integrity: sha512-WQD29gCZ7cajnMzezD1PRW0qQSxo/C6PX9ktygwhdinFx9nXuLZnKFOz65TiI8y48e53g1i7ivvgY3m4Sq5mIg==} 727 | engines: {node: '>=14'} 728 | dependencies: 729 | '@unocss/core': 0.58.0 730 | unconfig: 0.3.11 731 | dev: true 732 | 733 | /@unocss/core@0.58.0: 734 | resolution: {integrity: sha512-KhABQXGE2AgtO9vE28d+HnciuyGDcuygsnQdUwlzUuR4K05OSw2kRE9emRN4HaMycD+gA/zDbQrJxTXb6mQUiA==} 735 | dev: true 736 | 737 | /@unocss/extractor-arbitrary-variants@0.58.0: 738 | resolution: {integrity: sha512-s9wK2UugJM0WK1HpgPz2kTbpeyQc46zais+nauN/ykVX6NMq8PtGzSWszzf+0aIbtWAQGiqAfiYNTpf09tJHfg==} 739 | dependencies: 740 | '@unocss/core': 0.58.0 741 | dev: true 742 | 743 | /@unocss/inspector@0.58.0: 744 | resolution: {integrity: sha512-ZC4QauFGdh3/VkzW/FqkO2R03JEbzGNuX0DK03pwas8/jFIGh8pPldesj8GEKm1YWr1emx9cw7JUnhR8XSUBlA==} 745 | dependencies: 746 | '@unocss/core': 0.58.0 747 | '@unocss/rule-utils': 0.58.0 748 | gzip-size: 6.0.0 749 | sirv: 2.0.3 750 | dev: true 751 | 752 | /@unocss/postcss@0.58.0(postcss@8.4.31): 753 | resolution: {integrity: sha512-2hAwLbfUFqysi8FN1cn3xkHy5GhLMlYy6W4NrAZ2ws7F2MKpsCT2xCj7dT5cI2tW8ulD2YoVbKH15dBhNsMNUA==} 754 | engines: {node: '>=14'} 755 | peerDependencies: 756 | postcss: ^8.4.21 757 | dependencies: 758 | '@unocss/config': 0.58.0 759 | '@unocss/core': 0.58.0 760 | '@unocss/rule-utils': 0.58.0 761 | css-tree: 2.3.1 762 | fast-glob: 3.3.2 763 | magic-string: 0.30.5 764 | postcss: 8.4.31 765 | dev: true 766 | 767 | /@unocss/preset-attributify@0.58.0: 768 | resolution: {integrity: sha512-Ew78noYes12K9gk4dF36MkjpiIqTi1XVqcniiAzxCkzuctxN4B57vW3LVTwjInGmWNNKWN3UNR4q1o0VxH4xJg==} 769 | dependencies: 770 | '@unocss/core': 0.58.0 771 | dev: true 772 | 773 | /@unocss/preset-icons@0.58.0: 774 | resolution: {integrity: sha512-niT32avw+8l+L40LGhrmX6qDV9Z8/gOn4xjjRhLZZouKni3CJOpz9taILyF4xp1nak5nxGT4wa0tuC/htvOF5A==} 775 | dependencies: 776 | '@iconify/utils': 2.1.12 777 | '@unocss/core': 0.58.0 778 | ofetch: 1.3.3 779 | transitivePeerDependencies: 780 | - supports-color 781 | dev: true 782 | 783 | /@unocss/preset-mini@0.58.0: 784 | resolution: {integrity: sha512-oMliJZVTN3ecAvf52yN+MyJszaJOZoKwMMbUAFqVis62MaqRzZ8mSw12QFLFyX2pltulDFpMBTAKro+hP0wXEg==} 785 | dependencies: 786 | '@unocss/core': 0.58.0 787 | '@unocss/extractor-arbitrary-variants': 0.58.0 788 | '@unocss/rule-utils': 0.58.0 789 | dev: true 790 | 791 | /@unocss/preset-tagify@0.58.0: 792 | resolution: {integrity: sha512-I+dzfs/bofiGb2AUxkhcTDhB+r2+/3SO81PFwf3Ae7afnzhA2SLsKAkEqO8YN3M3mwZL7IfXn6vpsWeEAlk/yw==} 793 | dependencies: 794 | '@unocss/core': 0.58.0 795 | dev: true 796 | 797 | /@unocss/preset-typography@0.58.0: 798 | resolution: {integrity: sha512-8qo+Z1CJtXFMDbAvtizUTRLuLxCIzytgYU0GmuRkfc2iwASSDNDsvh8nAYQfWpyAEOV7QEHtS9c9xL4b0c89FA==} 799 | dependencies: 800 | '@unocss/core': 0.58.0 801 | '@unocss/preset-mini': 0.58.0 802 | dev: true 803 | 804 | /@unocss/preset-uno@0.58.0: 805 | resolution: {integrity: sha512-DpgfjtvSgsWeyZH+jQHc1k5IReiZNb7oGpHVnfF6SlHETTnMHSeNetxkPQWYrqJLPI6llNLPTdTa5j47NtmOiA==} 806 | dependencies: 807 | '@unocss/core': 0.58.0 808 | '@unocss/preset-mini': 0.58.0 809 | '@unocss/preset-wind': 0.58.0 810 | '@unocss/rule-utils': 0.58.0 811 | dev: true 812 | 813 | /@unocss/preset-web-fonts@0.58.0: 814 | resolution: {integrity: sha512-QarDDEUlexQ2IIn23pE1eHDskG2Tz+JjCe+FAN0DoNLLhvUUWSB4cQIMFWP6dSMJ047Blj9IpgAl9dERICW1qQ==} 815 | dependencies: 816 | '@unocss/core': 0.58.0 817 | ofetch: 1.3.3 818 | dev: true 819 | 820 | /@unocss/preset-wind@0.58.0: 821 | resolution: {integrity: sha512-2zgaIy9RAGie9CsUYCkYRDSERBi8kG6Q/mQLgNfP9HMz5IThlnDHFWF/hLAVD51xQUg9gH8qWBR9kN/1ioT5Tw==} 822 | dependencies: 823 | '@unocss/core': 0.58.0 824 | '@unocss/preset-mini': 0.58.0 825 | '@unocss/rule-utils': 0.58.0 826 | dev: true 827 | 828 | /@unocss/reset@0.58.0: 829 | resolution: {integrity: sha512-UVZ5kz37JGbwAA06k/gjKYcekcTwi6oIhev1EpTtCvHLL6XYcYqcwb/u4Wjzprd3L3lxDGYXvGdjREGm2u7vbQ==} 830 | dev: true 831 | 832 | /@unocss/rule-utils@0.58.0: 833 | resolution: {integrity: sha512-LBJ9dJ/j5UIMzJF7pmIig55MtJAYtG+tn/zQRveZuPRVahzP+KqwlyB7u3uCUnQhdgo/MJODMcqyr0jl6+kTuA==} 834 | engines: {node: '>=14'} 835 | dependencies: 836 | '@unocss/core': 0.58.0 837 | magic-string: 0.30.5 838 | dev: true 839 | 840 | /@unocss/scope@0.58.0: 841 | resolution: {integrity: sha512-XgUXZJvbxWSRC/DNOWI5DYdR6Nd6IZxsE5ls3AFA5msgtk5OH4YNQELLMabQw7xbRbU/fftlRJa3vncSfOyl6w==} 842 | dev: true 843 | 844 | /@unocss/transformer-attributify-jsx-babel@0.58.0: 845 | resolution: {integrity: sha512-ckDq/q476x2yikjS8usmSUGuakqMQrg2pm8sdBINTPdJxGc7kJRvI5UDnzRw4W9hE5IH+E4gg0XfCtFad0O3eg==} 846 | dependencies: 847 | '@babel/core': 7.23.5 848 | '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5) 849 | '@babel/preset-typescript': 7.23.3(@babel/core@7.23.5) 850 | '@unocss/core': 0.58.0 851 | transitivePeerDependencies: 852 | - supports-color 853 | dev: true 854 | 855 | /@unocss/transformer-attributify-jsx@0.58.0: 856 | resolution: {integrity: sha512-QDdBEFDE7ntfCH7+8zHRW72MIQ9NH3uYGUE7lYgr5Ap8qzBHCxMT1kUrY6gwuoo3U4dMu2wruglYRHD88hvGkw==} 857 | dependencies: 858 | '@unocss/core': 0.58.0 859 | dev: true 860 | 861 | /@unocss/transformer-compile-class@0.58.0: 862 | resolution: {integrity: sha512-/BysfTg2q9sGPfiRHqWw/bT60/gjpBGBRVkIFsG4WVT2pgf3BfQUPu5FumSvZSRd0rA/pR57Lp6ZREAdj6+q+A==} 863 | dependencies: 864 | '@unocss/core': 0.58.0 865 | dev: true 866 | 867 | /@unocss/transformer-directives@0.58.0: 868 | resolution: {integrity: sha512-sU2U/aIykRkGGbA4Qo9Z5XE/KqWf7KhBwC1m8pUoqjawsZex4aVnQgXzDPfcjtmy6pElwK0z2U5DnO+OK9vCgQ==} 869 | dependencies: 870 | '@unocss/core': 0.58.0 871 | '@unocss/rule-utils': 0.58.0 872 | css-tree: 2.3.1 873 | dev: true 874 | 875 | /@unocss/transformer-variant-group@0.58.0: 876 | resolution: {integrity: sha512-O2n8uVIpNic57rrkaaQ8jnC1WJ9N6FkoqxatRDXZ368aJ1CJNya0ZcVUL6lGGND0bOLXen4WmEN62ZxEWTqdkA==} 877 | dependencies: 878 | '@unocss/core': 0.58.0 879 | dev: true 880 | 881 | /@unocss/vite@0.58.0(vite@4.5.0): 882 | resolution: {integrity: sha512-OCUOLMSOBEtXOEyBbAvMI3/xdR175BWRzmvV9Wc34ANZclEvCdVH8+WU725ibjY4VT0gVIuX68b13fhXdHV41A==} 883 | peerDependencies: 884 | vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 885 | dependencies: 886 | '@ampproject/remapping': 2.2.1 887 | '@rollup/pluginutils': 5.1.0 888 | '@unocss/config': 0.58.0 889 | '@unocss/core': 0.58.0 890 | '@unocss/inspector': 0.58.0 891 | '@unocss/scope': 0.58.0 892 | '@unocss/transformer-directives': 0.58.0 893 | chokidar: 3.5.3 894 | fast-glob: 3.3.2 895 | magic-string: 0.30.5 896 | vite: 4.5.0 897 | transitivePeerDependencies: 898 | - rollup 899 | dev: true 900 | 901 | /@yr/monotone-cubic-spline@1.0.3: 902 | resolution: {integrity: sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==} 903 | dev: false 904 | 905 | /acorn@8.11.2: 906 | resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} 907 | engines: {node: '>=0.4.0'} 908 | hasBin: true 909 | dev: true 910 | 911 | /ansi-styles@3.2.1: 912 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 913 | engines: {node: '>=4'} 914 | dependencies: 915 | color-convert: 1.9.3 916 | dev: true 917 | 918 | /anymatch@3.1.3: 919 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 920 | engines: {node: '>= 8'} 921 | dependencies: 922 | normalize-path: 3.0.0 923 | picomatch: 2.3.1 924 | dev: true 925 | 926 | /apexcharts@3.44.0: 927 | resolution: {integrity: sha512-u7Xzrbcxc2yWznN78Jh5NMCYVAsWDfBjRl5ea++rVzFAqjU2hLz4RgKIFwYOBDRQtW1e/Qz8azJTqIJ1+Vu9Qg==} 928 | dependencies: 929 | '@yr/monotone-cubic-spline': 1.0.3 930 | svg.draggable.js: 2.2.2 931 | svg.easing.js: 2.0.0 932 | svg.filter.js: 2.0.2 933 | svg.pathmorphing.js: 0.1.3 934 | svg.resize.js: 1.4.3 935 | svg.select.js: 3.0.1 936 | dev: false 937 | 938 | /babel-plugin-jsx-dom-expressions@0.37.9(@babel/core@7.23.5): 939 | resolution: {integrity: sha512-6w+zs2i14fVanj4e1hXCU5cp+x0U0LJ5jScknpMZZUteHhwFRGJflHMVJ+xAcW7ku41FYjr7DgtK9mnc2SXlJg==} 940 | peerDependencies: 941 | '@babel/core': ^7.20.12 942 | dependencies: 943 | '@babel/core': 7.23.5 944 | '@babel/helper-module-imports': 7.18.6 945 | '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5) 946 | '@babel/types': 7.23.5 947 | html-entities: 2.3.3 948 | validate-html-nesting: 1.2.2 949 | dev: true 950 | 951 | /babel-preset-solid@1.8.6(@babel/core@7.23.5): 952 | resolution: {integrity: sha512-Ened42CHjU4EFkvNeS042/3Pm21yvMWn8p4G4ddzQTlKaMwSGGD1VciA/e7EshBVHJCcBj9vHiUd/r3A4qLPZA==} 953 | peerDependencies: 954 | '@babel/core': ^7.0.0 955 | dependencies: 956 | '@babel/core': 7.23.5 957 | babel-plugin-jsx-dom-expressions: 0.37.9(@babel/core@7.23.5) 958 | dev: true 959 | 960 | /binary-extensions@2.2.0: 961 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 962 | engines: {node: '>=8'} 963 | dev: true 964 | 965 | /braces@3.0.2: 966 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 967 | engines: {node: '>=8'} 968 | dependencies: 969 | fill-range: 7.0.1 970 | dev: true 971 | 972 | /browserslist@4.22.1: 973 | resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} 974 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 975 | hasBin: true 976 | dependencies: 977 | caniuse-lite: 1.0.30001565 978 | electron-to-chromium: 1.4.598 979 | node-releases: 2.0.13 980 | update-browserslist-db: 1.0.13(browserslist@4.22.1) 981 | dev: true 982 | 983 | /cac@6.7.14: 984 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 985 | engines: {node: '>=8'} 986 | dev: true 987 | 988 | /caniuse-lite@1.0.30001565: 989 | resolution: {integrity: sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==} 990 | dev: true 991 | 992 | /chalk@2.4.2: 993 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 994 | engines: {node: '>=4'} 995 | dependencies: 996 | ansi-styles: 3.2.1 997 | escape-string-regexp: 1.0.5 998 | supports-color: 5.5.0 999 | dev: true 1000 | 1001 | /chokidar@3.5.3: 1002 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} 1003 | engines: {node: '>= 8.10.0'} 1004 | dependencies: 1005 | anymatch: 3.1.3 1006 | braces: 3.0.2 1007 | glob-parent: 5.1.2 1008 | is-binary-path: 2.1.0 1009 | is-glob: 4.0.3 1010 | normalize-path: 3.0.0 1011 | readdirp: 3.6.0 1012 | optionalDependencies: 1013 | fsevents: 2.3.3 1014 | dev: true 1015 | 1016 | /color-convert@1.9.3: 1017 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 1018 | dependencies: 1019 | color-name: 1.1.3 1020 | dev: true 1021 | 1022 | /color-name@1.1.3: 1023 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} 1024 | dev: true 1025 | 1026 | /colorette@2.0.20: 1027 | resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} 1028 | dev: true 1029 | 1030 | /consola@3.2.3: 1031 | resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} 1032 | engines: {node: ^14.18.0 || >=16.10.0} 1033 | dev: true 1034 | 1035 | /convert-source-map@2.0.0: 1036 | resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} 1037 | dev: true 1038 | 1039 | /cross-spawn@7.0.3: 1040 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 1041 | engines: {node: '>= 8'} 1042 | dependencies: 1043 | path-key: 3.1.1 1044 | shebang-command: 2.0.0 1045 | which: 2.0.2 1046 | dev: true 1047 | 1048 | /css-tree@2.3.1: 1049 | resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} 1050 | engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} 1051 | dependencies: 1052 | mdn-data: 2.0.30 1053 | source-map-js: 1.0.2 1054 | dev: true 1055 | 1056 | /csstype@3.1.2: 1057 | resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} 1058 | 1059 | /debug@4.3.4: 1060 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 1061 | engines: {node: '>=6.0'} 1062 | peerDependencies: 1063 | supports-color: '*' 1064 | peerDependenciesMeta: 1065 | supports-color: 1066 | optional: true 1067 | dependencies: 1068 | ms: 2.1.2 1069 | dev: true 1070 | 1071 | /defu@6.1.3: 1072 | resolution: {integrity: sha512-Vy2wmG3NTkmHNg/kzpuvHhkqeIx3ODWqasgCRbKtbXEN0G+HpEEv9BtJLp7ZG1CZloFaC41Ah3ZFbq7aqCqMeQ==} 1073 | 1074 | /destr@2.0.2: 1075 | resolution: {integrity: sha512-65AlobnZMiCET00KaFFjUefxDX0khFA/E4myqZ7a6Sq1yZtR8+FVIvilVX66vF2uobSumxooYZChiRPCKNqhmg==} 1076 | dev: true 1077 | 1078 | /duplexer@0.1.2: 1079 | resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} 1080 | dev: true 1081 | 1082 | /electron-to-chromium@1.4.598: 1083 | resolution: {integrity: sha512-0JnipX0scPUlwsptQVCZggoCpREv+IrVD3h0ZG+sldmK9L27tSV3QjV8+QdaA4qQTzDf3PluNS45YYJky1oASw==} 1084 | dev: true 1085 | 1086 | /esbuild@0.18.20: 1087 | resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} 1088 | engines: {node: '>=12'} 1089 | hasBin: true 1090 | requiresBuild: true 1091 | optionalDependencies: 1092 | '@esbuild/android-arm': 0.18.20 1093 | '@esbuild/android-arm64': 0.18.20 1094 | '@esbuild/android-x64': 0.18.20 1095 | '@esbuild/darwin-arm64': 0.18.20 1096 | '@esbuild/darwin-x64': 0.18.20 1097 | '@esbuild/freebsd-arm64': 0.18.20 1098 | '@esbuild/freebsd-x64': 0.18.20 1099 | '@esbuild/linux-arm': 0.18.20 1100 | '@esbuild/linux-arm64': 0.18.20 1101 | '@esbuild/linux-ia32': 0.18.20 1102 | '@esbuild/linux-loong64': 0.18.20 1103 | '@esbuild/linux-mips64el': 0.18.20 1104 | '@esbuild/linux-ppc64': 0.18.20 1105 | '@esbuild/linux-riscv64': 0.18.20 1106 | '@esbuild/linux-s390x': 0.18.20 1107 | '@esbuild/linux-x64': 0.18.20 1108 | '@esbuild/netbsd-x64': 0.18.20 1109 | '@esbuild/openbsd-x64': 0.18.20 1110 | '@esbuild/sunos-x64': 0.18.20 1111 | '@esbuild/win32-arm64': 0.18.20 1112 | '@esbuild/win32-ia32': 0.18.20 1113 | '@esbuild/win32-x64': 0.18.20 1114 | dev: true 1115 | 1116 | /escalade@3.1.1: 1117 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} 1118 | engines: {node: '>=6'} 1119 | dev: true 1120 | 1121 | /escape-string-regexp@1.0.5: 1122 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 1123 | engines: {node: '>=0.8.0'} 1124 | dev: true 1125 | 1126 | /estree-walker@2.0.2: 1127 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} 1128 | dev: true 1129 | 1130 | /execa@5.1.1: 1131 | resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} 1132 | engines: {node: '>=10'} 1133 | dependencies: 1134 | cross-spawn: 7.0.3 1135 | get-stream: 6.0.1 1136 | human-signals: 2.1.0 1137 | is-stream: 2.0.1 1138 | merge-stream: 2.0.0 1139 | npm-run-path: 4.0.1 1140 | onetime: 5.1.2 1141 | signal-exit: 3.0.7 1142 | strip-final-newline: 2.0.0 1143 | dev: true 1144 | 1145 | /fast-glob@3.3.2: 1146 | resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} 1147 | engines: {node: '>=8.6.0'} 1148 | dependencies: 1149 | '@nodelib/fs.stat': 2.0.5 1150 | '@nodelib/fs.walk': 1.2.8 1151 | glob-parent: 5.1.2 1152 | merge2: 1.4.1 1153 | micromatch: 4.0.5 1154 | dev: true 1155 | 1156 | /fastq@1.15.0: 1157 | resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} 1158 | dependencies: 1159 | reusify: 1.0.4 1160 | dev: true 1161 | 1162 | /fill-range@7.0.1: 1163 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 1164 | engines: {node: '>=8'} 1165 | dependencies: 1166 | to-regex-range: 5.0.1 1167 | dev: true 1168 | 1169 | /find-up@5.0.0: 1170 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 1171 | engines: {node: '>=10'} 1172 | dependencies: 1173 | locate-path: 6.0.0 1174 | path-exists: 4.0.0 1175 | dev: true 1176 | 1177 | /fsevents@2.3.3: 1178 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 1179 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 1180 | os: [darwin] 1181 | requiresBuild: true 1182 | dev: true 1183 | optional: true 1184 | 1185 | /gensync@1.0.0-beta.2: 1186 | resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} 1187 | engines: {node: '>=6.9.0'} 1188 | dev: true 1189 | 1190 | /get-stream@6.0.1: 1191 | resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} 1192 | engines: {node: '>=10'} 1193 | dev: true 1194 | 1195 | /glob-parent@5.1.2: 1196 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 1197 | engines: {node: '>= 6'} 1198 | dependencies: 1199 | is-glob: 4.0.3 1200 | dev: true 1201 | 1202 | /globals@11.12.0: 1203 | resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} 1204 | engines: {node: '>=4'} 1205 | dev: true 1206 | 1207 | /gzip-size@6.0.0: 1208 | resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} 1209 | engines: {node: '>=10'} 1210 | dependencies: 1211 | duplexer: 0.1.2 1212 | dev: true 1213 | 1214 | /has-flag@3.0.0: 1215 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} 1216 | engines: {node: '>=4'} 1217 | dev: true 1218 | 1219 | /html-entities@2.3.3: 1220 | resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} 1221 | dev: true 1222 | 1223 | /human-signals@2.1.0: 1224 | resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} 1225 | engines: {node: '>=10.17.0'} 1226 | dev: true 1227 | 1228 | /is-binary-path@2.1.0: 1229 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 1230 | engines: {node: '>=8'} 1231 | dependencies: 1232 | binary-extensions: 2.2.0 1233 | dev: true 1234 | 1235 | /is-extglob@2.1.1: 1236 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 1237 | engines: {node: '>=0.10.0'} 1238 | dev: true 1239 | 1240 | /is-glob@4.0.3: 1241 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 1242 | engines: {node: '>=0.10.0'} 1243 | dependencies: 1244 | is-extglob: 2.1.1 1245 | dev: true 1246 | 1247 | /is-number@7.0.0: 1248 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 1249 | engines: {node: '>=0.12.0'} 1250 | dev: true 1251 | 1252 | /is-stream@2.0.1: 1253 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} 1254 | engines: {node: '>=8'} 1255 | dev: true 1256 | 1257 | /is-what@4.1.16: 1258 | resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} 1259 | engines: {node: '>=12.13'} 1260 | dev: true 1261 | 1262 | /isexe@2.0.0: 1263 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 1264 | dev: true 1265 | 1266 | /jiti@1.21.0: 1267 | resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} 1268 | hasBin: true 1269 | dev: true 1270 | 1271 | /js-tokens@4.0.0: 1272 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 1273 | dev: true 1274 | 1275 | /jsesc@2.5.2: 1276 | resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} 1277 | engines: {node: '>=4'} 1278 | hasBin: true 1279 | dev: true 1280 | 1281 | /json5@2.2.3: 1282 | resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} 1283 | engines: {node: '>=6'} 1284 | hasBin: true 1285 | dev: true 1286 | 1287 | /jsonc-parser@3.2.0: 1288 | resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} 1289 | dev: true 1290 | 1291 | /kolorist@1.8.0: 1292 | resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} 1293 | dev: true 1294 | 1295 | /local-pkg@0.4.3: 1296 | resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} 1297 | engines: {node: '>=14'} 1298 | dev: true 1299 | 1300 | /locate-path@6.0.0: 1301 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 1302 | engines: {node: '>=10'} 1303 | dependencies: 1304 | p-locate: 5.0.0 1305 | dev: true 1306 | 1307 | /lru-cache@5.1.1: 1308 | resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} 1309 | dependencies: 1310 | yallist: 3.1.1 1311 | dev: true 1312 | 1313 | /magic-string@0.30.5: 1314 | resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} 1315 | engines: {node: '>=12'} 1316 | dependencies: 1317 | '@jridgewell/sourcemap-codec': 1.4.15 1318 | dev: true 1319 | 1320 | /mdn-data@2.0.30: 1321 | resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} 1322 | dev: true 1323 | 1324 | /merge-anything@5.1.7: 1325 | resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} 1326 | engines: {node: '>=12.13'} 1327 | dependencies: 1328 | is-what: 4.1.16 1329 | dev: true 1330 | 1331 | /merge-stream@2.0.0: 1332 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} 1333 | dev: true 1334 | 1335 | /merge2@1.4.1: 1336 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 1337 | engines: {node: '>= 8'} 1338 | dev: true 1339 | 1340 | /micromatch@4.0.5: 1341 | resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} 1342 | engines: {node: '>=8.6'} 1343 | dependencies: 1344 | braces: 3.0.2 1345 | picomatch: 2.3.1 1346 | dev: true 1347 | 1348 | /mimic-fn@2.1.0: 1349 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} 1350 | engines: {node: '>=6'} 1351 | dev: true 1352 | 1353 | /mlly@1.4.2: 1354 | resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} 1355 | dependencies: 1356 | acorn: 8.11.2 1357 | pathe: 1.1.1 1358 | pkg-types: 1.0.3 1359 | ufo: 1.3.2 1360 | dev: true 1361 | 1362 | /mrmime@1.0.1: 1363 | resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} 1364 | engines: {node: '>=10'} 1365 | dev: true 1366 | 1367 | /ms@2.1.2: 1368 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 1369 | dev: true 1370 | 1371 | /nanoid@3.3.7: 1372 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 1373 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1374 | hasBin: true 1375 | dev: true 1376 | 1377 | /node-fetch-native@1.4.1: 1378 | resolution: {integrity: sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w==} 1379 | dev: true 1380 | 1381 | /node-releases@2.0.13: 1382 | resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==} 1383 | dev: true 1384 | 1385 | /normalize-path@3.0.0: 1386 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 1387 | engines: {node: '>=0.10.0'} 1388 | dev: true 1389 | 1390 | /npm-run-path@4.0.1: 1391 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} 1392 | engines: {node: '>=8'} 1393 | dependencies: 1394 | path-key: 3.1.1 1395 | dev: true 1396 | 1397 | /ofetch@1.3.3: 1398 | resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==} 1399 | dependencies: 1400 | destr: 2.0.2 1401 | node-fetch-native: 1.4.1 1402 | ufo: 1.3.2 1403 | dev: true 1404 | 1405 | /onetime@5.1.2: 1406 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} 1407 | engines: {node: '>=6'} 1408 | dependencies: 1409 | mimic-fn: 2.1.0 1410 | dev: true 1411 | 1412 | /p-limit@3.1.0: 1413 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 1414 | engines: {node: '>=10'} 1415 | dependencies: 1416 | yocto-queue: 0.1.0 1417 | dev: true 1418 | 1419 | /p-locate@5.0.0: 1420 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 1421 | engines: {node: '>=10'} 1422 | dependencies: 1423 | p-limit: 3.1.0 1424 | dev: true 1425 | 1426 | /path-exists@4.0.0: 1427 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 1428 | engines: {node: '>=8'} 1429 | dev: true 1430 | 1431 | /path-key@3.1.1: 1432 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 1433 | engines: {node: '>=8'} 1434 | dev: true 1435 | 1436 | /pathe@1.1.1: 1437 | resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} 1438 | dev: true 1439 | 1440 | /perfect-debounce@1.0.0: 1441 | resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} 1442 | dev: true 1443 | 1444 | /picocolors@1.0.0: 1445 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 1446 | dev: true 1447 | 1448 | /picomatch@2.3.1: 1449 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 1450 | engines: {node: '>=8.6'} 1451 | dev: true 1452 | 1453 | /pkg-types@1.0.3: 1454 | resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} 1455 | dependencies: 1456 | jsonc-parser: 3.2.0 1457 | mlly: 1.4.2 1458 | pathe: 1.1.1 1459 | dev: true 1460 | 1461 | /postcss@8.4.31: 1462 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 1463 | engines: {node: ^10 || ^12 || >=14} 1464 | dependencies: 1465 | nanoid: 3.3.7 1466 | picocolors: 1.0.0 1467 | source-map-js: 1.0.2 1468 | dev: true 1469 | 1470 | /queue-microtask@1.2.3: 1471 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 1472 | dev: true 1473 | 1474 | /readdirp@3.6.0: 1475 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 1476 | engines: {node: '>=8.10.0'} 1477 | dependencies: 1478 | picomatch: 2.3.1 1479 | dev: true 1480 | 1481 | /reusify@1.0.4: 1482 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 1483 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 1484 | dev: true 1485 | 1486 | /rollup@3.29.4: 1487 | resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} 1488 | engines: {node: '>=14.18.0', npm: '>=8.0.0'} 1489 | hasBin: true 1490 | optionalDependencies: 1491 | fsevents: 2.3.3 1492 | dev: true 1493 | 1494 | /run-parallel@1.2.0: 1495 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 1496 | dependencies: 1497 | queue-microtask: 1.2.3 1498 | dev: true 1499 | 1500 | /semver@6.3.1: 1501 | resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} 1502 | hasBin: true 1503 | dev: true 1504 | 1505 | /seroval@0.12.4: 1506 | resolution: {integrity: sha512-JIsZHp98o+okpYN8HEPyI9Blr0gxAUPIGvg3waXrEMFjPz9obiLYMz0uFiUGezKiCK8loosYbn8WsqO8WtAJUA==} 1507 | engines: {node: '>=10'} 1508 | 1509 | /shebang-command@2.0.0: 1510 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 1511 | engines: {node: '>=8'} 1512 | dependencies: 1513 | shebang-regex: 3.0.0 1514 | dev: true 1515 | 1516 | /shebang-regex@3.0.0: 1517 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 1518 | engines: {node: '>=8'} 1519 | dev: true 1520 | 1521 | /signal-exit@3.0.7: 1522 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} 1523 | dev: true 1524 | 1525 | /sirv@2.0.3: 1526 | resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} 1527 | engines: {node: '>= 10'} 1528 | dependencies: 1529 | '@polka/url': 1.0.0-next.24 1530 | mrmime: 1.0.1 1531 | totalist: 3.0.1 1532 | dev: true 1533 | 1534 | /solid-apexcharts@0.3.2(apexcharts@3.44.0)(solid-js@1.8.5): 1535 | resolution: {integrity: sha512-4U80dk2e/YEghD903aitBrO0l5WjUv/IhRI6Pp8mfJNT6JPkYG4HTMjBrI+XHRU8uJICJl9ZwLtkvFZnKd2Shw==} 1536 | peerDependencies: 1537 | apexcharts: ^3.40.0 1538 | solid-js: ^1.6.0 1539 | dependencies: 1540 | apexcharts: 3.44.0 1541 | defu: 6.1.3 1542 | solid-js: 1.8.5 1543 | dev: false 1544 | 1545 | /solid-js@1.8.5: 1546 | resolution: {integrity: sha512-xvtJvzJzWbsn35oKFhW9kNwaxG1Z/YLMsDp4tLVcYZTMPzvzQ8vEZuyDQ6nt7xDArVgZJ7TUFrJUwrui/oq53A==} 1547 | dependencies: 1548 | csstype: 3.1.2 1549 | seroval: 0.12.4 1550 | 1551 | /solid-refresh@0.5.3(solid-js@1.8.5): 1552 | resolution: {integrity: sha512-Otg5it5sjOdZbQZJnvo99TEBAr6J7PQ5AubZLNU6szZzg3RQQ5MX04oteBIIGDs0y2Qv8aXKm9e44V8z+UnFdw==} 1553 | peerDependencies: 1554 | solid-js: ^1.3 1555 | dependencies: 1556 | '@babel/generator': 7.23.5 1557 | '@babel/helper-module-imports': 7.22.15 1558 | '@babel/types': 7.23.5 1559 | solid-js: 1.8.5 1560 | dev: true 1561 | 1562 | /source-map-js@1.0.2: 1563 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 1564 | engines: {node: '>=0.10.0'} 1565 | dev: true 1566 | 1567 | /strip-final-newline@2.0.0: 1568 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} 1569 | engines: {node: '>=6'} 1570 | dev: true 1571 | 1572 | /supports-color@5.5.0: 1573 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 1574 | engines: {node: '>=4'} 1575 | dependencies: 1576 | has-flag: 3.0.0 1577 | dev: true 1578 | 1579 | /svg.draggable.js@2.2.2: 1580 | resolution: {integrity: sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==} 1581 | engines: {node: '>= 0.8.0'} 1582 | dependencies: 1583 | svg.js: 2.7.1 1584 | dev: false 1585 | 1586 | /svg.easing.js@2.0.0: 1587 | resolution: {integrity: sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==} 1588 | engines: {node: '>= 0.8.0'} 1589 | dependencies: 1590 | svg.js: 2.7.1 1591 | dev: false 1592 | 1593 | /svg.filter.js@2.0.2: 1594 | resolution: {integrity: sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==} 1595 | engines: {node: '>= 0.8.0'} 1596 | dependencies: 1597 | svg.js: 2.7.1 1598 | dev: false 1599 | 1600 | /svg.js@2.7.1: 1601 | resolution: {integrity: sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==} 1602 | dev: false 1603 | 1604 | /svg.pathmorphing.js@0.1.3: 1605 | resolution: {integrity: sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==} 1606 | engines: {node: '>= 0.8.0'} 1607 | dependencies: 1608 | svg.js: 2.7.1 1609 | dev: false 1610 | 1611 | /svg.resize.js@1.4.3: 1612 | resolution: {integrity: sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==} 1613 | engines: {node: '>= 0.8.0'} 1614 | dependencies: 1615 | svg.js: 2.7.1 1616 | svg.select.js: 2.1.2 1617 | dev: false 1618 | 1619 | /svg.select.js@2.1.2: 1620 | resolution: {integrity: sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==} 1621 | engines: {node: '>= 0.8.0'} 1622 | dependencies: 1623 | svg.js: 2.7.1 1624 | dev: false 1625 | 1626 | /svg.select.js@3.0.1: 1627 | resolution: {integrity: sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==} 1628 | engines: {node: '>= 0.8.0'} 1629 | dependencies: 1630 | svg.js: 2.7.1 1631 | dev: false 1632 | 1633 | /to-fast-properties@2.0.0: 1634 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} 1635 | engines: {node: '>=4'} 1636 | dev: true 1637 | 1638 | /to-regex-range@5.0.1: 1639 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1640 | engines: {node: '>=8.0'} 1641 | dependencies: 1642 | is-number: 7.0.0 1643 | dev: true 1644 | 1645 | /totalist@3.0.1: 1646 | resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} 1647 | engines: {node: '>=6'} 1648 | dev: true 1649 | 1650 | /typescript@5.2.2: 1651 | resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} 1652 | engines: {node: '>=14.17'} 1653 | hasBin: true 1654 | dev: true 1655 | 1656 | /ufo@1.3.2: 1657 | resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} 1658 | dev: true 1659 | 1660 | /unconfig@0.3.11: 1661 | resolution: {integrity: sha512-bV/nqePAKv71v3HdVUn6UefbsDKQWRX+bJIkiSm0+twIds6WiD2bJLWWT3i214+J/B4edufZpG2w7Y63Vbwxow==} 1662 | dependencies: 1663 | '@antfu/utils': 0.7.6 1664 | defu: 6.1.3 1665 | jiti: 1.21.0 1666 | mlly: 1.4.2 1667 | dev: true 1668 | 1669 | /unocss@0.58.0(postcss@8.4.31)(vite@4.5.0): 1670 | resolution: {integrity: sha512-MSPRHxBqWN+1AHGV+J5uUy4//e6ZBK6O+ISzD0qrXcCD/GNtxk1+lYjOK2ltkUiKX539+/KF91vNxzhhwEf+xA==} 1671 | engines: {node: '>=14'} 1672 | peerDependencies: 1673 | '@unocss/webpack': 0.58.0 1674 | vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 1675 | peerDependenciesMeta: 1676 | '@unocss/webpack': 1677 | optional: true 1678 | vite: 1679 | optional: true 1680 | dependencies: 1681 | '@unocss/astro': 0.58.0(vite@4.5.0) 1682 | '@unocss/cli': 0.58.0 1683 | '@unocss/core': 0.58.0 1684 | '@unocss/extractor-arbitrary-variants': 0.58.0 1685 | '@unocss/postcss': 0.58.0(postcss@8.4.31) 1686 | '@unocss/preset-attributify': 0.58.0 1687 | '@unocss/preset-icons': 0.58.0 1688 | '@unocss/preset-mini': 0.58.0 1689 | '@unocss/preset-tagify': 0.58.0 1690 | '@unocss/preset-typography': 0.58.0 1691 | '@unocss/preset-uno': 0.58.0 1692 | '@unocss/preset-web-fonts': 0.58.0 1693 | '@unocss/preset-wind': 0.58.0 1694 | '@unocss/reset': 0.58.0 1695 | '@unocss/transformer-attributify-jsx': 0.58.0 1696 | '@unocss/transformer-attributify-jsx-babel': 0.58.0 1697 | '@unocss/transformer-compile-class': 0.58.0 1698 | '@unocss/transformer-directives': 0.58.0 1699 | '@unocss/transformer-variant-group': 0.58.0 1700 | '@unocss/vite': 0.58.0(vite@4.5.0) 1701 | vite: 4.5.0 1702 | transitivePeerDependencies: 1703 | - postcss 1704 | - rollup 1705 | - supports-color 1706 | dev: true 1707 | 1708 | /update-browserslist-db@1.0.13(browserslist@4.22.1): 1709 | resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} 1710 | hasBin: true 1711 | peerDependencies: 1712 | browserslist: '>= 4.21.0' 1713 | dependencies: 1714 | browserslist: 4.22.1 1715 | escalade: 3.1.1 1716 | picocolors: 1.0.0 1717 | dev: true 1718 | 1719 | /validate-html-nesting@1.2.2: 1720 | resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} 1721 | dev: true 1722 | 1723 | /vite-plugin-solid@2.7.2(solid-js@1.8.5)(vite@4.5.0): 1724 | resolution: {integrity: sha512-GV2SMLAibOoXe76i02AsjAg7sbm/0lngBlERvJKVN67HOrJsHcWgkt0R6sfGLDJuFkv2aBe14Zm4vJcNME+7zw==} 1725 | peerDependencies: 1726 | solid-js: ^1.7.2 1727 | vite: ^3.0.0 || ^4.0.0 1728 | dependencies: 1729 | '@babel/core': 7.23.5 1730 | '@babel/preset-typescript': 7.23.3(@babel/core@7.23.5) 1731 | '@types/babel__core': 7.20.5 1732 | babel-preset-solid: 1.8.6(@babel/core@7.23.5) 1733 | merge-anything: 5.1.7 1734 | solid-js: 1.8.5 1735 | solid-refresh: 0.5.3(solid-js@1.8.5) 1736 | vite: 4.5.0 1737 | vitefu: 0.2.5(vite@4.5.0) 1738 | transitivePeerDependencies: 1739 | - supports-color 1740 | dev: true 1741 | 1742 | /vite@4.5.0: 1743 | resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==} 1744 | engines: {node: ^14.18.0 || >=16.0.0} 1745 | hasBin: true 1746 | peerDependencies: 1747 | '@types/node': '>= 14' 1748 | less: '*' 1749 | lightningcss: ^1.21.0 1750 | sass: '*' 1751 | stylus: '*' 1752 | sugarss: '*' 1753 | terser: ^5.4.0 1754 | peerDependenciesMeta: 1755 | '@types/node': 1756 | optional: true 1757 | less: 1758 | optional: true 1759 | lightningcss: 1760 | optional: true 1761 | sass: 1762 | optional: true 1763 | stylus: 1764 | optional: true 1765 | sugarss: 1766 | optional: true 1767 | terser: 1768 | optional: true 1769 | dependencies: 1770 | esbuild: 0.18.20 1771 | postcss: 8.4.31 1772 | rollup: 3.29.4 1773 | optionalDependencies: 1774 | fsevents: 2.3.3 1775 | dev: true 1776 | 1777 | /vitefu@0.2.5(vite@4.5.0): 1778 | resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} 1779 | peerDependencies: 1780 | vite: ^3.0.0 || ^4.0.0 || ^5.0.0 1781 | peerDependenciesMeta: 1782 | vite: 1783 | optional: true 1784 | dependencies: 1785 | vite: 4.5.0 1786 | dev: true 1787 | 1788 | /which@2.0.2: 1789 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1790 | engines: {node: '>= 8'} 1791 | hasBin: true 1792 | dependencies: 1793 | isexe: 2.0.0 1794 | dev: true 1795 | 1796 | /yallist@3.1.1: 1797 | resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} 1798 | dev: true 1799 | 1800 | /yocto-queue@0.1.0: 1801 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 1802 | engines: {node: '>=10'} 1803 | dev: true 1804 | --------------------------------------------------------------------------------