diff --git a/.gitignore b/.gitignore index ab308736..ac6a9ca8 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,9 @@ node_modules/ .vs/ x64/ out/ +bin/ +*.ilk +*.pdb CMakePresets.json CMakeUserPresets.json diff --git a/.vuepress/config.js b/.vuepress/config.js index 4efb8f0e..889cb274 100644 --- a/.vuepress/config.js +++ b/.vuepress/config.js @@ -28,6 +28,7 @@ export default defineUserConfig({ { text: 'std::thread 的构造-源码解析', link: srcCodePath + '01thread的构造与源码解析', }, { text: 'std::scoped_lock 的源码实现与解析', link: srcCodePath + '02scoped_lock源码解析', }, { text: 'std::async 与 std::future 源码解析', link: srcCodePath + '03async与future源码解析', }, + { text: '线程池', link: srcCodePath + "04线程池", }, ] }, ], @@ -41,8 +42,32 @@ export default defineUserConfig({ pageInfo: ['ReadingTime'], plugins: { mdEnhance: { + gfm: true, + hint: true, + vPre: true, + alert: true, + tabs: true, + codetabs: true, + align: true, + attrs: true, + sup: true, + sub: true, footnote: true, - imgLazyload: true + mark: true, + figure: true, + imgLazyload: true, + imgMark: true, + imgSize: true, + obsidianImgSize: true, + tasklist: true, + include: true, + katex: true, + component: true, + chart: true, + echarts: true, + flowchart: true, + mermaid: true, + plantuml: true, }, searchPro: true } diff --git a/.vuepress/params.js b/.vuepress/params.js index eee102f7..cfb73ec8 100644 --- a/.vuepress/params.js +++ b/.vuepress/params.js @@ -3,5 +3,7 @@ const logoPath = '/image/现代C++并发编程教程.png'; const repoName = '现代C++并发编程教程'; const repoBase = '/ModernCpp-ConcurrentProgramming-Tutorial'; const repoUrl = `https://github.com/Mq-b${repoBase}/`; +const sponsor = '/image/赞助.jpg'; +const cat = '/image/猫猫虫旋转.jpg' -export { repoUrl, repoBase, repoName, logoPath } \ No newline at end of file +export { repoUrl, repoBase, repoName, logoPath, sponsor, cat } \ No newline at end of file diff --git "a/.vuepress/public/image/\347\214\253\347\214\253\350\231\253\346\227\213\350\275\254.jpg" "b/.vuepress/public/image/\347\214\253\347\214\253\350\231\253\346\227\213\350\275\254.jpg" new file mode 100644 index 00000000..f2c960c5 Binary files /dev/null and "b/.vuepress/public/image/\347\214\253\347\214\253\350\231\253\346\227\213\350\275\254.jpg" differ diff --git "a/.vuepress/public/image/\350\265\236\345\212\251.jpg" "b/.vuepress/public/image/\350\265\236\345\212\251.jpg" new file mode 100644 index 00000000..22adddd4 Binary files /dev/null and "b/.vuepress/public/image/\350\265\236\345\212\251.jpg" differ diff --git a/README.md b/README.md index 4e4f62b5..415527f9 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ # 现代C++并发编程教程 -本仓库用来存放 B 站课程[《现代 C++ 并发编程教程》]()的教案、代码。 +本仓库用来存放 B 站课程[《现代 C++ 并发编程教程》](https://www.bilibili.com/cheese/play/ss34184)的教案、代码。 不管是否购买课程,任何组织和个人遵守 [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh-hans) 协议均可随意使用学习。 -[捐赠](/image/捐赠)、[issues](https://github.com/Mq-b/ModernCpp-ConcurrentProgramming-Tutorial/issues)、[pr](https://github.com/Mq-b/ModernCpp-ConcurrentProgramming-Tutorial/pulls) 均会在致谢列表中**铭记您的贡献**。 +[捐赠](https://github.com/Mq-b/ModernCpp-ConcurrentProgramming-Tutorial/tree/main/image/%E6%8D%90%E8%B5%A0)、[issues](https://github.com/Mq-b/ModernCpp-ConcurrentProgramming-Tutorial/issues)、[pr](https://github.com/Mq-b/ModernCpp-ConcurrentProgramming-Tutorial/pulls) 均会在致谢列表中**铭记您的贡献**。 @@ -25,3 +25,16 @@   虽强调现代,但不用担心,我们几乎是从头教学,即使你从来没使用过 C++ 进行多线程编程,也不成问题。   我们希望您的编译器版本和标准尽可能的高,我们的代码均会测试三大编译器 gcc、clang、msvc。需要更高的标准会进行强调。 + +
+ +![猫猫虫](./image/猫猫虫旋转.jpg) + +如果你觉得本仓库对你有所帮助,可以通过支付宝赞助白老师,激励白老师有更多的精力和信心维护本仓库。 + +
+ +> [!TIP] +> 每一位开发者赞助 `30`,白老师一天的食品安全就有了着落。 + +cpp diff --git a/SUMMARY.md b/SUMMARY.md deleted file mode 100644 index cb06bc84..00000000 --- a/SUMMARY.md +++ /dev/null @@ -1,14 +0,0 @@ -# Summary - -* [首页](README.md) -* [阅读须知](md/README.md) -* [基本概念](md/01基本概念.md) -* [使用线程](md/02使用线程.md) -* [共享数据](md/03共享数据.md) -* [同步操作](md/04同步操作.md) -* [内存模型与原子操作](md/05内存模型与原子操作.md) -* [协程](md/06协程.md) -* [详细分析](md/详细分析/README.md) - * [`std::thread` 的构造-源码解析](md/详细分析/01thread的构造与源码解析.md) - * [`std::scoped_lock` 的源码实现与解析](md/详细分析/02scoped_lock源码解析.md) - * [`std::async` 与 `std::future` 源码解析](md/详细分析/03async与future源码解析.md) diff --git "a/code/04\345\220\214\346\255\245\346\223\215\344\275\234/async_progress_bar/async_progress_bar.h" "b/code/04\345\220\214\346\255\245\346\223\215\344\275\234/async_progress_bar/async_progress_bar.h" index f97b0ac9..c3f43d3f 100644 --- "a/code/04\345\220\214\346\255\245\346\223\215\344\275\234/async_progress_bar/async_progress_bar.h" +++ "b/code/04\345\220\214\346\255\245\346\223\215\344\275\234/async_progress_bar/async_progress_bar.h" @@ -67,5 +67,5 @@ class async_progress_bar : public QMainWindow{ QPushButton* button{}; QPushButton* button2{}; Ui::async_progress_barClass ui{}; - std::futurefuture; + std::future future; }; diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/01-HelloWorld.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/01-HelloWorld.cpp new file mode 100644 index 00000000..3fbaabd8 --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/01-HelloWorld.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +void hello(){ + std::cout << "Hello World" << std::endl; + // std::this_thread::sleep_for(std::chrono::seconds(5)); +} + +int main(){ + std::thread t; // 默认构造?构造不关联线程的 thread 对象 + std::cout < +#include +#include +#include +#include +#include + +template +auto sum(ForwardIt first, ForwardIt last) { + using value_type = std::iter_value_t; + std::size_t num_threads = std::thread::hardware_concurrency(); + std::ptrdiff_t distance = std::distance(first, last); + + if (distance > 1024000) { + // 计算每个线程处理的元素数量 + std::size_t chunk_size = distance / num_threads; + std::size_t remainder = distance % num_threads; + + // 存储每个线程的结果 + std::vector results{ num_threads }; + + // 存储关联线程的线程对象 + std::vector threads; + + // 创建并启动线程 + auto start = first; + for (std::size_t i = 0; i < num_threads; ++i) { + auto end = std::next(start, chunk_size + (i < remainder ? 1 : 0)); + threads.emplace_back([start, end, &results, i] { + results[i] = std::accumulate(start, end, value_type{}); + }); + start = end; // 开始迭代器不断向前 + } + + // 等待所有线程执行完毕 + for (auto& thread : threads) + thread.join(); + + // 汇总线程的计算结果 + value_type total_sum = std::accumulate(results.begin(), results.end(), value_type{}); + return total_sum; + } + + value_type total_sum = std::accumulate(first, last, value_type{}); + return total_sum; +} + +int main() { + std::vector vecs{ "1","2","3","4" }; + auto result = sum(vecs.begin(), vecs.end()); + std::cout << result << '\n'; + + vecs.clear(); + for (std::size_t i = 0; i <= 1024001u; ++i) { + vecs.push_back(std::to_string(i)); + } + result = sum(vecs.begin(), vecs.end()); + std::cout << result << '\n'; +} \ No newline at end of file diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/03-thread_management.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/03-thread_management.cpp new file mode 100644 index 00000000..0a9f1371 --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/03-thread_management.cpp @@ -0,0 +1,32 @@ +#include +#include + +struct func { + int& m_i; + func(int& i) :m_i{ i } {} + void operator()(int n)const { + for (int i = 0; i <= n; ++i) { + m_i += i; // 可能悬空引用 + } + } +}; + +void f2() { throw std::runtime_error("test f2()"); } + +void f() { + int n = 0; + std::thread t{ func{n},10 }; + try { + // todo.. 一些当前线程可能抛出异常的代码 + f2(); + t.join(); + } + catch (...) { + t.join(); // 1 + // 如果此处不抛出 会掩盖错误 我们根本没有处理 没有解决 + } +} + +int main() { + f(); +} \ No newline at end of file diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/04-RAII.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/04-RAII.cpp new file mode 100644 index 00000000..e3e793af --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/04-RAII.cpp @@ -0,0 +1,48 @@ +#include +#include +#include + +struct func { + int& m_i; + func(int& i) :m_i{ i } {} + void operator()(int n)const { + for (int i = 0; i <= n; ++i) { + m_i += i; // 可能悬空引用 + } + } +}; + +void f2(){ + // todo.. + throw std::runtime_error("f2 error"); +} + +class thread_guard{ +public: + explicit thread_guard(std::thread& t) :thread_{ t } + {} + ~thread_guard(){ + std::puts("析构"); + if(thread_.joinable()){ // 如果当前有活跃线程 则进行 join + thread_.join(); + } + } + thread_guard& operator=(const thread_guard&) = delete; + thread_guard(const thread_guard&) = delete; + + std::thread& thread_; +}; + +void f() { + int n = 0; + std::thread t{ func{n},10 }; + thread_guard g(t); + f2(); // 可能抛出异常 +} + +int main(){ + // 栈回溯 + try{ + f(); + }catch (...){} +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/05-\344\274\240\351\200\222\345\217\202\346\225\260.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/05-\344\274\240\351\200\222\345\217\202\346\225\260.cpp" new file mode 100644 index 00000000..1ae06177 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/05-\344\274\240\351\200\222\345\217\202\346\225\260.cpp" @@ -0,0 +1,20 @@ +#include +#include +#include + +void f(const std::string&); + +void test() { + char buffer[1024]{}; + //todo.. code + std::thread t{ f, std::string(buffer) }; // std::string(buffer) 构造对象,由 std::string 对象自行管理 + t.detach(); +} + +int main(){ + // A 的引用只能引用 A 类型,或者以任何形式 转换到 A + double a = 1; + const int& p = a; // a 隐式转换到了 int 类型,这个转换是纯右值表达式 + // 因为 const T& 可以接右值表达式,所以才能通过编译 + const std::string& s = "123"; // "123" 构造了 std::string 对象 +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/06-this_thread\345\221\275\345\220\215\347\251\272\351\227\264.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/06-this_thread\345\221\275\345\220\215\347\251\272\351\227\264.cpp" new file mode 100644 index 00000000..b99567e7 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/06-this_thread\345\221\275\345\220\215\347\251\272\351\227\264.cpp" @@ -0,0 +1,28 @@ +#include +#include +#include +using namespace std::chrono_literals; + +int main() { + // 获取当前时间点 + auto now = std::chrono::system_clock::now(); + + // 设置要等待的时间点为当前时间点之后的5秒 + auto wakeup_time = now + 5s; + + // 输出当前时间 + auto now_time = std::chrono::system_clock::to_time_t(now); + std::cout << "Current time:\t\t" << std::put_time(std::localtime(&now_time), "%H:%M:%S") << std::endl; + + // 输出等待的时间点 + auto wakeup_time_time = std::chrono::system_clock::to_time_t(wakeup_time); + std::cout << "Waiting until:\t\t" << std::put_time(std::localtime(&wakeup_time_time), "%H:%M:%S") << std::endl; + + // 等待到指定的时间点 + std::this_thread::sleep_until(wakeup_time); + + // 输出等待结束后的时间 + now = std::chrono::system_clock::now(); + now_time = std::chrono::system_clock::to_time_t(now); + std::cout << "Time after waiting:\t" << std::put_time(std::localtime(&now_time), "%H:%M:%S") << std::endl; +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/07-thread\345\257\271\350\261\241\350\275\254\347\247\273\346\211\200\346\234\211\346\235\203.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/07-thread\345\257\271\350\261\241\350\275\254\347\247\273\346\211\200\346\234\211\346\235\203.cpp" new file mode 100644 index 00000000..3d7d267a --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/07-thread\345\257\271\350\261\241\350\275\254\347\247\273\346\211\200\346\234\211\346\235\203.cpp" @@ -0,0 +1,18 @@ +#include +#include + +// https://github.com/Mq-b/Loser-HomeWork/discussions/206 + +// 反直觉 +// 形参、实参 +// 函数调用传参,实际上是初始化了(构造)形参的对象 + +void f(std::thread t) { + t.join(); +} + +int main() { + std::thread t{ [] {} }; + f(std::move(t)); + f(std::thread{ [] {} }); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/08-thread\346\236\204\351\200\240\346\272\220\347\240\201\350\247\243\346\236\220.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/08-thread\346\236\204\351\200\240\346\272\220\347\240\201\350\247\243\346\236\220.cpp" new file mode 100644 index 00000000..691290ef --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/08-thread\346\236\204\351\200\240\346\272\220\347\240\201\350\247\243\346\236\220.cpp" @@ -0,0 +1,16 @@ +#include +#include +#include + +struct X { + X(X&& x)noexcept {} + template , X>, int> = 0> + X(Fn&& f, Args&&...args) {} + X(const X&) = delete; +}; + +int main(){ + std::thread + X x{ [] {} }; + X x2{ x }; // 选择到了有参构造函数,不导致编译错误 +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/09-\345\256\236\347\216\260joining_thread.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/09-\345\256\236\347\216\260joining_thread.cpp" new file mode 100644 index 00000000..ec488654 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/09-\345\256\236\347\216\260joining_thread.cpp" @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +class joining_thread { + std::thread t; +public: + joining_thread()noexcept = default; + + template + explicit joining_thread(Callable&& func, Args&&...args) : + t{ std::forward(func), std::forward(args)... } {} + + explicit joining_thread(std::thread t_)noexcept : t{ std::move(t_) } {} + + joining_thread(joining_thread&& other)noexcept : t{ std::move(other.t) } {} + + joining_thread& operator=(std::thread&& other)noexcept { + if (joinable()) { // 如果当前有活跃线程(判断当前对象是否持有资源),那就先执行完(先释放) + join(); // 就相当于释放资源一样的意思 + } + t = std::move(other); + return *this; + } + ~joining_thread() { + if (joinable()) { + join(); + } + } + void swap(joining_thread& other)noexcept { + t.swap(other.t); + } + std::thread::id get_id()const noexcept { + return t.get_id(); + } + bool joinable()const noexcept { + return t.joinable(); + } + void join() { + t.join(); + } + void detach() { + t.detach(); + } + std::thread& data()noexcept { + return t; + } + const std::thread& data()const noexcept { + return t; + } +}; + +int main(){ + auto func = []{ + std::cout << std::this_thread::get_id() << '\n'; + }; + + std::vector vec; + + for (int i = 0; i < 10; ++i){ + vec.emplace_back(func); + } + + for(auto& thread : vec){ + thread.join(); + } +} \ No newline at end of file diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/10-C++20jthread.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/10-C++20jthread.cpp new file mode 100644 index 00000000..308b281f --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/10-C++20jthread.cpp @@ -0,0 +1,20 @@ +#include +#include + +using namespace std::literals::chrono_literals; + +void f(std::stop_token stop_token, int value) { + while (!stop_token.stop_requested()) { // 检查是否已经收到停止请求 + std::cout << value++ << ' ' << std::flush; + std::this_thread::sleep_for(200ms); + } + std::cout << std::endl; +} + +int main() { + std::jthread thread{ f, 1 }; // 打印 1..15 大约 3 秒 + std::this_thread::sleep_for(3s); + thread.request_stop(); // 发送信息,线程终止 + std::cout << "乐\n"; + // jthread 的析构函数调用 request_stop() 和 join()。 +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/11-\346\225\260\346\215\256\347\253\236\344\272\211.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/11-\346\225\260\346\215\256\347\253\236\344\272\211.cpp" new file mode 100644 index 00000000..5607d6c6 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/11-\346\225\260\346\215\256\347\253\236\344\272\211.cpp" @@ -0,0 +1,19 @@ +#include +#include +#include + +std::vector v; + +int n = 1; + +int main() { + int cnt = 0; + auto f = [&] { cnt++; }; + std::thread t1{ f }, t2{ f }, t3{ f }; // ub 未定义行为 + t1.join(); + t2.join(); + t3.join(); + std::cout << cnt << '\n'; +} +// 数据竞争它是未定义行为,但是 C++ 的编译器,它会假设你的程序(假设程序是对的,代码是对的)没有任何的未定义行为再去进行优化 +// 输出 n,优化,直接缓存这个值 \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/12-\344\275\277\347\224\250\344\272\222\346\226\245\351\207\217.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/12-\344\275\277\347\224\250\344\272\222\346\226\245\351\207\217.cpp" new file mode 100644 index 00000000..534d2410 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/12-\344\275\277\347\224\250\344\272\222\346\226\245\351\207\217.cpp" @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include +#include + +std::mutex m; + +// 写 +void add_to_list(int n, std::list& list) { + std::vector numbers(n + 1); + std::iota(numbers.begin(), numbers.end(), 0); + int sum = std::accumulate(numbers.begin(), numbers.end(), 0); + + { + std::scoped_lock lc{ m }; + list.push_back(sum); + } +} + +// 读 +void print_list(const std::list& list) { + std::scoped_lock lc{ m }; + for (const auto& i : list) { + std::cout << i << ' '; + } + std::cout << '\n'; +} + +int main(){ + std::list list; + std::thread t1{ add_to_list,10,std::ref(list) }; + std::thread t2{ add_to_list,10,std::ref(list) }; + std::thread t3{ print_list,std::cref(list) }; + std::thread t4{ print_list,std::cref(list) }; + t1.join(); + t2.join(); + t3.join(); + t4.join(); + std::cout << "---------------------\n"; + print_list(list); +} \ No newline at end of file diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/13-try_lock.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/13-try_lock.cpp new file mode 100644 index 00000000..fcbbf365 --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/13-try_lock.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include +using namespace std::string_literals; + +std::mutex mtx; + +void thread_function(int id) { + // 尝试加锁 + if (mtx.try_lock()) { + std::string s = "线程:"s + std::to_string(id) + " 获得锁"s + "\n"; + std::string s2 = "线程:"s + std::to_string(id) + " 释放锁"s + "\n"; + std::cout << s; + // 临界区代码 + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟临界区操作 + mtx.unlock(); // 解锁 + std::cout << s2; + } + else { + std::string s = "线程:"s + std::to_string(id) + " 获取锁失败 处理步骤"s + "\n"; + std::cout << s; + } +} + +int main(){ + std::thread t1(thread_function, 1); + std::thread t2(thread_function, 2); + + t1.join(); + t2.join(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/14-\344\277\235\346\212\244\345\205\261\344\272\253\346\225\260\346\215\256.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/14-\344\277\235\346\212\244\345\205\261\344\272\253\346\225\260\346\215\256.cpp" new file mode 100644 index 00000000..d93d694d --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/14-\344\277\235\346\212\244\345\205\261\344\272\253\346\225\260\346\215\256.cpp" @@ -0,0 +1,40 @@ +#include +#include +#include + +class Data { + int a{}; + std::string b{}; +public: + void do_something() { + // 修改数据成员等... + } +}; + +class Data_wrapper { + Data data; + std::mutex m; +public: + template + void process_data(Func func) { + std::lock_guard lc{ m }; + func(data); // 受保护数据传递给函数 + } +}; + +Data* p = nullptr; + +void malicious_function(Data& protected_data) { + p = &protected_data; // 受保护的数据被传递到外部 +} + +Data_wrapper d; + +void foo() { + d.process_data(malicious_function); // 传递了一个恶意的函数 + p->do_something(); // 在无保护的情况下访问保护数据 +} + +int main(){ + +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/15-\346\255\273\351\224\201\357\274\232\351\227\256\351\242\230\344\270\216\350\247\243\345\206\263.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/15-\346\255\273\351\224\201\357\274\232\351\227\256\351\242\230\344\270\216\350\247\243\345\206\263.cpp" new file mode 100644 index 00000000..dd22b6e2 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/15-\346\255\273\351\224\201\357\274\232\351\227\256\351\242\230\344\270\216\350\247\243\345\206\263.cpp" @@ -0,0 +1,28 @@ +#include +#include +#include +#include +using namespace std::chrono_literals; + +struct X { + X(const std::string& str) :object{ str } {} + + friend void swap(X& lhs, X& rhs); +private: + std::string object; + std::mutex m; +}; + +void swap(X& lhs, X& rhs) { + if (&lhs == &rhs) return; + std::scoped_lock guard{ lhs.m,rhs.m }; + swap(lhs.object, rhs.object); +} + +int main(){ + X a{ "🤣" }, b{ "😅" }; + std::thread t{ [&] {swap(a, b); } }; // 1 + std::thread t2{ [&] {swap(b, a); } }; // 2 + t.join(); + t2.join(); +} \ No newline at end of file diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/16-unique_lock.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/16-unique_lock.cpp new file mode 100644 index 00000000..74e32db7 --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/16-unique_lock.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include +using namespace std::chrono_literals; + +struct X { + X(const std::string& str) :object{ str } {} + + friend void swap(X& lhs, X& rhs); +private: + std::string object; + std::mutex m; +}; + +void swap(X& lhs, X& rhs) { + if (&lhs == &rhs) return; + std::lock(rhs.m, lhs.m); + + std::unique_lock lock1{ lhs.m, std::adopt_lock }; + std::unique_lock lock2{ rhs.m, std::adopt_lock }; + // std::lock(lock1, lock2); + swap(lhs.object, rhs.object); +} + +int main() { + X a{ "🤣" }, b{ "😅" }; + std::thread t{ [&] {swap(a, b); } }; // 1 + std::thread t2{ [&] {swap(b, a); } }; // 2 + t.join(); + t2.join(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/17-\345\234\250\344\270\215\345\220\214\344\275\234\347\224\250\345\237\237\344\274\240\351\200\222\344\272\222\346\226\245\351\207\217.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/17-\345\234\250\344\270\215\345\220\214\344\275\234\347\224\250\345\237\237\344\274\240\351\200\222\344\272\222\346\226\245\351\207\217.cpp" new file mode 100644 index 00000000..dbe08886 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/17-\345\234\250\344\270\215\345\220\214\344\275\234\347\224\250\345\237\237\344\274\240\351\200\222\344\272\222\346\226\245\351\207\217.cpp" @@ -0,0 +1,19 @@ +#include +#include +#include +#include +#include + +std::unique_lock get_lock() { + extern std::mutex some_mutex; + std::unique_lock lk{ some_mutex }; + return lk; // 选择到 unique_lock 的移动构造,转移所有权 +} +void process_data() { + std::unique_lock lk{ get_lock() }; // 转移到了主函数的 lk 中 + // 执行一些任务... +}// 最后才会 unlock 解锁 + +int main(){ + process_data(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/18-\344\277\235\346\212\244\345\205\261\344\272\253\346\225\260\346\215\256\347\232\204\345\210\235\345\247\213\345\214\226\350\277\207\347\250\213.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/18-\344\277\235\346\212\244\345\205\261\344\272\253\346\225\260\346\215\256\347\232\204\345\210\235\345\247\213\345\214\226\350\277\207\347\250\213.cpp" new file mode 100644 index 00000000..8b19028c --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/18-\344\277\235\346\212\244\345\205\261\344\272\253\346\225\260\346\215\256\347\232\204\345\210\235\345\247\213\345\214\226\350\277\207\347\250\213.cpp" @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include + +struct some{ + void do_something(){} +}; + +std::shared_ptr ptr; +std::once_flag resource_flag; + +void init_resource() { + ptr.reset(new some); +} + +void foo() { + std::call_once(resource_flag, []{ptr.reset(new some); }); // 线程安全的一次初始化 + ptr->do_something(); +} + +void test(){ + std::call_once(resource_flag, [] {std::cout << "f init\n"; }); +} + +std::once_flag flag; +int n = 0; + +void f() { + std::call_once(flag, [] { + ++n; + std::cout << "第 " << n << " 次调用\n"; + throw std::runtime_error("异常"); + }); +} + +class my_class{}; + +inline my_class& get_my_class_instance() { + static my_class instance; // 线程安全的初始化过程 初始化严格发生一次 + return instance; +} + +int main() { + get_my_class_instance(); + get_my_class_instance(); + get_my_class_instance(); + get_my_class_instance(); + get_my_class_instance(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/19\344\277\235\346\212\244\344\270\215\345\270\270\346\233\264\346\226\260\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/19\344\277\235\346\212\244\344\270\215\345\270\270\346\233\264\346\226\260\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204.cpp" new file mode 100644 index 00000000..135048b8 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/19\344\277\235\346\212\244\344\270\215\345\270\270\346\233\264\346\226\260\347\232\204\346\225\260\346\215\256\347\273\223\346\236\204.cpp" @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include + +class Settings { +private: + std::map data_; + mutable std::shared_timed_mutex mutex_; // “M&M 规则”:mutable 与 mutex 一起出现 + +public: + void set(const std::string& key, const std::string& value) { + std::lock_guard lock{ mutex_ }; + data_[key] = value; + } + + std::string get(const std::string& key) const { + std::shared_lock lock(mutex_); + auto it = data_.find(key); + return (it != data_.end()) ? it->second : ""; // 如果没有找到键返回空字符串 + } +}; + +Settings set; + +void read(){ + (void)set.get("1"); +} + +void write(){ + set.set("1", "a"); +} + +int main(){ + std::thread t{ read }; + std::thread t2{ read }; + std::thread t3{ read }; + std::thread t4{ read }; + std::thread t5{ write }; + t.join(); + t2.join(); + t3.join(); + t4.join(); + t5.join(); +} \ No newline at end of file diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/20recursive_mutex.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/20recursive_mutex.cpp new file mode 100644 index 00000000..aa01fd21 --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/20recursive_mutex.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +std::recursive_mutex mtx; + +void recursive_function(int count) { + std::lock_guard lc{ mtx }; + std::cout << "Locked by thread: " << std::this_thread::get_id() << ", count: " << count << std::endl; + if (count > 0) { + recursive_function(count - 1); + } +} + +int main() { + std::thread t1(recursive_function, 3); + std::thread t2(recursive_function, 2); + + t1.join(); + t2.join(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/21new\345\222\214delete\346\230\257\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\220\227\357\274\237.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/21new\345\222\214delete\346\230\257\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\220\227\357\274\237.cpp" new file mode 100644 index 00000000..102a0295 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/21new\345\222\214delete\346\230\257\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\220\227\357\274\237.cpp" @@ -0,0 +1,26 @@ +#include +#include +#include + +struct X { + X(int v) { // 主要防止有人认为构造函数、析构函数啊,是线程安全的 + std::cout << v << " 🤣\n"; + } +}; + +void f() { + X* p = new X{ 1 }; // 存在数据竞争 + delete p; +} + +int main() +{ + for (int i = 0; i < 10; ++i) { + std::thread t{ f }; + std::thread t2{ f }; + t.join(); + t2.join(); + } + + // C++ 保证的是内存的申请和释放 这种全局状态 是线程安全的 +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/22\347\272\277\347\250\213\345\255\230\345\202\250\346\234\237.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/22\347\272\277\347\250\213\345\255\230\345\202\250\346\234\237.cpp" new file mode 100644 index 00000000..61a6fc4e --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/22\347\272\277\347\250\213\345\255\230\345\202\250\346\234\237.cpp" @@ -0,0 +1,15 @@ +#include +#include + +int global_counter = 0; +__declspec(thread) int thread_local_counter = 0; + +void print_counters() { + std::cout << "global:" << global_counter++ << '\n'; + std::cout << "thread_local:" << thread_local_counter++ << '\n'; +} + +int main() { + std::thread{ print_counters }.join(); + std::thread{ print_counters }.join(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/23\345\261\200\351\203\250\343\200\201\345\205\250\345\261\200\343\200\201\347\272\277\347\250\213\343\200\201CPU\345\217\230\351\207\217\347\232\204\345\257\271\346\257\224\344\270\216\344\275\277\347\224\250.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/23\345\261\200\351\203\250\343\200\201\345\205\250\345\261\200\343\200\201\347\272\277\347\250\213\343\200\201CPU\345\217\230\351\207\217\347\232\204\345\257\271\346\257\224\344\270\216\344\275\277\347\224\250.cpp" new file mode 100644 index 00000000..ce460983 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/23\345\261\200\351\203\250\343\200\201\345\205\250\345\261\200\343\200\201\347\272\277\347\250\213\343\200\201CPU\345\217\230\351\207\217\347\232\204\345\257\271\346\257\224\344\270\216\344\275\277\347\224\250.cpp" @@ -0,0 +1,27 @@ +#include +#include + +thread_local int n = (std::puts("thread_local init"), 0); + +void f(){ + (void)n; // 防止 gcc 与 clang 优化 + std::puts("f"); +} + +void f2(){ + thread_local static int n = (std::puts("f2 init"), 0); +} + +int main(){ + (void)n; // 防止 gcc 与 clang 优化 + std::cout << "main\n"; + std::thread{ f }.join(); + f2(); + f2(); + f2(); +} + +// gcc 与 clang 存在优化,会出现与 msvc 不同的结果,它们直接将线程变量优化掉了 +// 这应该视作 bug。 +// 视频中想到 std::async 是下一章的内容跳过了(想到的是 msvc 的一个问题),忘记了 gcc 与 clang 此处也存在问题。 +// https://godbolt.org/z/qa6YfMqP7 \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/24\347\255\211\345\276\205\344\272\213\344\273\266\346\210\226\346\235\241\344\273\266.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/24\347\255\211\345\276\205\344\272\213\344\273\266\346\210\226\346\235\241\344\273\266.cpp" new file mode 100644 index 00000000..7c8718bd --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/24\347\255\211\345\276\205\344\272\213\344\273\266\346\210\226\346\235\241\344\273\266.cpp" @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +using namespace std::chrono_literals; + +std::mutex mtx; // 互斥量 +std::condition_variable_any cv; // 条件变量 +bool arrived = false; + +void wait_for_arrival() { + std::unique_lock lck(mtx); // 上锁 + cv.wait(lck, [] { return arrived; }); // 等待 arrived 变为 true 会解锁的 再次上锁 + std::cout << "到达目的地,可以下车了!" << std::endl; +} + +void simulate_arrival() { + std::this_thread::sleep_for(std::chrono::seconds(5)); // 模拟地铁到站,假设5秒后到达目的地 + { + std::lock_guard lck(mtx); + arrived = true; // 设置条件变量为 true,表示到达目的地 + } + cv.notify_one(); // 通知等待的线程 +} + +int main(){ + std::thread t{ wait_for_arrival }; + std::thread t2{ simulate_arrival }; + t.join(); + t2.join(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/25\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\351\230\237\345\210\227.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/25\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\351\230\237\345\210\227.cpp" new file mode 100644 index 00000000..df90476c --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/25\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\351\230\237\345\210\227.cpp" @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +using namespace std::chrono_literals; + +template +class threadsafe_queue { + mutable std::mutex m; // M&M 原则 互斥量,用于保护队列操作的独占访问 + std::condition_variable data_cond; // 条件变量,用于在队列为空时等待 + std::queue data_queue; // 实际存储数据的队列 +public: + threadsafe_queue() {} + + void push(T new_value) { + { + std::lock_guard lk{ m }; + std::cout << "push:" << new_value << std::endl; + data_queue.push(new_value); + } + data_cond.notify_one(); + } + // 从队列中弹出元素(阻塞直到队列不为空) + void pop(T& value) { + std::unique_lock lk{ m }; + data_cond.wait(lk, [this] {return !data_queue.empty(); }); // 解除阻塞 重新获取锁 lock + value = data_queue.front(); + std::cout << "pop:" << value << std::endl; + data_queue.pop(); + } + // 从队列中弹出元素(阻塞直到队列不为空),并返回一个指向弹出元素的 shared_ptr + std::shared_ptr pop() { + std::unique_lock lk{ m }; + data_cond.wait(lk, [this] {return !data_queue.empty(); }); + std::shared_ptrres{ std::make_shared(data_queue.front()) }; + data_queue.pop(); + return res; + } + bool empty()const { + std::lock_guard lk(m); + return data_queue.empty(); + } +}; + +void producer(threadsafe_queue& q) { + for (int i = 0; i < 5; ++i) { + q.push(i); + } +} +void consumer(threadsafe_queue& q) { + for (int i = 0; i < 5; ++i) { + int value{}; + q.pop(value); + } +} + +int main() { + threadsafe_queue q; + + std::thread producer_thread(producer, std::ref(q)); + std::thread consumer_thread(consumer, std::ref(q)); + + producer_thread.join(); + consumer_thread.join(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/26\344\275\277\347\224\250\346\235\241\344\273\266\345\217\230\351\207\217\345\256\236\347\216\260\345\220\216\345\217\260\346\217\220\347\244\272\351\237\263\346\222\255\346\224\276.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/26\344\275\277\347\224\250\346\235\241\344\273\266\345\217\230\351\207\217\345\256\236\347\216\260\345\220\216\345\217\260\346\217\220\347\244\272\351\237\263\346\222\255\346\224\276.cpp" new file mode 100644 index 00000000..88e4ca18 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/26\344\275\277\347\224\250\346\235\241\344\273\266\345\217\230\351\207\217\345\256\236\347\216\260\345\220\216\345\217\260\346\217\220\347\244\272\351\237\263\346\222\255\346\224\276.cpp" @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std::chrono_literals; + +class AudioPlayer{ +public: + AudioPlayer() : stop {false}, player_thread{ &AudioPlayer::playMusic, this } + {} + + ~AudioPlayer(){ + while (!audio_queue.empty()){ + std::this_thread::sleep_for(50ms); + } + stop = true; + cond.notify_all(); + if(player_thread.joinable()){ + player_thread.join(); + } + } + + void addAudioPath(const std::string& path){ + std::lock_guard lc{ m }; + audio_queue.push(path); + cond.notify_one(); + } + +private: + void playMusic(){ + while(!stop){ + std::string path; + { + std::unique_lock lock{ m }; + // 条件不满足,就解锁 unlock,让其它线程得以运行 如果被唤醒了,就会重新获取锁 lock + cond.wait(lock, [this] {return !audio_queue.empty() || stop; }); + + if (audio_queue.empty()) return; // 防止对象为空时出问题 + + path = audio_queue.front(); // 取出 + audio_queue.pop(); // 取出后就删除这个元素,表示此元素以及被使用 + } + + if(!music.openFromFile(path)){ + std::cerr << "无法加载音频文件: " << path << std::endl; + continue; + } + + music.play(); // 异步 非阻塞 + + while(music.getStatus() == sf::SoundSource::Playing){ + sf::sleep(sf::seconds(0.1f)); // sleep 避免忙等 占用 CPU + } + } + } + + std::atomic stop; // 控制线程的停止与退出 + std::thread player_thread; // 后台执行音频播放任务的专用线程 + std::mutex m; // 保护共享资源 + std::condition_variable cond; // 控制线程的等待和唤醒,当有新的任务的时候通知播放线程 + std::queue audio_queue; // 音频任务队列,存储待播放的音频文件的路径 + sf::Music music; // SFML 音频播放器对象,用来加载播放音频 + +public: + static constexpr std::array soundResources{ + "./sound/01初始化失败.ogg", + "./sound/02初始化成功.ogg", + "./sound/03试剂不足,请添加.ogg", + "./sound/04试剂已失效,请更新.ogg", + "./sound/05清洗液不足,请添加.ogg", + "./sound/06废液桶即将装满,请及时清空.ogg", + "./sound/07废料箱即将装满,请及时清空.ogg", + "./sound/08激发液A液不足,请添加.ogg", + "./sound/09激发液B液不足,请添加.ogg", + "./sound/10反应杯不足,请添加.ogg", + "./sound/11检测全部完成.ogg" + }; +}; + +AudioPlayer audioPlayer; + +int main() { + audioPlayer.addAudioPath(AudioPlayer::soundResources[4]); + audioPlayer.addAudioPath(AudioPlayer::soundResources[5]); + audioPlayer.addAudioPath(AudioPlayer::soundResources[6]); + audioPlayer.addAudioPath(AudioPlayer::soundResources[7]); + + std::thread t{ [] { + std::this_thread::sleep_for(1s); + audioPlayer.addAudioPath(AudioPlayer::soundResources[1]); + } }; + std::thread t2{ [] { + audioPlayer.addAudioPath(AudioPlayer::soundResources[0]); + } }; + + std::cout << "乐\n"; + + t.join(); + t2.join(); + + std::cout << "end\n"; +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/27\345\210\233\345\273\272\345\274\202\346\255\245\344\273\273\345\212\241\350\216\267\345\217\226\350\277\224\345\233\236\345\200\274.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/27\345\210\233\345\273\272\345\274\202\346\255\245\344\273\273\345\212\241\350\216\267\345\217\226\350\277\224\345\233\236\345\200\274.cpp" new file mode 100644 index 00000000..cb33de38 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/27\345\210\233\345\273\272\345\274\202\346\255\245\344\273\273\345\212\241\350\216\267\345\217\226\350\277\224\345\233\236\345\200\274.cpp" @@ -0,0 +1,13 @@ +#include +#include +#include // 引入 future 头文件 + +void f() { + std::cout << std::this_thread::get_id() << '\n'; +} + +int main() { + auto t = std::async([] {}); + std::future future{ std::move(t) }; + future.wait(); // Error! 抛出异常 +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/28future\344\270\216 packaged_task.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/28future\344\270\216 packaged_task.cpp" new file mode 100644 index 00000000..abfa730a --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/28future\344\270\216 packaged_task.cpp" @@ -0,0 +1,46 @@ +#include +#include +#include + +template +void async_task(std::packaged_task& task, Args&&...args) { + // todo.. + task(std::forward(args)...); +} + +int main() { + std::packaged_task task([](int a, int b) { + return a + b; + }); + + int value = 50; + + std::future future = task.get_future(); + + // 创建一个线程来执行异步任务 + std::thread t{ [&] { async_task(task, value, value); } }; + std::cout << future.get() << '\n'; + t.join(); +} + +//int main(){ +// std::cout << "main: " << std::this_thread::get_id() << '\n'; +// +// // 只能移动不能复制 +// std::packaged_task task{ [](int a, int b) { +// std::cout << "packaged_task: " << std::this_thread::get_id() << '\n'; +// return std::pow(a, b); +// } }; +// +// std::future future = task.get_future(); +// +// // task(10, 2); // 调用 此处执行任务 +// +// std::thread t{ std::move(task) ,10,2 }; +// +// std::cout << "------\n"; +// +// std::cout << future.get() << '\n'; // 会阻塞,直到任务执行完毕 +// +// t.join(); +//} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/29\344\275\277\347\224\250promise.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/29\344\275\277\347\224\250promise.cpp" new file mode 100644 index 00000000..c37e3070 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/29\344\275\277\347\224\250promise.cpp" @@ -0,0 +1,55 @@ +#include +#include +#include +#include +using namespace std::chrono_literals; + +void f(std::promise obj ,int num){ + // todo.. + obj.set_value(num * num); // 调用了 set_value + // todo.. + std::this_thread::sleep_for(5s); // 模拟一些计算 +} + +void throw_function(std::promise prom) { + prom.set_value(100); + try { + // todo.. + throw std::runtime_error("一个异常"); + } + catch (...) { + try { + // 共享状态的 promise 已存储值,调用 set_exception 产生异常 + prom.set_exception(std::current_exception()); + } + catch (std::exception& e) { + std::cerr << "来自 set_exception 的异常: " << e.what() << '\n'; + } + } +} + +int main() { + std::promise prom; + std::future fut = prom.get_future(); + + std::thread t(throw_function, std::move(prom)); + + std::cout << "等待线程执行,抛出异常并设置\n"; + std::cout << "值:" << fut.get() << '\n'; // 100 + + t.join(); +} + + +//int main(){ +// std::promise promise; +// +// auto future = promise.get_future(); // 关联了 +// +// std::thread t{ f,std::move(promise), 10 }; +// // f(std::move(promise), 10); +// +// std::cout << future.get() << '\n'; // 阻塞,直至结果可用 +// std::cout << "end\n"; +// t.join(); +//} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/30future\347\232\204\347\212\266\346\200\201\345\217\230\345\214\226.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/30future\347\232\204\347\212\266\346\200\201\345\217\230\345\214\226.cpp" new file mode 100644 index 00000000..b6cb59b6 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/30future\347\232\204\347\212\266\346\200\201\345\217\230\345\214\226.cpp" @@ -0,0 +1,16 @@ +#include +#include +#include + +int main(){ + std::futurefuture = std::async([] {}); + std::cout << std::boolalpha << future.valid() << '\n'; // true + future.get(); + std::cout << std::boolalpha << future.valid() << '\n'; // false + try { + future.get(); // 抛出 future_errc::no_state 异常 + } + catch (std::exception& e) { + std::cerr << e.what() << '\n'; + } +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/31\345\244\232\344\270\252\347\272\277\347\250\213\347\232\204\347\255\211\345\276\205shared_future.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/31\345\244\232\344\270\252\347\272\277\347\250\213\347\232\204\347\255\211\345\276\205shared_future.cpp" new file mode 100644 index 00000000..7ada4b4a --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/31\345\244\232\344\270\252\347\272\277\347\250\213\347\232\204\347\255\211\345\276\205shared_future.cpp" @@ -0,0 +1,37 @@ +#include +#include +#include + +std::string fetch_data() { + std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作 + return "从网络获取的数据!"; +} + +int main() { + std::future future_data = std::async(std::launch::async, fetch_data); + + // // 转移共享状态,原来的 future 被清空 valid() == false + std::shared_future shared_future_data = future_data.share(); + + // 多个线程持有一个 shared_future 对象并操作 + + // 第一个线程等待结果并访问数据 + std::thread thread1([shared_future_data] { + std::cout << "线程1:等待数据中..." << std::endl; + shared_future_data.wait(); // 等待结果可用 + std::cout << "线程1:收到数据:" << shared_future_data.get() << std::endl; + }); + + // 第二个线程等待结果并访问数据 + std::thread thread2([shared_future_data] { + std::cout << "线程2:等待数据中..." << std::endl; + shared_future_data.wait(); + std::cout << "线程2:收到数据:" << shared_future_data.get() << std::endl; + }); + + thread1.join(); + thread2.join(); + + std::promise p; + std::shared_future sf{ p.get_future() }; // 隐式转移所有权 +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/32\351\231\220\346\227\266\347\255\211\345\276\205-\346\227\266\351\222\237.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/32\351\231\220\346\227\266\347\255\211\345\276\205-\346\227\266\351\222\237.cpp" new file mode 100644 index 00000000..d5a07cdc --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/32\351\231\220\346\227\266\347\255\211\345\276\205-\346\227\266\351\222\237.cpp" @@ -0,0 +1,14 @@ +#include +#include +#include +using namespace std::chrono_literals; + +int main(){ + auto now = std::chrono::system_clock::now(); + time_t now_time = std::chrono::system_clock::to_time_t(now); + std::cout << "Current time:\t" << std::put_time(std::localtime(&now_time), "%H:%M:%S\n"); + + auto now2 = std::chrono::steady_clock::now(); + now_time = std::chrono::system_clock::to_time_t(now); + std::cout << "Current time:\t" << std::put_time(std::localtime(&now_time), "%H:%M:%S\n"); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/33\351\231\220\346\227\266\347\255\211\345\276\205-\346\227\266\351\227\264\346\256\265.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/33\351\231\220\346\227\266\347\255\211\345\276\205-\346\227\266\351\227\264\346\256\265.cpp" new file mode 100644 index 00000000..550c88cc --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/33\351\231\220\346\227\266\347\255\211\345\276\205-\346\227\266\351\227\264\346\256\265.cpp" @@ -0,0 +1,17 @@ +#include +#include +#include +#include +using namespace std::chrono_literals; + +int main(){ + using namespace std::chrono; + auto future = std::async(std::launch::deferred, []{ + std::cout << "deferred\n"; + }); + + if (future.wait_for(35ms) == std::future_status::deferred) + std::cout << "future_status::deferred " << "正在延迟执行\n"; + + future.wait(); // 在 wait() 或 get() 调用时执行,不创建线程 +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/34\351\231\220\346\227\266\347\255\211\345\276\205-\346\227\266\351\227\264\347\202\271.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/34\351\231\220\346\227\266\347\255\211\345\276\205-\346\227\266\351\227\264\347\202\271.cpp" new file mode 100644 index 00000000..de346347 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/34\351\231\220\346\227\266\347\255\211\345\276\205-\346\227\266\351\227\264\347\202\271.cpp" @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include +#include +#pragma comment(lib,"winmm.lib") + +using namespace std::chrono_literals; + +std::condition_variable cv; +bool done{}; +std::mutex m; + +bool wait_loop() { + const auto timeout = std::chrono::steady_clock::now() + 500ms; + std::unique_lock lk{ m }; + if (!cv.wait_until(lk, timeout, [] {return done; })) { + std::cout << "超时 500ms\n"; + return false; + } + return true; +} + +int main() { + std::thread t{ wait_loop }; + std::this_thread::sleep_for(400ms); + done = true; + cv.notify_one(); + t.join(); +} diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/35C++20\344\277\241\345\217\267\351\207\217.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/35C++20\344\277\241\345\217\267\351\207\217.cpp" new file mode 100644 index 00000000..36629670 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/35C++20\344\277\241\345\217\267\351\207\217.cpp" @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include +using namespace std::chrono_literals; + +// 定义一个信号量,最大并发数为 3 +std::counting_semaphore<3> semaphore{ 3 }; + +void handle_request(int request_id) { + // 请求到达,尝试获取信号量 + std::cout << "进入 handle_request 尝试获取信号量\n"; + + semaphore.acquire(); + + std::cout << "成功获取信号量\n"; + + // 此处延时三秒可以方便测试,会看到先输出 3 个“成功获取信号量”,因为只有三个线程能成功调用 acquire,剩余的会被阻塞 + std::this_thread::sleep_for(3s); + + // 模拟处理时间 + std::random_device rd; + std::mt19937 gen{ rd() }; + std::uniform_int_distribution<> dis(1, 5); + int processing_time = dis(gen); + std::this_thread::sleep_for(std::chrono::seconds(processing_time)); + + std::cout << std::format("请求 {} 已被处理\n", request_id); + + semaphore.release(); +} + +int main() { + // 模拟 10 个并发请求 + std::vector threads; + for (int i = 0; i < 10; ++i) { + threads.emplace_back(handle_request, i); + } +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/36C++20\351\227\251latch.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/36C++20\351\227\251latch.cpp" new file mode 100644 index 00000000..e7ef4efe --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/36C++20\351\227\251latch.cpp" @@ -0,0 +1,21 @@ +#include +#include +#include +#include +using namespace std::chrono_literals; + +std::latch latch{ 10 }; + +void f(int id) { + //todo.. 脑补任务 + std::cout << std::format("线程 {} 执行完任务,开始等待其它线程执行到此处\n", id); + latch.arrive_and_wait(); // 减少 并等待 count_down(1); wait(); 等待计数为 0 + std::cout << std::format("线程 {} 彻底退出函数\n", id); +} + +int main() { + std::vector threads; + for (int i = 0; i < 10; ++i) { + threads.emplace_back(f, i); + } +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/37C++20\345\261\217\351\232\234barrier.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/37C++20\345\261\217\351\232\234barrier.cpp" new file mode 100644 index 00000000..d8e3e1a7 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/37C++20\345\261\217\351\232\234barrier.cpp" @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +void f(int start, int end, int thread_id) { + for (int i = start; i <= end; ++i) { + // 输出当前线程的数字 + std::cout << std::to_string(i) + " "; + + // 等待所有线程同步到达 barrier 也就是等待都输出完数字 +#pragma omp barrier + +// 每个线程输出完一句后,主线程输出轮次信息 +#pragma omp master + { + static int round_number = 1; + std::cout << "\t第" << round_number++ << "轮结束\n"; + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + + // 再次同步 等待所有线程(包括主线程)到达此处、避免其它线程继续执行打断主线程的输出 +#pragma omp barrier + } +} + +int main() { + constexpr int num_threads = 10; + omp_set_num_threads(num_threads); + +#pragma omp parallel + { + const int thread_id = omp_get_thread_num(); + f(thread_id * 10 + 1, (thread_id + 1) * 10, thread_id); + } + +} + +// https://godbolt.org/z/fabqhbx3P \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/38\347\254\254\345\233\233\347\253\240\346\200\273\347\273\223-\345\213\230\350\257\257\345\210\235\345\247\213\345\214\226\351\241\272\345\272\217.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/38\347\254\254\345\233\233\347\253\240\346\200\273\347\273\223-\345\213\230\350\257\257\345\210\235\345\247\213\345\214\226\351\241\272\345\272\217.cpp" new file mode 100644 index 00000000..6c86778a --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/38\347\254\254\345\233\233\347\253\240\346\200\273\347\273\223-\345\213\230\350\257\257\345\210\235\345\247\213\345\214\226\351\241\272\345\272\217.cpp" @@ -0,0 +1,41 @@ +#include +#include +#include + +struct X { + X() { + // 假设 X 的初始化没那么快 + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::puts("X"); + v.resize(10, 6); + } + std::vector v; +}; + +struct Test { + Test()/* : t{ &Test::f, this }*/ // 线程已经开始执行 + { + // 严格意义来说 这里不算初始化 至少不算 C++ 标准的定义 + } + void start() + { + t = std::thread{ &Test::f, this }; + } + ~Test() { + if (t.joinable()) + t.join(); + } + void f()const { // 如果在函数执行的线程 f 中使用 x 则会存在问题。使用了未初始化的数据成员 ub + std::cout << "f\n"; + std::cout << x.v[9] << '\n'; + } + + + std::thread t; // 声明顺序决定了初始化顺序,优先初始化 t + X x; +}; + +int main() { + Test t; + t.start(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/39\345\216\237\345\255\220\347\261\273\345\236\213atomic.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/39\345\216\237\345\255\220\347\261\273\345\236\213atomic.cpp" new file mode 100644 index 00000000..91afb9f5 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/39\345\216\237\345\255\220\347\261\273\345\236\213atomic.cpp" @@ -0,0 +1,49 @@ +#include +#include +#include +#include +using namespace std::chrono_literals; + +// 不要这样使用 不要在多线程并发中使用 volatile +// 它的行为是不保证的 +std::atomic n = 0; + +void read(){ + while(true){ + std::this_thread::sleep_for(500ms); + std::cout << n.load() << '\n'; + } +} + +void write(){ + while (true){ + ++n; + } +} + +// 数据竞争 数据竞争未定义行为 +// 优化会假设你的程序中没有未定义行为 + +// C 语言的平凡的结构体 +struct trivial_type { + int x; + float y; +}; + +int main(){ + // 创建一个 std::atomic 对象 + std::atomic atomic_my_type{ { 10, 20.5f } }; + + // 使用 store 和 load 操作来设置和获取值 + trivial_type new_value{ 30, 40.5f }; + atomic_my_type.store(new_value); + + std::cout << "x: " << atomic_my_type.load().x << ", y: " << atomic_my_type.load().y << std::endl; + + // 使用 exchange 操作 + trivial_type exchanged_value = atomic_my_type.exchange({ 50, 60.5f }); + std::cout << "交换前的 x: " << exchanged_value.x + << ", 交换前的 y: " << exchanged_value.y << std::endl; + std::cout << "交换后的 x: " << atomic_my_type.load().x + << ", 交换后的 y: " << atomic_my_type.load().y << std::endl; +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/40\347\272\277\347\250\213\346\261\240\344\275\277\347\224\250.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/40\347\272\277\347\250\213\346\261\240\344\275\277\347\224\250.cpp" new file mode 100644 index 00000000..d8898ba7 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/40\347\272\277\347\250\213\346\261\240\344\275\277\347\224\250.cpp" @@ -0,0 +1,23 @@ +#include +#include +#include +#include + +int main(int argc, char* argv[]) { + QCoreApplication app(argc, argv); + + QThreadPool* threadPool = QThreadPool::globalInstance(); + + // 线程池最大线程数 + qDebug() << threadPool->maxThreadCount(); + + for (int i = 0; i < 20; ++i) { + threadPool->start([i]{ + qDebug() << QString("thread id %1").arg(i); + }); + } + // 当前活跃线程数 10 + qDebug() << threadPool->activeThreadCount(); + + app.exec(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/41\345\256\236\347\216\260\344\270\200\344\270\252\347\272\277\347\250\213\346\261\240.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/41\345\256\236\347\216\260\344\270\200\344\270\252\347\272\277\347\250\213\346\261\240.cpp" new file mode 100644 index 00000000..091ca19f --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/41\345\256\236\347\216\260\344\270\200\344\270\252\347\272\277\347\250\213\346\261\240.cpp" @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std::chrono_literals; + +inline std::size_t default_thread_pool_size() noexcept{ + std::size_t num_threads = std::thread::hardware_concurrency(); + num_threads = num_threads == 0 ? 2 : num_threads; // 防止无法检测当前硬件,让我们线程池至少有 2 个线程 + return num_threads; +} + +class ThreadPool{ +public: + using Task = std::packaged_task; + + ThreadPool(const ThreadPool&) = delete; + ThreadPool& operator=(const ThreadPool&) = delete; + + ThreadPool(std::size_t num_thread = default_thread_pool_size()) : + stop_{ false }, num_thread_{ num_thread } + { + start(); + } + ~ThreadPool(){ + stop(); + } + + void stop(){ + stop_ = true; + cv_.notify_all(); + for (auto& thread : pool_){ + if (thread.joinable()) + thread.join(); + } + pool_.clear(); + } + + template + std::future, std::decay_t...>> submit(F&& f, Args&&...args){ + using RetType = std::invoke_result_t, std::decay_t...>; + if(stop_){ + throw std::runtime_error("ThreadPool is stopped"); + } + auto task = std::make_shared>(std::bind(std::forward(f), std::forward(args)...)); + + std::future ret = task->get_future(); + + { + std::lock_guard lc{ mutex_ }; + tasks_.emplace([task] {(*task)(); }); + } + cv_.notify_one(); + + return ret; + } + + void start(){ + for (std::size_t i = 0; i < num_thread_; ++i){ + pool_.emplace_back([this]{ + while (!stop_) { + Task task; + { + std::unique_lock lock{ mutex_ }; + cv_.wait(lock, [this] {return stop_ || !tasks_.empty(); }); + if (tasks_.empty()) return; + task = std::move(tasks_.front()); + tasks_.pop(); + } + task(); + } + }); + } + } + +private: + std::mutex mutex_; + std::condition_variable cv_; + std::atomic stop_; + std::atomic num_thread_; + std::queue tasks_; + std::vector pool_; +}; + +int print_task(int n) { + std::osyncstream{ std::cout } << "Task " << n << " is running on thr: " << + std::this_thread::get_id() << '\n'; + return n; +} +int print_task2(int n) { + std::osyncstream{ std::cout } << "🐢🐢🐢 " << n << " 🐉🐉🐉" << std::endl; + return n; +} + +struct X { + void f(const int& n) const { + std::osyncstream{ std::cout } << &n << '\n'; + } +}; + +int main() { + ThreadPool pool{ 4 }; // 创建一个有 4 个线程的线程池 + + X x; + int n = 6; + std::cout << &n << '\n'; + auto t = pool.submit(&X::f, &x, n); // 默认复制,地址不同 + auto t2 = pool.submit(&X::f, &x, std::ref(n)); + t.wait(); + t2.wait(); +} // 析构自动 stop()自动 stop() \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/42atomic_flag\345\256\236\347\216\260\350\207\252\346\227\213\351\224\201.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/42atomic_flag\345\256\236\347\216\260\350\207\252\346\227\213\351\224\201.cpp" new file mode 100644 index 00000000..b6d9cca1 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/42atomic_flag\345\256\236\347\216\260\350\207\252\346\227\213\351\224\201.cpp" @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +class spinlock_mutex { + std::atomic_flag flag{}; +public: + spinlock_mutex()noexcept = default; + void lock()noexcept { + while (flag.test_and_set(std::memory_order_acquire)); + } + + void unlock()noexcept { + flag.clear(std::memory_order_release); + } +}; + +spinlock_mutex m; + +void f() { + std::lock_guard lc{ m }; + std::cout << "😅😅" << "❤️❤️\n"; +} + +int main(){ + std::thread t{ f }; + std::thread t1{ f }; + std::thread t2{ f }; + std::thread t3{ f }; + std::thread t4{ f }; + std::thread t5{ f }; + t.join(); + t1.join(); + t2.join(); + t3.join(); + t4.join(); + t5.join(); +} \ No newline at end of file diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/43atomic_bool.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/43atomic_bool.cpp new file mode 100644 index 00000000..ae7f1c94 --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/43atomic_bool.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +std::atomic flag{ false }; +bool expected = false; + +void try_set_flag() { + // 尝试将 flag 设置为 true,如果当前值为 false + if (flag.compare_exchange_strong(expected, true)) { + std::cout << "flag 为 false,flag 设为 true。\n"; + } + else { + std::cout << "flag 为 true, expected 设为 true。\n"; + } +} + +int main() { + std::thread t1{ try_set_flag }; + std::thread t2{ try_set_flag }; + t1.join(); + t2.join(); + std::cout << "flag: " << std::boolalpha << flag << '\n'; + std::cout << "expected: " << std::boolalpha << expected << '\n'; +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/44atomic\346\214\207\351\222\210\347\211\271\345\214\226.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/44atomic\346\214\207\351\222\210\347\211\271\345\214\226.cpp" new file mode 100644 index 00000000..644e566b --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/44atomic\346\214\207\351\222\210\347\211\271\345\214\226.cpp" @@ -0,0 +1,32 @@ +#include + +struct X{ + int v{}; + void f()const { + std::cout << v << '\n'; + } +}; + +int main(){ + int arr[10]{ 1,2 }; + + std::atomic p{ arr }; + + p.fetch_add(1); + std::cout << *(p.load()) << '\n'; + + p.fetch_sub(1); + std::cout << *(p.load()) << '\n'; + + p += 1; + std::cout << *(p.load()) << '\n'; + + p -= 1; + std::cout << *(p.load()) << '\n'; + + X xs[3]{ {10},{20},{30} }; + std::atomic p2{ xs }; + p2.load()->f(); + p2 += 2; + p2.load()->f(); +} \ No newline at end of file diff --git "a/code/ModernCpp-ConcurrentProgramming-Tutorial/45\345\216\237\345\255\220\347\211\271\345\214\226shared_ptr.cpp" "b/code/ModernCpp-ConcurrentProgramming-Tutorial/45\345\216\237\345\255\220\347\211\271\345\214\226shared_ptr.cpp" new file mode 100644 index 00000000..e050eda8 --- /dev/null +++ "b/code/ModernCpp-ConcurrentProgramming-Tutorial/45\345\216\237\345\255\220\347\211\271\345\214\226shared_ptr.cpp" @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +using namespace std::chrono_literals; + +class Data { +public: + Data(int value = 0) : value_(value) {} + int get_value() const { return value_; } + void set_value(int new_value) { value_ = new_value; } +private: + int value_; +}; + +std::atomic> data = std::make_shared(); + +void writer() { + for (int i = 0; i < 10; ++i) { + std::shared_ptr new_data = std::make_shared(i); + data.store(new_data); + std::this_thread::sleep_for(100ms); + } +} + +void reader() { + for (int i = 0; i < 10; ++i) { + if (auto sp = data.load()) { + std::cout << "读取线程值: " << sp->get_value() << std::endl; + } + else { + std::cout << "没有读取到数据" << std::endl; + } + std::this_thread::sleep_for(100ms); + } +} + +std::atomic> ptr = std::make_shared(); + +void wait_for_wake_up() { + std::osyncstream{ std::cout } + << "线程 " + << std::this_thread::get_id() + << " 阻塞,等待更新唤醒\n"; + + // 等待 ptr 变为其它值 + ptr.wait(ptr.load()); + + std::osyncstream{ std::cout } + << "线程 " + << std::this_thread::get_id() + << " 已被唤醒\n"; +} + +void wake_up() { + std::this_thread::sleep_for(5s); + + // 更新值并唤醒 + ptr.store(std::make_shared(10)); + ptr.notify_one(); +} + +int main() { + //std::thread writer_thread{ writer }; + //std::thread reader_thread{ reader }; + + //writer_thread.join(); + //reader_thread.join(); + + //std::atomic> ptr = std::make_shared(10); + //std::atomic_ref ref{ *ptr.load() }; + //ref = 100; // 原子地赋 100 给被引用的对象 + //std::cout << *ptr.load() << '\n'; + std::thread t1{ wait_for_wake_up }; + wake_up(); + t1.join(); +} \ No newline at end of file diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/CMakeLists.txt b/code/ModernCpp-ConcurrentProgramming-Tutorial/CMakeLists.txt new file mode 100644 index 00000000..94d9567a --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required (VERSION 3.8) + +project ("ModernCpp-ConcurrentProgramming-Tutorial") + +set(CMAKE_CXX_STANDARD 20) + +SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) + +if(MSVC) + add_compile_options("/utf-8" "/permissive-" "/Zc:nrvo" "/openmp") +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_compile_options("-finput-charset=UTF-8" "-fexec-charset=UTF-8" "-fopenmp") +endif() + +add_executable(${PROJECT_NAME} "test2.cpp") + +set(CMAKE_PREFIX_PATH "D:/lib" CACHE STRING "自定义查找包的安装路径" FORCE) + +find_package(SFML 2.6.1 COMPONENTS system window graphics audio network REQUIRED) +target_link_libraries(${PROJECT_NAME} PRIVATE sfml-system sfml-window sfml-graphics sfml-audio sfml-network) + +find_package(fmt CONFIG REQUIRED) +target_link_libraries(${PROJECT_NAME} PRIVATE fmt::fmt-header-only) + +find_package(Qt6 REQUIRED Widgets) +target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets) + +# 当前环境可以直接查找到 vcpkg 的 Boost_DIR 但是却无法查找到 include 路径,手动设置 +set(Boost_INCLUDE_DIR "D:/vcpkg-master/installed/x64-windows/include") +include_directories(${Boost_INCLUDE_DIR}) +find_package(Boost REQUIRED COMPONENTS system) +target_link_libraries(${PROJECT_NAME} PRIVATE Boost::system) + +target_include_directories(${PROJECT_NAME} PRIVATE "D:/project/cpp-terminal/include") +if(CMAKE_BUILD_TYPE STREQUAL "Release") + target_link_libraries(${PROJECT_NAME} PRIVATE + "D:/project/cpp-terminal/lib/cpp-terminal-private.lib" + "D:/project/cpp-terminal/lib/cpp-terminal.lib" + ) +else() + target_link_libraries(${PROJECT_NAME} PRIVATE + "D:/project/cpp-terminal/lib/private/debug/cpp-terminal-private.lib" + "D:/project/cpp-terminal/lib/debug/cpp-terminal.lib" + ) +endif() + +find_package(spdlog REQUIRED) +target_link_libraries(${PROJECT_NAME} PRIVATE spdlog::spdlog_header_only) diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/CMakeSettings.json b/code/ModernCpp-ConcurrentProgramming-Tutorial/CMakeSettings.json new file mode 100644 index 00000000..e254f77b --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/CMakeSettings.json @@ -0,0 +1,63 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "" + }, + { + "name": "x64-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": [ "msvc_x64_x64" ], + "variables": [] + }, + { + "name": "x86-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": [ "msvc_x86" ], + "variables": [] + }, + { + "name": "x86-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": [ "msvc_x86" ], + "variables": [] + }, + { + "name": "x64-Clang-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "inheritEnvironments": [ "clang_cl_x64_x64" ], + "variables": [] + } + ] +} \ No newline at end of file diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/Log.h b/code/ModernCpp-ConcurrentProgramming-Tutorial/Log.h new file mode 100644 index 00000000..78e152c3 --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/Log.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +inline void setupLogging() { + auto file_sink = std::make_shared("logs.txt"); + file_sink->set_level(spdlog::level::debug); + file_sink->set_pattern("[%Y-%m-%d %H:%M:%S] [%@] [%!] [thread %t] [%oms] [%l] %v"); + + auto console_sink = std::make_shared(); + console_sink->set_level(spdlog::level::debug); + console_sink->set_pattern("%^[%Y-%m-%d %H:%M:%S] [thread %t] [%oms] [%l] %v%$"); + + auto logger = std::make_shared("multi_sink", spdlog::sinks_init_list{ file_sink, console_sink }); + spdlog::register_logger(logger); + + spdlog::set_default_logger(logger); +} + +// spdlog 要想输出文件、路径、函数、行号,只能借助此宏,才会显示。 +// 其实使用 C++20 std::source_location 也能获取这些信息,后面再考虑单独封装吧,目前这样做导致没办法做格式字符串。 + +#define LOG_INFO(msg, ...) SPDLOG_LOGGER_INFO(spdlog::get("multi_sink"), msg) +#define LOG_WARN(msg, ...) SPDLOG_LOGGER_WARN(spdlog::get("multi_sink"), msg) +#define LOG_ERROR(msg, ...) SPDLOG_LOGGER_ERROR(spdlog::get("multi_sink"), msg) + +const auto init_log = (setupLogging(), 0); diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/test.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/test.cpp new file mode 100644 index 00000000..05d2784d --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/test.cpp @@ -0,0 +1,25 @@ +#include "test/AduioPlayer.h" + +AudioPlayer audioPlayer; + +int main(){ + audioPlayer.addAudioPath(AudioPlayer::soundResources[4]); + audioPlayer.addAudioPath(AudioPlayer::soundResources[5]); + audioPlayer.addAudioPath(AudioPlayer::soundResources[6]); + audioPlayer.addAudioPath(AudioPlayer::soundResources[7]); + + std::thread t{ []{ + std::this_thread::sleep_for(1s); + audioPlayer.addAudioPath(AudioPlayer::soundResources[1]); + } }; + std::thread t2{ []{ + audioPlayer.addAudioPath(AudioPlayer::soundResources[0]); + } }; + + std::cout << "乐\n"; + + t.join(); + t2.join(); + + std::cout << "end\n"; +} \ No newline at end of file diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/test/AduioPlayer.h b/code/ModernCpp-ConcurrentProgramming-Tutorial/test/AduioPlayer.h new file mode 100644 index 00000000..2b51ecd5 --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/test/AduioPlayer.h @@ -0,0 +1,103 @@ +#ifndef AUDIOPLAYER_H +#define AUDIOPLAYER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std::chrono_literals; + +class AudioPlayer { +public: + AudioPlayer() : stop{ false }, player_thread{ &AudioPlayer::playMusic, this } + {} + + ~AudioPlayer() { + // 等待队列中所有音乐播放完毕 + while (!audio_queue.empty()) { + std::this_thread::sleep_for(50ms); + } + stop = true; + cond.notify_all(); + if (player_thread.joinable()) { + player_thread.join(); + } + } + + void addAudioPath(const std::string& path) { + std::lock_guard lock{ mtx }; // 互斥量确保了同一时间不会有其它地方在操作共享资源(队列) + audio_queue.push(path); // 为队列添加元素 表示有新的提示音需要播放 + cond.notify_one(); // 通知线程新的音频 + } + +private: + void playMusic() { + while (!stop) { + std::string path; + { + std::unique_lock lock{ mtx }; + cond.wait(lock, [this] { return !audio_queue.empty() || stop; }); + + if (audio_queue.empty()) return; // 防止在对象为空时析构出错 + + path = audio_queue.front(); // 从队列中取出元素 + audio_queue.pop(); // 取出后就删除元素,表示此元素已被使用 + } + + if (!music.openFromFile(path)) { + std::cerr << "无法加载音频文件: " << path << std::endl; + continue; // 继续播放下一个音频 + } + + music.play(); + + // 等待音频播放完毕 + while (music.getStatus() == sf::SoundSource::Playing) { + sf::sleep(sf::seconds(0.1f)); // sleep 避免忙等占用 CPU + } + } + } + + std::atomic stop; // 控制线程的停止与退出, + std::thread player_thread; // 后台执行音频任务的专用线程 + std::mutex mtx; // 保护共享资源 + std::condition_variable cond; // 控制线程等待和唤醒,当有新任务时通知音频线程 + std::queue audio_queue; // 音频任务队列,存储待播放的音频文件路径 + sf::Music music; // SFML 音频播放器,用于加载和播放音频文件 + +public: + static constexpr std::array soundResources{ + "./sound/01初始化失败.ogg", + "./sound/02初始化成功.ogg", + "./sound/03试剂不足,请添加.ogg", + "./sound/04试剂已失效,请更新.ogg", + "./sound/05清洗液不足,请添加.ogg", + "./sound/06废液桶即将装满,请及时清空.ogg", + "./sound/07废料箱即将装满,请及时清空.ogg", + "./sound/08激发液A液不足,请添加.ogg", + "./sound/09激发液B液不足,请添加.ogg", + "./sound/10反应杯不足,请添加.ogg", + "./sound/11检测全部完成.ogg" + }; + enum SoundIndex { + InitializationFailed, + InitializationSuccessful, + ReagentInsufficient, + ReagentExpired, + CleaningAgentInsufficient, + WasteBinAlmostFull, + WasteContainerAlmostFull, + LiquidAInsufficient, + LiquidBInsufficient, + ReactionCupInsufficient, + DetectionCompleted, + SoundCount // 总音频数量,用于计数 + }; +}; + +#endif // AUDIOPLAYER_H diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/test/test_mutex.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/test/test_mutex.cpp new file mode 100644 index 00000000..c8a0555d --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/test/test_mutex.cpp @@ -0,0 +1,3 @@ +#include + +std::mutex some_mutex; diff --git a/code/ModernCpp-ConcurrentProgramming-Tutorial/test2.cpp b/code/ModernCpp-ConcurrentProgramming-Tutorial/test2.cpp new file mode 100644 index 00000000..b9266883 --- /dev/null +++ b/code/ModernCpp-ConcurrentProgramming-Tutorial/test2.cpp @@ -0,0 +1,13 @@ +#include "Log.h" +#include +#include +using namespace std::chrono_literals; + +int main() { + LOG_WARN("😅"); + std::jthread t{[]{ + std::this_thread::sleep_for(100ms); + LOG_ERROR("🤣"); + }}; + LOG_INFO("👉"); +} \ No newline at end of file diff --git a/homework/README.md b/homework/README.md new file mode 100644 index 00000000..8726a92e --- /dev/null +++ b/homework/README.md @@ -0,0 +1,13 @@ +# 此处存放作业 + +- **文件名**:`id` + 日期数字。 + + `id` :随意。 + + 日期数字:`8` 位数字,例如现在是 2024 年 7 月 31 日,那么日期数字则为:`20240731` + +- **后缀名**:根据作业情况,随意。如果是普通代码,自然是 `.cpp` 后缀。 + +--- + +1. 线程池作业存放在 [thread_pool](./thread_pool) 文件夹中。 diff --git a/homework/thread_pool/.gitkeep b/homework/thread_pool/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git "a/image/\347\214\253\347\214\253\350\231\253\346\227\213\350\275\254.jpg" "b/image/\347\214\253\347\214\253\350\231\253\346\227\213\350\275\254.jpg" new file mode 100644 index 00000000..f2c960c5 Binary files /dev/null and "b/image/\347\214\253\347\214\253\350\231\253\346\227\213\350\275\254.jpg" differ diff --git "a/image/\350\265\236\345\212\251.jpg" "b/image/\350\265\236\345\212\251.jpg" new file mode 100644 index 00000000..22adddd4 Binary files /dev/null and "b/image/\350\265\236\345\212\251.jpg" differ diff --git "a/md/01\345\237\272\346\234\254\346\246\202\345\277\265.md" "b/md/01\345\237\272\346\234\254\346\246\202\345\277\265.md" index 1773eb2d..d2797696 100644 --- "a/md/01\345\237\272\346\234\254\346\246\202\345\277\265.md" +++ "b/md/01\345\237\272\346\234\254\346\246\202\345\277\265.md" @@ -24,6 +24,38 @@   在现在,我们日常使用的机器,基本上是二者都有。我们现在的 CPU 基本都是多核,而操作系统调度基本也一样有任务切换,因为要执行的任务非常之多,CPU 是很快的,但是核心却没有那么多,不可能每一个任务都单独给一个核心。大家可以打开自己电脑的任务管理器看一眼,进程至少上百个,线程更是上千。这基本不可能每一个任务分配一个核心,都并行,而且也没必要。正是任务切换使得这些后台任务可以运行,这样系统使用者就可以同时运行文字处理器、编译器、编辑器和 Web 浏览器。 +```mermaid +graph TD; + subgraph "多核机器的真正并行" + MultiCore[多核机器] + CPU1[CPU1] + CPU2[CPU2] + CPU3[CPU3] + + MultiCore --> CPU1 + MultiCore --> CPU2 + MultiCore --> CPU3 + + CPU1 --> Task1[任务1] + CPU2 --> Task2[任务2] + CPU3 --> Task3[任务3] + end +``` + +```mermaid +graph TD; + subgraph "单核机器的任务切换" + SingleCore[单核机器] + SingleCore --> OS[操作系统调度] + OS --> Task1[任务1执行片段] + Task1 --> OS2[操作系统调度] + OS2 --> Task2[任务2执行片段] + Task2 --> OS3[操作系统调度] + OS3 --> Task3[任务3执行片段] + end + +``` + ## 并发与并行 事实上,对于这两个术语,并没有非常公认的说法。 diff --git "a/md/02\344\275\277\347\224\250\347\272\277\347\250\213.md" "b/md/02\344\275\277\347\224\250\347\272\277\347\250\213.md" index abcae16f..798596db 100644 --- "a/md/02\344\275\277\347\224\250\347\272\277\347\250\213.md" +++ "b/md/02\344\275\277\347\224\250\347\272\277\347\250\213.md" @@ -36,7 +36,7 @@ int main(){ `std::thread t{ hello };` 创建了一个线程对象 `t`,将 `hello` 作为它的[可调用(Callable)](https://zh.cppreference.com/w/cpp/named_req/Callable)对象,在新线程中执行。线程对象关联了一个线程资源,我们无需手动控制,在线程对象构造成功,就自动在新线程开始执行函数 `hello`。 -`t.join();` 等待线程对象 `t` 关联的线程执行完毕,否则将一直堵塞。这里的调用是必须的,否则 `std::thread` 的析构函数将调用 [`std::terminate()`](https://zh.cppreference.com/w/cpp/error/terminate) 无法正确析构。 +`t.join();` 等待线程对象 `t` 关联的线程执行完毕,否则将一直阻塞。这里的调用是必须的,否则 `std::thread` 的析构函数将调用 [`std::terminate()`](https://zh.cppreference.com/w/cpp/error/terminate) 无法正确析构。 这是因为我们创建线程对象 `t` 的时候就关联了一个活跃的线程,调用 `join()` 就是确保线程对象关联的线程已经执行完毕,然后会修改对象的状态,让 [`std::thread::joinable()`](https://zh.cppreference.com/w/cpp/thread/thread/joinable) 返回 `false`,表示线程对象目前没有关联活跃线程。`std::thread` 的析构函数,正是通过 `joinable()` 判断线程对象目前是否有关联活跃线程,如果为 `true`,那么就当做有关联活跃线程,会调用 `std::terminate()`。 @@ -70,7 +70,7 @@ int main(){ 当然了,都 2024 年了,我们还得考虑一个问题:“ *英特尔从 12 代酷睿开始,为其处理器引入了全新的“**大小核**”混合设计架构*”。 -比如我的 CPU `i7 13700H` 它是 14 核心,20 线程,有 6 个能效核,6 个性能核。不过我们说了,物理核心这个*通常*不看重,`hardware_concurrency()` 输出的值会为 20。 +比如我的 CPU `i7 13700H` 它是 14 核心,20 线程,有 8 个能效核,6 个性能核。不过我们说了,物理核心这个*通常*不看重,`hardware_concurrency()` 输出的值会为 20。 - **在进行多线程编程时,我们可以参考此值来确定创建的线程数量,以更好地利用当前硬件,从而提升程序性能。** @@ -91,7 +91,7 @@ auto sum(ForwardIt first, ForwardIt last){ std::size_t remainder = distance % num_threads; // 存储每个线程的结果 - std::vectorresults(num_threads); + std::vector results(num_threads); // 存储关联线程的线程对象 std::vector threads; @@ -120,7 +120,7 @@ auto sum(ForwardIt first, ForwardIt last){ } ``` -> [运行](https://godbolt.org/z/8oq3MnvT5)测试。 +> [运行](https://godbolt.org/z/9qW55aY6j)测试。 我们写了这样一个求和函数 `sum`,接受两个迭代器计算它们范围中对象的和。 @@ -291,7 +291,7 @@ my_thread.join(); 认为这样可以确保被分离的线程在这里阻塞执行完? -我们前面聊的很清楚了,detach() 是线程分离,**线程对象放弃了线程资源的所有权**,此时我们的 my_thread 它现在根本没有关联任何线程。调用 join() 是:“阻塞当前线程直至 *this 所标识的线程结束其执行”,我们的**线程对象都没有线程,堵塞什么?执行什么呢?** +我们前面聊的很清楚了,detach() 是线程分离,**线程对象放弃了线程资源的所有权**,此时我们的 my_thread 它现在根本没有关联任何线程。调用 join() 是:“阻塞当前线程直至 *this 所标识的线程结束其执行”,我们的**线程对象都没有线程,阻塞什么?执行什么呢?** 简单点说,必须是 std::thread 的 joinable() 为 true 即线程对象有活跃线程,才能调用 join() 和 detach()。 @@ -347,7 +347,7 @@ void f(){ > > 你是否觉得这样也可以?也没问题?简单的[测试](https://godbolt.org/z/Wo7Tj95Tz)运行的确没问题。 > -> **但是这是不对的**,你要注意我们的注释:“**一些当前线程可能抛出异常的代码**”,而不是 `f2()`,我们的 `try` `catch` 只是为了让线程对象关联的线程得以正确执行完毕,以及线程对象正确析构。并没有处理什么其他的东西,不掩盖错误,try` 块中的代码抛出了异常, `catch` 接住了,我们理所应当再次抛出。 +> **但是这是不对的**,你要注意我们的注释:“**一些当前线程可能抛出异常的代码**”,而不是 `f2()`,我们的 `try` `catch` 只是为了让线程对象关联的线程得以正确执行完毕,以及线程对象正确析构。并没有处理什么其他的东西,不掩盖错误,`try` 块中的代码抛出了异常, `catch` 接住了,我们理所应当再次抛出。 ### RAII @@ -691,7 +691,7 @@ int main() { } ``` -这段代码通过**移动构造**转移了线程对象 `t` 的线程资源所有权到 `t2`,这里虽然有两个 `std::thread` 对象,但是从始至终只有一个线程资源,让持有线程资源的 `t2` 对象最后调用 `join()` 堵塞让其线程执行完毕。`t` 与 `t2` 都能正常析构。 +这段代码通过**移动构造**转移了线程对象 `t` 的线程资源所有权到 `t2`,这里虽然有两个 `std::thread` 对象,但是从始至终只有一个线程资源,让持有线程资源的 `t2` 对象最后调用 `join()` 阻塞让其线程执行完毕。`t` 与 `t2` 都能正常析构。 我们还可以使用移动赋值来转移线程资源的所有权: @@ -757,7 +757,7 @@ int main(){ 了解其实现,才能更好的使用它,同时也能解释其使用与学习中的各种问题。如: -- 为什么默认按值复制? +- 如何做到的默认按值复制? - 为什么需要 `std::ref` ? - 如何支持只能移动的对象? - 如何做到接受任意[可调用](https://zh.cppreference.com/w/cpp/named_req/Callable)对象? @@ -974,7 +974,7 @@ _NODISCARD_CTOR_JTHREAD explicit jthread(_Fn&& _Fx, _Args&&... _Ax) { ### 总结 -**零开销原则**应当很好理解。我们本节的难点只在于使用到了一些 MSVC STL 的源码实现来配合理解,其主要在于“线程停止”。线程停止设施你会感觉是一种类似与外部与线程进行某种信号通信的设施,`std::stop_source` 和 `std::stop_token` 都与线程对象关联,然后来管理函数到底如何执行。 +**零开销原则**应当很好理解。我们本节的难点只在于使用到了一些 MSVC STL 的源码实现来配合理解,其主要在于“线程停止”。线程停止设施你会感觉是一种类似于外部与线程进行某种信号通信的设施,`std::stop_source` 和 `std::stop_token` 都与线程对象关联,然后来管理函数到底如何执行。 我们并没有举很多的例子,我们觉得这一个小例子所牵扯到的内容也就足够了,关键在于理解其设计与概念。 @@ -986,6 +986,6 @@ _NODISCARD_CTOR_JTHREAD explicit jthread(_Fn&& _Fx, _Args&&... _Ax) { 本章节的内容围绕着:“使用线程”,也就是"**使用 `std::thread`**"展开, `std::thread` 是我们学习 C++ 并发支持库的重中之重,在最后谈起了 C++20 引入的 `std::jthread` ,它的使用与概念也非常的简单。本章的内容在市面上并不算少见,但是却是少有的**准确与完善**。即使你早已学习乃至使用 C++ 标准库进行多线程编程,我相信本章也一定可以让你收获良多。 -并且如果是第一次学习本章的内容,能会有一些难以理解的地方。建议你多思考、多记忆,并在以后反复查看和实践。 +并且如果是第一次学习本章的内容,可能会有一些难以理解的地方。建议你多思考、多记忆,并在以后反复查看和实践。 我尽量以简单通俗的方式进行讲解。学完本章后,你可能还无法在实际环境利用多线程提升程序效率,至少还需要学习到使用互斥量来保护共享数据,才能实际应用多线程编程。 diff --git "a/md/03\345\205\261\344\272\253\346\225\260\346\215\256.md" "b/md/03\345\205\261\344\272\253\346\225\260\346\215\256.md" index a2b6cc60..ebc29e14 100644 --- "a/md/03\345\205\261\344\272\253\346\225\260\346\215\256.md" +++ "b/md/03\345\205\261\344\272\253\346\225\260\346\215\256.md" @@ -75,7 +75,7 @@ std::thread t1{f}, t2{f}, t3{f}; // 未定义行为 ## 使用互斥量 -互斥量(Mutex),又称为互斥锁(或者直接被称作“锁”),是一种用来保护**临界区**[^1]的特殊对象,其相当于实现了一个公共的“**标志位**”。它可以处于锁定(locked)状态,也可以处于解锁(unlocked)状态: +互斥量(Mutex),又常被称为互斥锁、互斥体(或者直接被称作“锁”),是一种用来保护**临界区**[^1]的特殊对象,其相当于实现了一个公共的“**标志位**”。它可以处于锁定(locked)状态,也可以处于解锁(unlocked)状态: 1. 如果互斥量是锁定的,通常说某个特定的线程正持有这个锁。 @@ -336,7 +336,7 @@ public: Data* p = nullptr; void malicious_function(Data& protected_data){ - p = &protected_data; // 受保护的数据被传递 + p = &protected_data; // 受保护的数据被传递到外部 } Data_wrapper d; @@ -371,7 +371,7 @@ std::size_t n{}; void f(){ std::lock_guard lc1{ m1 }; - std::lock_guard lc2{ m2 };; + std::lock_guard lc2{ m2 }; ++n; } void f2() { @@ -482,7 +482,6 @@ void swap(X& lhs, X& rhs) { std::unique_lock lock2{ rhs.m, std::defer_lock }; std::lock(lock1, lock2); swap(lhs.object, rhs.object); - ++n; } ``` @@ -621,11 +620,10 @@ void f() { 一种可能的使用是允许函数去锁住一个互斥量,并将互斥量的所有权转移到调用者上,所以调用者可以在这个锁保护的范围内执行代码。 ```cpp -std::unique_lockget_lock(){ +std::unique_lock get_lock(){ extern std::mutex some_mutex; std::unique_lock lk{ some_mutex }; return lk; - } void process_data(){ std::unique_lock lk{ get_lock() }; @@ -649,7 +647,7 @@ void process_data(){ 保护共享数据并非必须使用互斥量,互斥量只是其中一种常见的方式而已,对于一些特殊的场景,也有专门的保护方式,比如**对于共享数据的初始化过程的保护**。我们通常就不会用互斥量,**这会造成很多的额外开销**。 -我们不想为各位介绍其它乱七八糟的各种保护初始化的方式,我们只介绍三种:**双检锁(错误)**、**使用 `std::call_once`**、**静态局部变量初始化在 C++11 是线程安全**。 +我们不想为各位介绍其它乱七八糟的各种保护初始化的方式,我们只介绍三种:**双检锁(错误)**、**使用 `std::call_once`**、**静态局部变量初始化从 C++11 开始是线程安全**。 1. **双检锁(错误)线程不安全** @@ -678,8 +676,7 @@ void process_data(){ 2. C++ 标准委员会也认为处理此问题很重要,**所以标准库提供了 [`std::call_once`](https://zh.cppreference.com/w/cpp/thread/call_once) 和 [`std::once_flag`](https://zh.cppreference.com/w/cpp/thread/once_flag)** 来处理这种情况。比起锁住互斥量并显式检查指针,每个线程只需要使用 `std::call_once` 就可以。**使用 `std::call_once` 比显式使用互斥量消耗的资源更少,特别是当初始化完成之后**。 ```cpp - std::shared_ptrptr; - std::mutex m; + std::shared_ptr ptr; std::once_flag resource_flag; void init_resource(){ @@ -811,7 +808,7 @@ int main() { - [**`lock`**](https://zh.cppreference.com/w/cpp/thread/recursive_mutex/lock):线程可以在递归互斥体上重复调用 `lock`。在线程调用 `unlock` 匹配次数后,所有权才会得到**释放**。 -- [**`unlock`**](https://zh.cppreference.com/w/cpp/thread/recursive_mutex/unlock):若所有权层数为 1(此线程对 [lock()](https://zh.cppreference.com/w/cpp/thread/recursive_mutex/lock) 的调用恰好比 `unlock()` 多一次 )则**解锁互斥体**,否则将所有权层数减少 1。 +- [**`unlock`**](https://zh.cppreference.com/w/cpp/thread/recursive_mutex/unlock):若所有权层数为 1(此线程对 [lock()](https://zh.cppreference.com/w/cpp/thread/recursive_mutex/lock) 的调用恰好比 `unlock()` 多一次 )则**解锁互斥量**,否则将所有权层数减少 1。 我们重点的强调了一下这两个成员函数的这个概念,其实也很简单,总而言之就是 `unlock` 必须和 `lock` 的调用次数一样,才会真正解锁互斥量。 diff --git "a/md/04\345\220\214\346\255\245\346\223\215\344\275\234.md" "b/md/04\345\220\214\346\255\245\346\223\215\344\275\234.md" index 8c5d14c1..85e001f0 100644 --- "a/md/04\345\220\214\346\255\245\346\223\215\344\275\234.md" +++ "b/md/04\345\220\214\346\255\245\346\223\215\344\275\234.md" @@ -1,6 +1,6 @@ # 同步操作 -"同步操作"是指在计算机科学和信息技术中的一种操作方式,其中不同的任务或操作按顺序执行,一个操作完成后才能开始下一个操作。在多线程编程中,各个任务通常需要通过同步操作进行相互**协调和等待**,以确保数据的**一致性**和**正确性**。 +"同步操作"是指在计算机科学和信息技术中的一种操作方式,其中不同的任务或操作按顺序执行,一个操作完成后才能开始下一个操作。在多线程编程中,各个任务通常需要通过**同步设施**进行相互**协调和等待**,以确保数据的**一致性**和**正确性**。 本章的主要内容有: @@ -12,7 +12,7 @@ - Qt 实现异步任务的示例 -- 其它 C++20 同步设施:信号两、闩与屏障 +- 其它 C++20 同步设施:信号量、闩与屏障 本章将讨论如何使用条件变量等待事件,介绍 future 等标准库设施用作同步操作,使用Qt+CMake 构建一个项目展示多线程的必要性,介绍 C++20 引入的新的同步设施。 @@ -125,13 +125,28 @@ while (!pred()) wait(lock); ``` -这可以避免“[虚假唤醒(spurious wakeup)](https://en.wikipedia.org/wiki/Spurious_wakeup)”。 +第二个版本只是对第一个版本的**包装**,等待并判断谓词,会调用第一个版本的重载。这可以避免“[虚假唤醒(spurious wakeup)](https://en.wikipedia.org/wiki/Spurious_wakeup)”。 > 条件变量虚假唤醒是指在使用条件变量进行线程同步时,有时候线程可能会在没有收到通知的情况下被唤醒。问题取决于程序和系统的具体实现。解决方法很简单,在循环中等待并判断条件可一并解决。使用 C++ 标准库则没有这个烦恼了。 +我们也可以简单看一下 MSVC STL 的[源码实现](https://github.com/microsoft/STL/blob/22a8826/stl/inc/mutex#L555-L565): + +```cpp +void wait(unique_lock& _Lck) noexcept { + _Cnd_wait(_Mycnd(), _Lck.mutex()->_Mymtx()); +} + +template +void wait(unique_lock& _Lck, _Predicate _Pred) { + while (!_Pred()) { + wait(_Lck); + } +} +``` + ## 线程安全的队列 -在本节中,我们介绍了一个更为复杂的示例,以巩固我们对条件变量的学习。为了实现一个线程安全的队列,我们需要考虑以下两个关键点: +在本节中,我们介将绍一个更为复杂的示例,以巩固我们对条件变量的学习。为了实现一个线程安全的队列,我们需要考虑以下两个关键点: 1. 当执行 `push` 操作时,需要确保没有其他线程正在执行 `push` 或 `pop` 操作;同样,在执行 `pop` 操作时,也需要确保没有其他线程正在执行 `push` 或 `pop` 操作。 @@ -165,7 +180,7 @@ public: std::shared_ptr pop() { std::unique_lock lk{ m }; data_cond.wait(lk, [this] {return !data_queue.empty(); }); - std::shared_ptrres { std::make_shared(data_queue.front()) }; + std::shared_ptr res { std::make_shared(data_queue.front()) }; data_queue.pop(); return res; } @@ -271,6 +286,143 @@ Consumer 线程弹出元素 4: 到此,也就可以了。 +## 使用条件变量实现后台提示音播放 + +一个常见的场景是:当你的软件完成了主要功能后,领导可能突然要求添加一些竞争对手产品的功能。比如领导看到了人家的设备跑起来总是有一些播报,说明当前的情况,执行的过程,或者报错了也会有提示音说明。于是就想让我们的程序也增加“**语音提示**”的功能。此时,你需要考虑如何在程序运行到不同状态时添加适当的语音播报,并且**确保这些提示音的播放不会影响其他功能的正常运行**。 + +为了不影响程序的流畅执行,提示音的播放显然不能占据业务线程的资源。我们需要额外启动一个线程来专门处理这个任务。 + +但是,大多数的提示音播放都是短暂且简单。如果每次播放提示音时都新建一个线程,且不说创建线程也需要大量时间,可能影响业务正常的执行任务的流程,就光是其频繁创建线程的开销也是不能接受的。 + +--- + +因此,更合理的方案是:**在程序启动时,就启动一个专门用于播放提示音的线程。当没有需要播放的提示时,该线程会一直处于等待状态;一旦有提示音需要播放,线程就被唤醒,完成播放任务**。 + +具体来说,我们可以通过条件变量来实现这一逻辑,核心是监控一个音频队列。我们可以封装一个类型,包含以下功能: + +- 一个成员函数在对象构造时就启动,使用条件变量监控队列是否为空,互斥量确保共享资源的同步。如果队列中有任务,就取出并播放提示音;如果队列为空,则线程保持阻塞状态,等待新的任务到来。 +- 提供一个外部函数,以供在需要播放提示音的时候调用它,向队列添加新的元素,该函数需要通过互斥量来保护数据一致性,并在成功添加任务后唤醒条件变量,通知播放线程执行任务。 + +> 这种设计通过合理利用**条件变量**和**互斥量**,不仅有效减少了 CPU 的无效开销,还能够确保主线程的顺畅运行。它不仅适用于提示音的播放,还能扩展用于其他类似的后台任务场景。 + +我们引入 [SFML](https://github.com/SFML/SFML) 三方库进行声音播放,然后再自己进行上层封装。 + +```CPP +class AudioPlayer { +public: + AudioPlayer() : stop{ false }, player_thread{ &AudioPlayer::playMusic, this } + {} + + ~AudioPlayer() { + // 等待队列中所有音乐播放完毕 + while (!audio_queue.empty()) { + std::this_thread::sleep_for(50ms); + } + stop = true; + cond.notify_all(); + if (player_thread.joinable()) { + player_thread.join(); + } + } + + void addAudioPath(const std::string& path) { + std::lock_guard lock{ mtx }; // 互斥量确保了同一时间不会有其它地方在操作共享资源(队列) + audio_queue.push(path); // 为队列添加元素 表示有新的提示音需要播放 + cond.notify_one(); // 通知线程新的音频 + } + +private: + void playMusic() { + while (!stop) { + std::string path; + { + std::unique_lock lock{ mtx }; + cond.wait(lock, [this] { return !audio_queue.empty() || stop; }); + + if (audio_queue.empty()) return; // 防止在对象为空时析构出错 + + path = audio_queue.front(); // 从队列中取出元素 + audio_queue.pop(); // 取出后就删除元素,表示此元素已被使用 + } + + if (!music.openFromFile(path)) { + std::cerr << "无法加载音频文件: " << path << std::endl; + continue; // 继续播放下一个音频 + } + + music.play(); + + // 等待音频播放完毕 + while (music.getStatus() == sf::SoundSource::Playing) { + sf::sleep(sf::seconds(0.1f)); // sleep 避免忙等占用 CPU + } + } + } + + std::atomic stop; // 控制线程的停止与退出, + std::thread player_thread; // 后台执行音频任务的专用线程 + std::mutex mtx; // 保护共享资源 + std::condition_variable cond; // 控制线程等待和唤醒,当有新任务时通知音频线程 + std::queue audio_queue; // 音频任务队列,存储待播放的音频文件路径 + sf::Music music; // SFML 音频播放器,用于加载和播放音频文件 +}; +``` + +该代码实现了一个简单的**后台音频播放类型**,通过**条件变量**和**互斥量**确保播放线程 `playMusic` 只在只在**有音频任务需要播放时工作**(当外部通过调用 `addAudioPath()` 向队列添加播放任务时)。在没有任务时,线程保持等待状态,避免占用 CPU 资源影响主程序的运行。 + +> ### 注意 +> +> 其实这段代码还存在着一个初始化顺序导致的问题,见 [**#27**](https://github.com/Mq-b/ModernCpp-ConcurrentProgramming-Tutorial/issues/27) + +此外,关于提示音的播报,为了避免每次都手动添加路径,我们可以创建一个音频资源数组,便于使用: + +```cpp +static constexpr std::array soundResources{ + "./sound/01初始化失败.ogg", + "./sound/02初始化成功.ogg", + "./sound/03试剂不足,请添加.ogg", + "./sound/04试剂已失效,请更新.ogg", + "./sound/05清洗液不足,请添加.ogg", + "./sound/06废液桶即将装满,请及时清空.ogg", + "./sound/07废料箱即将装满,请及时清空.ogg", + "./sound/08激发液A液不足,请添加.ogg", + "./sound/09激发液B液不足,请添加.ogg", + "./sound/10反应杯不足,请添加.ogg", + "./sound/11检测全部完成.ogg" +}; +``` + +为了提高代码的可读性,我们还可以使用一个枚举类型来表示音频资源的索引: + +```cpp +enum SoundIndex { + InitializationFailed, + InitializationSuccessful, + ReagentInsufficient, + ReagentExpired, + CleaningAgentInsufficient, + WasteBinAlmostFull, + WasteContainerAlmostFull, + LiquidAInsufficient, + LiquidBInsufficient, + ReactionCupInsufficient, + DetectionCompleted, + SoundCount // 总音频数量,用于计数 +}; +``` + +需要注意的是 SFML不支持 `.mp3` 格式的音频文件,大家可以使用 ffmpeg 或者其它软件[网站](https://www.freeconvert.com/audio-converter)将音频转换为支持的格式。 + +如果是测试使用,不知道去哪生成这些语音播报,我们推荐 [`tts-vue`](https://github.com/LokerL/tts-vue)。 + +> 我们的代码也可以在 Linux 中运行,并且整体仅需 C++11 标准(除了 `soundResources` 数组)。 +> SFML 依赖于 [**FLAC**](https://xiph.org/flac/) 和 [**OpenAL**](https://www.openal.org/) 这两个库。官网上[下载](https://www.sfml-dev.org/download/sfml/2.5.1/)的 windows 版本的 SFML 已包含这些依赖,但在 Linux 上需要用户自行下载并安装它们。如: +> ```shell +> sudo apt-get install libflac-dev +> sudo apt-get install libopenal-dev +> ``` + + ## 使用 `future` 举个例子:我们在车站等车,你可能会做一些别的事情打发时间,比如学习[现代 C++ 模板教程](https://github.com/Mq-b/Modern-Cpp-templates-tutorial)、观看 [mq白](https://space.bilibili.com/1292761396) 的视频教程、玩手机等。不过,你始终在等待一件事情:***车到站***。 @@ -279,7 +431,7 @@ C++ 标准库将这种事件称为 [future](https://zh.cppreference.com/w/cpp/t C++ 标准库有两种 future,都声明在 [``](https://zh.cppreference.com/w/cpp/header/future) 头文件中:独占的 [`std::future`](https://zh.cppreference.com/w/cpp/thread/future) 、共享的 [`std::shared_future`](https://zh.cppreference.com/w/cpp/thread/shared_future)。它们的区别与 `std::unique_ptr` 和 `std::shared_ptr` 类似。`std::future` 只能与**单个**指定事件关联,而 `std::shared_future` 能关联**多个**事件。它们都是模板,它们的模板类型参数,就是其关联的事件(函数)的返回类型。当多个线程需要访问一个独立 future 对象时, 必须使用互斥量或类似同步机制进行保护。而多个线程访问同一共享状态,若每个线程都是通过其自身的 `shared_future` 对象副本进行访问,则是安全的。 -最简单的作用是,我们先前讲的 `std::thread` 执行任务是没有返回值的,这个问题就能使用 future 解决。 +最简单有效的使用是,我们先前讲的 `std::thread` 在线程中执行任务是没有返回值的,这个问题就能使用 future 解决。 ### 创建异步任务获取返回值 @@ -431,14 +583,14 @@ int main(){ 其实到此基本就差不多了,我们再介绍两个常见问题即可: -1. 如果从 `std::async` 获得的 [std::future](https://zh.cppreference.com/w/cpp/thread/future) 没有被移动或绑定到引用,那么在完整表达式结尾, [std::future](https://zh.cppreference.com/w/cpp/thread/future) 的**析构函数将阻塞到异步计算完成**。因为临时对象的生存期就在这一行,调用析构函数阻塞执行。 +1. 如果从 `std::async` 获得的 [`std::future`](https://zh.cppreference.com/w/cpp/thread/future) 没有被移动或绑定到引用,那么在完整表达式结尾, `std::future` 的**[析构函数](https://zh.cppreference.com/w/cpp/thread/future/%7Efuture)将阻塞,直到到异步任务完成**。因为临时对象的生存期就在这一行,而对象生存期结束就会调用调用析构函数。 ```cpp std::async(std::launch::async, []{ f(); }); // 临时量的析构函数等待 f() std::async(std::launch::async, []{ g(); }); // f() 完成前不开始 ``` - 如你所见,这并不能创建异步任务,会堵塞,然后逐个执行。 + 如你所见,这并不能创建异步任务,它会阻塞,然后逐个执行。 2. 被移动的 `std::future` 没有所有权,失去共享状态,不能调用 `get`、`wait` 成员函数。 @@ -474,7 +626,7 @@ std::packaged_task task([](int a, int b){ }); std::futurefuture = task.get_future(); task(10, 2); // 此处执行任务 -std::cout << future.get() << '\n'; // 不堵塞,此处获取返回值 +std::cout << future.get() << '\n'; // 不阻塞,此处获取返回值 ``` > [运行](https://godbolt.org/z/799Khvadc)测试。 @@ -485,12 +637,12 @@ std::cout << future.get() << '\n'; // 不堵塞,此处获取返回值 std::packaged_task task([](int a, int b){ return std::pow(a, b); }); -std::futurefuture = task.get_future(); +std::future future = task.get_future(); std::thread t{ std::move(task),10,2 }; // 任务在线程中执行 // todo.. 幻想还有许多耗时的代码 t.join(); -std::cout << future.get() << '\n'; // 并不堵塞,获取任务返回值罢了 +std::cout << future.get() << '\n'; // 并不阻塞,获取任务返回值罢了 ``` > [运行](https://godbolt.org/z/85r9db49z)测试。 @@ -544,9 +696,9 @@ auto sum(ForwardIt first, ForwardIt last) { std::size_t remainder = distance % num_threads; // 存储每个线程要执行的任务 - std::vector>tasks; + std::vector> tasks; // 和每一个任务进行关联的 future 用于获取返回值 - std::vector>futures(num_threads); + std::vector> futures(num_threads); // 存储关联线程的线程对象 std::vector threads; @@ -580,9 +732,9 @@ auto sum(ForwardIt first, ForwardIt last) { } ``` -> [运行](https://godbolt.org/z/r19MYcv6e)测试。 +> [运行](https://godbolt.org/z/79fe1Gvcq)测试。 -相比于之前,其实不同无非是定义了 `std::vector> tasks` 与 `std::vector> futures` ,然后在循环中制造任务插入容器,关联 tuple,再放到线程中执行。最后汇总的时候写一个循环,`futures[i].get()` 获取任务的返回值加起来即可。 +相比于之前,其实不同无非是定义了 `std::vector> tasks` 与 `std::vector> futures` ,然后在循环中制造任务插入容器,关联 future,再放到线程中执行。最后汇总的时候写一个循环,`futures[i].get()` 获取任务的返回值加起来即可。 到此,也就可以了。 @@ -659,7 +811,7 @@ int main() { 来自线程的异常: 一个异常 ``` -你可能对这段代码还有一些疑问:我们写的是 `promised` ,但是却没有使用 `set_value` 设置值,你可能会想着再写一行 `prom.set_value(0)`? +你可能对这段代码还有一些疑问:我们写的是 `promise` ,但是却没有使用 `set_value` 设置值,你可能会想着再写一行 `prom.set_value(0)`? 共享状态的 promise 已经存储值或者异常,再次调用 `set_value`(`set_exception`) 会抛出 [std::future_error](https://zh.cppreference.com/w/cpp/thread/future_error) 异常,将错误码设置为 [`promise_already_satisfied`](https://zh.cppreference.com/w/cpp/thread/future_errc)。这是因为 `std::promise` 对象只能是存储值或者异常其中一种,而**无法共存**。 @@ -753,13 +905,43 @@ _Ty& get() { 如果需要进行多次 `get` 调用,可以考虑使用下文提到的 `std::shared_future`。 -### 多个线程的等待 +### 多个线程的等待 `std::shared_future` + +之前的例子中我们一直使用 `std::future`,但 `std::future` 有一个局限:**future 是一次性的**,它的结果只能被一个线程获取。`get()` 成员函数只能调用一次,当结果被某个线程获取后,`std::future` 就无法再用于其他线程。 -之前的例子中都在用 `std::future` ,不过 `std::future` 也有局限性。很多线程在等待的时候,只有一个线程能获取结果。当多个线程等待相同事件的结果时,就需要使用 `std::shared_future` 来替代 `std::future` 了。`std::future` 与 `std::shared_future` 的区别就如同 `std::unique_ptr`、`std::shared_ptr` 一样。 +```cpp +int task(){ + // todo.. + return 10; +} + +void thread_functio(std::future& fut){ + // todo.. + int result = fut.get(); + std::cout << result << '\n'; + // todo.. +} + +int main(){ + auto future = std::async(task); // 启动耗时的异步任务 + + // 可能有多个线程都需要此任务的返回值,于是我们将与其关联的 future 对象的引入传入 + std::thread t{ thread_functio,std::ref(future) }; + std::thread t2{ thread_functio,std::ref(future) }; + t.join(); + t2.join(); +} +``` + +> 可能有多个线程都需要耗时的异步任务的返回值,于是我们将与其关联的 future 对象的引入传给线程对象,让它能在需要的时候获取。 +> +> 但是这存在个问题,future 是一次性的,只能被调用一次 `get()` 成员函数,所以以上代码存在问题。 + +此时就需要使用 `std::shared_future` 来替代 `std::future` 了。`std::future` 与 `std::shared_future` 的区别就如同 `std::unique_ptr`、`std::shared_ptr` 一样。 `std::future` 是只能移动的,其所有权可以在不同的对象中互相传递,但只有一个对象可以获得特定的同步结果。而 `std::shared_future` 是可复制的,多个对象可以指代同一个共享状态。 -在多个线程中对**同一个 **`std::shared_future` 对象进行操作时(如果没有进行同步保护)存在竞争条件。而从多个线程访问同一共享状态,若每个线程都是通过其自身的 `shared_future` 对象**副本**进行访问,则是安全的。 +在多个线程中对**同一个 **`std::shared_future` 对象进行操作时(如果没有进行同步保护)存在条件竞争。而从多个线程访问同一共享状态,若每个线程都是通过其自身的 `shared_future` 对象**副本**进行访问,则是安全的。 ```cpp std::string fetch_data() { @@ -792,7 +974,7 @@ int main() { } ``` -这段代码存在数据竞争,就如同我们先前所说:“***在多个线程中对**同一个 **`std::shared_future` 对象进行操作时(如果没有进行同步保护)存在竞争条件***”,它并没有提供线程安全的方式。而我们的 lambda 是按引用传递,也就是“**同一个**”进行操作了。可以改为: +这段代码存在数据竞争,就如同我们先前所说:“***在多个线程中对**同一个 **`std::shared_future` 对象进行操作时(如果没有进行同步保护)存在条件竞争***”,它并没有提供线程安全的方式。而我们的 lambda 是按引用传递,也就是“**同一个**”进行操作了。可以改为: ```cpp std::string fetch_data() { @@ -822,13 +1004,13 @@ int main() { } ``` -这样访问的就都是 `std::shared_future` 的副本了,我们的 lambda 按复制捕获 std::shared_future 对象,每个线程都有一个 shared_future 的副本,这样不会有任何问题。这一点和 `std::shared_ptr` 类似[^2]。 +这样访问的就都是 `std::shared_future` 的副本了,我们的 lambda 按复制捕获 `std::shared_future` 对象,每个线程都有一个 shared_future 的副本,这样不会有任何问题。这一点和 `std::shared_ptr` 类似[^2]。 `std::promise` 也同,它的 `get_future()` 成员函数一样可以用来构造 `std::shared_future`,虽然它的返回类型是 `std::future`,不过不影响,这是因为 `std::shared_future` 有一个 `std::future&&` 参数的[构造函数](https://zh.cppreference.com/w/cpp/thread/shared_future/shared_future),转移 `std::future` 的所有权。 ```cpp -std::promisep; -std::shared_futuresf{ p.get_future() }; // 隐式转移所有权 +std::promise p; +std::shared_future sf{ p.get_future() }; // 隐式转移所有权 ``` 就不需要再强调了。 @@ -870,7 +1052,7 @@ class duration; 如你所见,它默认的时钟节拍是 1,这是一个很重要的类,标准库通过它定义了很多的时间类型,比如 **`std::chrono::minutes`** 是分钟类型,那么它的 `Period` 就是 `std::ratio<60>` ,因为一分钟等于 60 秒。 ```cpp -std::chrono::minutes std::chrono::duration> +using minutes = duration>; ``` 稳定时钟(Steady Clock)是指提供稳定、持续递增的时间流逝信息的时钟。它的特点是不受系统时间调整或变化的影响,即使在系统休眠或时钟调整的情况下,它也能保持稳定。在 C++ 标准库中,[`std::chrono::steady_clock`](https://zh.cppreference.com/w/cpp/chrono/steady_clock) 就是一个稳定时钟。它通常用于测量时间间隔和性能计时等需要高精度和稳定性的场景。可以通过 `is_steady` 静态常量判断当前时钟是否是稳定时钟。 @@ -913,10 +1095,12 @@ using years = duration, days::period>>; using months = duration>>; ``` +如果没有指明 `duration` 的第二个非类型模板参数,那么代表默认 `std::ratio<1>`,比如 `seconds` 也就是一秒。 + 如上,是 MSVC STL 定义的,看似有一些没有使用 `ratio` 作为第二个参数,其实也还是别名罢了,[见](https://github.com/microsoft/STL/blob/daeb0a6/stl/inc/ratio#L262-L277): ```cpp -using milli = ratio<1, 1000>; +using milli = ratio<1, 1000>; // 千分之一秒,也就是一毫秒了 ``` 并且为了方便使用,在 C++14 标准库增加了时间字面量,存在于 `std::chrono_literals` 命名空间中,让我们得以简单的使用: @@ -940,7 +1124,30 @@ std::chrono::seconds s = std::chrono::duration_cast(ms); std::cout << s.count() << '\n'; ``` -这里的结果是截断的,而不会进行所谓的四舍五入,最终的值是 `3`。 +这里的结果是**截断**的,而不会进行所谓的四舍五入,`3999` 毫秒,也就是 `3.999` 秒最终的值是 `3`。 + +> 很多时候这并不是我们想要的,比如我们想要的其实是输出 `3.999` 秒,而不是 `3` 秒 或者 `3999` 毫秒。 +> +> seconds 是 `duration` 这意味着它无法接受浮点数,我们直接改成 `duration` 即可: +> +> ```cpp +> std::chrono::duration s = std::chrono::duration_cast>(ms); +> ``` +> +> 当然了,这样写很冗余,并且这种形式的转换是可以直接隐式的,也就是其实我们可以直接: +> +> ```cpp +> std::chrono::duration s = ms; +> ``` +> +> 无需使用 `duration_cast`,可以直接隐式转换。 +> +> 另外我们用的 `duration` 都是省略了 `ratio` 的,其实默认类型就是 `ratio<1>`,代表一秒。参见源码[声明](https://github.com/microsoft/STL/blob/0be5257/stl/inc/__msvc_chrono.hpp#L80-L81): +> +> ```cpp +> _EXPORT_STD template > +> class duration; +> ``` 时间库支持四则运算,可以对两个时间段进行加减乘除。时间段对象可以通过 [`count()`](https://zh.cppreference.com/w/cpp/chrono/duration/count) 成员函数获得计次数。例如 `std::chrono::milliseconds{123}.count()` 的结果就是 123。 @@ -979,7 +1186,7 @@ template< > class time_point; ``` -如你所见,它的第二个模板参数的时间单位,默认是根据第一个参数时钟得到的,所以假设有类型: +如你所见,它的第二个模板参数是**时间段**,就是时间的间隔,其实也就可以理解为表示时间点的**精度**,默认是根据第一个参数时钟得到的,所以假设有类型: ```cpp std::chrono::time_point @@ -997,8 +1204,30 @@ std::chrono::time_point> // // 100 nanoseconds ``` +也就是说 `std::chrono::time_point` 的精度是 100 纳秒。 + 更多的问题参见[源码](https://github.com/microsoft/STL/blob/f54203f/stl/inc/__msvc_chrono.hpp#L644-L647)都很直观。 +> 注意,这里的精度并非是实际的时间精度。时间和硬件系统等关系极大,以 windows 为例: +> +> Windows 内核中的时间间隔计时器默认每隔 **15.6** 毫秒触发一次中断。因此,如果你使用基于系统时钟的计时方法,默认情况下精度约为 15.6 毫秒。不可能达到纳秒级别。 +> +> 由于这个系统时钟的限制,那些基于系统时钟的 API(例如 `Sleep()`、`WaitForSingleObject()` 等)的最小睡眠时间默认就是 15.6 毫秒左右。 +> +> 如: +> +> ```cpp +> std::this_thread::sleep_for(std::chrono::milliseconds(1)); +> ``` +> +> 不过我们也可以使用系统 API 调整系统时钟的精度,需要链接 windows 多媒体库 **`winmm.lib`** ,然后使用 API: +> +> ```cpp +> timeBeginPeriod(1); // 设置时钟精度为 1 毫秒 +> // todo.. +> timeEndPeriod(1); // 恢复默认精度 +> ``` + --- 同样的,时间点也支持加减以及比较操作。 @@ -1192,7 +1421,7 @@ async_progress_bar::async_progress_bar(QWidget *parent) ### 跨平台兼容性 -C++11 的 `std::this_thread::get_id()` 返回的内部类型没办法直接转换为 `unsigned int`,我们就直接使用了 win32 的 API *`_Thrd_id()`* 了。如果您是 Linux 之类的环境,使用 [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/) 接口 [*`pthread_self()`*](https://pubs.opengroup.org/onlinepubs/009696699/functions/pthread_self.html)。 +C++11 的 `std::this_thread::get_id()` 返回的内部类 [`std::thread::id`](https://zh.cppreference.com/w/cpp/thread/thread/id) 没办法直接转换为 `unsigned int`,我们就直接使用了 win32 的 API *`_Thrd_id()`* 了。如果您是 Linux 之类的环境,使用 [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/) 接口 [*`pthread_self()`*](https://pubs.opengroup.org/onlinepubs/009696699/functions/pthread_self.html)。 ### 实践建议 @@ -1328,7 +1557,7 @@ int main() { ### `std::latch` -“闩”,这个字其实个人觉得是不常见,“**门闩**” 是指门背后用来关门的棍子。好了好了,不用在意,在 C++ 中就是先前说的:*单次使用的线程屏障*。 +“*闩*” ,中文语境一般说“*门闩*” 是指门背后用来关门的棍子。不过不用在意,在 C++ 中的意思就是先前说的:***单次使用的线程屏障***。 `latch` 类维护着一个 [`std::ptrdiff_t`](https://zh.cppreference.com/w/cpp/types/ptrdiff_t) 类型的计数[^6],且只能**减少**计数,**无法增加计数**。在创建对象的时候初始化计数器的值。线程可以阻塞,直到 latch 对象的计数减少到零。由于无法增加计数,这使得 `latch` 成为一种**单次使用的屏障**。 @@ -1455,7 +1684,7 @@ int main(){ - 并发调用`barrier` 除了析构函数外的成员函数不会引起数据竞争。 -另外你可能注意到我们使用了 [`std::osyncstream`](https://zh.cppreference.com/w/cpp/io/basic_osyncstream) ,它是 C++20 引入的,此处是确保输出流在多线程环境中同步,**免除数据竞争,而且将不以任何方式穿插或截断**。 +另外你可能注意到我们使用了 [`std::osyncstream`](https://zh.cppreference.com/w/cpp/io/basic_osyncstream) ,它是 C++20 引入的,此处是确保输出流在多线程环境中同步,**避免除数据竞争,而且将不以任何方式穿插或截断**。 > 虽然 `std::cout` 的 `operator<<` 调用是线程安全的,不会被打断,但多个 `operator<<` 的调用在多线程环境中可能会**交错**,导致输出结果混乱,使用 `std::osyncstream` 就可以解决这个问题。开发者可以尝试去除 `std::osyncstream` 直接使用 `std::cout`,效果会非常明显。 @@ -1483,10 +1712,10 @@ std::barrier barrier{ 4, } }; -void f(int thread_id){ - for (int i = 0; i < 5; ++i) { +void f(int thread_id) { + for (int i = 1; i <= 5; ++i) { std::osyncstream{ std::cout } << "线程 " << thread_id << " 输出: " << i << '\n'; - if (i == 2 && thread_id == 2) { // 假设线程ID为2的线程在输出完2后退出 + if (i == 3 && thread_id == 2) { // 假设线程 ID 为 2 的线程在完成第三轮同步后退出 std::osyncstream{ std::cout } << "线程 " << thread_id << " 完成并退出\n"; --active_threads; // 减少活跃线程数 barrier.arrive_and_drop(); // 减少当前计数 1,并减少重置计数 1 @@ -1496,7 +1725,7 @@ void f(int thread_id){ } } -int main(){ +int main() { std::vector threads; for (int i = 1; i <= 4; ++i) { threads.emplace_back(f, i); @@ -1504,9 +1733,9 @@ int main(){ } ``` -> [运行](https://godbolt.org/z/csor1bq8d)测试。 +> [运行](https://godbolt.org/z/T7WYeYGd1)测试。 -初始线程有 4 个,线程 2 在执行了两轮同步之后便直接退出了,调用 `arrive_and_drop` 函数,下一个阶段的计数会重置为 `3`,也就是只有三个活跃线程继续执行。查看输出结果,非常的直观。 +初始线程有 4 个,线程 2 在执行了**三轮**同步便直接退出了,调用 `arrive_and_drop` 函数,下一个阶段的计数会重置为 `3`,也就是执行完第三轮同步后只有**三个活跃线程**继续执行。查看输出结果,非常的直观。 这样,`arrive_and_drop` 的作用就非常明显了,使用也十分的简单。 diff --git "a/md/05\345\206\205\345\255\230\346\250\241\345\236\213\344\270\216\345\216\237\345\255\220\346\223\215\344\275\234.md" "b/md/05\345\206\205\345\255\230\346\250\241\345\236\213\344\270\216\345\216\237\345\255\220\346\223\215\344\275\234.md" index ea4d47d4..1b576d77 100644 --- "a/md/05\345\206\205\345\255\230\346\250\241\345\236\213\344\270\216\345\216\237\345\255\220\346\223\215\344\275\234.md" +++ "b/md/05\345\206\205\345\255\230\346\250\241\345\236\213\344\270\216\345\216\237\345\255\220\346\223\215\344\275\234.md" @@ -40,11 +40,13 @@ void f() { ### 原子类型 `std::atomic` -标准原子类型定义在头文件 [``](https://zh.cppreference.com/w/cpp/header/atomic) 中。这些类型的操作都是原子的,语言定义中只有这些类型的操作是原子的,虽然也可以用互斥量来模拟原子操作(见上文)。标准的原子的类型实现可能是:*它们几乎都有一个 `is_lock_free()` 成员函数,这个函数可以让用户查询某原子类型的操作是直接用的原子指令(返回 `true`),还是内部用了锁实现(返回 `false`)。* +标准原子类型定义在头文件 [``](https://zh.cppreference.com/w/cpp/header/atomic) 中。这些类型的操作都是原子的,语言定义中只有这些类型的操作是原子的,虽然也可以用互斥量来模拟原子操作(见上文)。 -> 每个 `std::atomic` 模板的实例化和全特化均定义一个原子类型。**如果一个线程写入原子对象,同时另一线程从它读取,那么行为有良好定义**(数据竞争的细节见[内存模型](https://zh.cppreference.com/w/cpp/language/memory_model))。 +标准原子类型的实现通常包括一个 `is_lock_free()` 成员函数,允许用户查询特定原子类型的操作是否是通过直接的原子指令实现(返回 true),还是通过锁来实现(返回 false)。 -原子操作可以代替互斥量,来进行同步操作,也能带来更高的性能。但是如果它的内部使用互斥量实现,那么不可能有性能的提升。 +> **如果一个线程写入原子对象,同时另一线程从它读取,那么行为有良好定义**(数据竞争的细节见[内存模型](https://zh.cppreference.com/w/cpp/language/memory_model))。 + +原子操作可以在一些时候代替互斥量,来进行同步操作,也能带来更高的性能。但是如果它的内部使用互斥量实现,那么不可能有性能的提升。 在 C++17 中,所有原子类型都有一个 `static constexpr` 的数据成员 [`is_always_lock_free`](https://zh.cppreference.com/w/cpp/atomic/atomic/is_always_lock_free) 。如果当前环境上的原子类型 X 是无锁类型,那么 `X::is_always_lock_free` 将返回 `true` 。例如: @@ -101,9 +103,9 @@ else { 因为 `is_always_lock_free` 是编译期常量,所以我们可以使用 C++17 引入的 `constexpr if` ,它可以在编译阶段进行决策,避免了运行时的判断开销,提高了性能。 -宏则更是简单了,最基本的预处理器判断,在预处理阶段就选择执行合适的代码。 +宏则更是简单了,最基本的预处理器判断,在预处理阶段就选择编译合适的代码。 -在实际应用中,如果一个类型的原子操作总是无锁的,我们可以更放心地在性能关键的代码路径中使用它。例如,在高频交易系统、实时系统或者其它需要高并发性能的场景中,无锁的原子操作可以显著减少锁的开销和争用,提高系统的吞吐量和响应时间。 +在实际应用中,如果一个类型的原子操作总是无锁的,我们可以更放心地在性能关键的代码路径中使用它。例如,在高频交易系统、实时系统或者其它需要高并发性能的场景中,无锁的原子操作可以显著减少锁的开销和竞争,提高系统的吞吐量和响应时间。 另一方面,如果发现某些原子类型在目标平台上是有锁的,我们可以考虑以下优化策略: @@ -111,6 +113,8 @@ else { 2. **减少原子操作的频率**:通过批处理等技术,减少对原子操作的调用次数。 3. **使用更高效的同步机制**:在一些情况下,其它同步机制(如读写锁)可能比原子操作更高效。 +当然,其实很多时候根本没这种性能的担忧,我们很多时候使用原子对象只是为了简单方便,比如 `std::atomic` 表示状态、`std::atomic` 进行计数等。即使它们是用了锁,那也是封装好了的,起码用着方便,而不需要在代码中引入额外的互斥量来保护,更加简洁。这也是很正常的需求,各位不但要考虑程序的性能,同时也要考虑代码的简洁性、易用性。即使使用原子类型无法带来效率的提升,那也没有负提升。 + --- 除了直接使用 `std::atomic` 模板外,也可以使用原子类型的别名。这个数量非常之多,见 [MSVC STL](https://github.com/microsoft/STL/blob/daeb0a6/stl/inc/atomic#L2745-L2805)。 @@ -224,7 +228,7 @@ struct trivial_type { > > 最后强调一下:任何 [std::atomic](https://zh.cppreference.com/w/cpp/atomic/atomic) 类型,**初始化不是原子操作**。 -### `st::atomic_flag` +### `std::atomic_flag` `std::atomic_flag` 是最简单的原子类型,这个类型的对象可以在两个状态间切换:**设置(true)**和**清除(false)**。它很简单,通常只是用作构建一些库设施,不会单独使用或直接面向普通开发者。 @@ -271,6 +275,12 @@ bool r = f.test_and_set(); 有限的特性使得 `std::atomic_flag` 非常适合用作制作**自旋锁**。 +> 自旋锁可以理解为一种***忙等锁***,因为它在等待锁的过程中不会主动放弃 CPU,而是持续检查锁的状态。 +> +> 与此相对,`std::mutex` 互斥量是一种***睡眠锁***。当线程请求锁(`lock()`)而未能获取时,它会放弃 CPU 时间片,让其他线程得以执行,从而有效利用系统资源。 +> +> 从性能上看,自旋锁的响应更快,但是睡眠锁更加节省资源,高效。 + ```cpp class spinlock_mutex { std::atomic_flag flag{}; @@ -301,7 +311,7 @@ void f(){ 稍微聊一下原理,我们的 `spinlock_mutex` 对象中存储的 `flag` 对象在默认构造时是清除 (`false`) 状态。在 `lock()` 函数中调用 `test_and_set` 函数,它是原子的,只有一个线程能成功调用并将 `flag` 的状态原子地更改为设置 (`true`),并返回它先前的值 (`false`)。此时,该线程成功获取了锁,退出循环。 -当 `flag` 对象的状态为设置 (`true`) 时,其线程调用 `test_and_set` 函数会返回 `true`,导致它们继续在循环中自旋,无法退出。直到先前持有锁的线程调用 `unlock()` 函数,将 `flag` 对象的状态原子地更改为清除 (`false`) 状态。此时,等待的线程中会有一个线程成功调用 `test_and_set` 返回 `false`,然后退出循环,成功获取锁。 +当 `flag` 对象的状态为设置 (`true`) 时,其它线程调用 `test_and_set` 函数会返回 `true`,导致它们继续在循环中自旋,无法退出。直到先前持有锁的线程调用 `unlock()` 函数,将 `flag` 对象的状态原子地更改为清除 (`false`) 状态。此时,等待的线程中会有一个线程成功调用 `test_and_set` 返回 `false`,然后退出循环,成功获取锁。 > 值得注意的是,我们只是稍微的讲一下使用 `std::atomic_flag` 实现自旋锁。不过并不推荐各位在实践中使用它,具体可参见 [**Linus Torvalds**](https://en.wikipedia.org/wiki/Linus_Torvalds) 的[文章](https://www.realworldtech.com/forum/?threadid=189711&curpostid=189723)。其中有一段话说得很直接: > @@ -330,7 +340,7 @@ b = false; ```cpp std::atomicb {true}; auto& ref = (b = false); // 假设返回 atomic 引用 -bool flag = ref.load(); // 必须显式调用 load() 加载 +bool flag = ref.load(); // 那就必须显式调用 load() 加载 ``` 通过返回非原子值进行赋值,可以避免多余的加载(load)过程,得到实际存储的值。 @@ -580,7 +590,7 @@ void reader() { 不过事实上 `std::atomic` 的功能相当有限,单看它提供的修改接口(`=`、`store`、`load`、`exchang`)就能明白。如果要操作其保护的共享指针指向的资源还是得 `load()` 获取底层共享指针的副本。此时再进行操作时就得考虑 `std::shared_ptr` 本身在多线程的支持了。 -[^3]: 不用感到奇怪,之所以多个线程通过 shared_ptr 的副本可以调用一切成员函数,甚至包括非 const 的成员函数 `operator=`、`reset`,是因为 ``shared_ptr` 的**控制块是线程安全的**。 +[^3]: 不用感到奇怪,之所以多个线程通过 shared_ptr 的副本可以调用一切成员函数,甚至包括非 const 的成员函数 `operator=`、`reset`,是因为 `shared_ptr` 的**控制块是线程安全的**。 --- @@ -666,7 +676,7 @@ void wake_up(){ 你们可能还有疑问:“**单线程能不能指令重排**?” -CPU 的指令重排必须遵循一定的规则,以确保程序的可观察副作用不受影响。对于单线程程序,CPU 会保证外部行为的一致性。对于多线程程序,需要开发者使用同步原语来显式地控制内存操作的顺序和可见性,确保多线程环境下的正确性。而标准库中提供的原子对象的原子操作,还可以设置内存次序。 +CPU 的指令重排必须遵循一定的规则,以确保程序的**可观察副作用**不受影响。对于单线程程序,CPU 会保证外部行为的一致性。对于多线程程序,需要开发者使用同步原语来显式地控制内存操作的顺序和可见性,确保多线程环境下的正确性。而标准库中提供的原子对象的原子操作,还可以设置内存次序。 那有没有可能: @@ -682,3 +692,127 @@ print("end"); // 2 不禁止就是有可能,但是我们无需在乎,**就算真的 CPU 将 end 重排到 start 前面了,也得在可观测行为发生前回溯了**。所以我一直在强调,这些东西,**我们无需在意**。 好了,到此,基本认识也就足够了,以上的示例更多的是泛指,知道其表达的意思就好,这些还是简单直接且符合直觉的。 + +### 可见 + +**可见** 是 C++ 多线程并发编程中的一个重要概念,它描述了一个线程中的数据修改对其他线程的可见程度。具体来说,如果线程 A 对变量 x 进行了修改,那么**其他线程 B 是否能够看到线程 A 对 x 的修改**,就涉及到可见的问题。 + +在讨论多线程的内存模型和执行顺序时,虽然经常会提到 CPU 重排、编译器优化、缓存等底层细节,但真正核心的概念是*可见*,而不是这些底层实现细节。 + +**C++ 标准中的可见**: + +- 如果线程 A 对变量 x 进行了修改,而线程 B 能够读取到线程 A 对 x 的修改,那么我们说线程 B 能看到线程 A 对 x 的修改。也就是说,线程 A 的修改对线程 B 是***可见***的。 + +C++ 标准通过内存序(memory order)来定义如何确保这种*可见*,而不必直接关心底层的 CPU 和编译器的具体行为。内存序提供了操作之间的顺序关系,确保即使存在 CPU 重排、编译器优化或缓存问题,线程也能正确地看到其他线程对共享数据的修改。 + +例如,通过使用合适的内存序(如 memory_order_release 和 memory_order_acquire),可以确保线程 A 的写操作在其他线程 B 中是可见的,从而避免数据竞争问题。 + +总结: + +- *可见* 关注的是线程之间的数据一致性,而不是底层的实现细节。 + +- 使用 C++ 的内存序机制可以确保数据修改的可见,而不必过多关注具体的 CPU 和编译器行为。 + +这种描述方式可以帮助更清楚地理解和描述多线程并发编程中如何通过 C++ 标准的内存模型来确保线程之间的数据一致性,而无需太多关注底层细节。 + +--- + +我知道各位肯定有疑问,我们大多数时候写多线程代码都从来没使用过内存序,一般都是互斥量、条件变量等高级同步设施,这没有可见性的问题吗? + +没有,这些设施自动确保数据的可见性。例如: `std::mutex` 的 `unlock()` 保证: + +- 此操作*同步于*任何后继的取得同一互斥体所有权的锁定操作。 + +也就是 [`unlock()`](https://zh.cppreference.com/w/cpp/thread/mutex/unlock) *同步于* `lock()`。 + +“*同步于*”:操作 A 的完成会确保操作 B 在其之后的执行中,能够看到操作 A 所做的所有修改。 + +也就是说: + +- `std::mutex` 的 `unlock()` 操作*同步于*任何随后的 `lock()` 操作。这意味着,线程在调用 `unlock()` 时,对共享数据的修改会对之后调用 `lock()` 的线程*可见*。 + +### `std::memory_order` + +`std::memory_order` 是一个枚举类型,用来指定原子操作的内存顺序,影响这些操作的行为。 + +```cpp +typedef enum memory_order { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst +} memory_order; + +// C++20 起则为: + +enum class memory_order : /* 未指明 */ { + relaxed, consume, acquire, release, acq_rel, seq_cst +}; +inline constexpr memory_order memory_order_relaxed = memory_order::relaxed; +inline constexpr memory_order memory_order_consume = memory_order::consume; +inline constexpr memory_order memory_order_acquire = memory_order::acquire; +inline constexpr memory_order memory_order_release = memory_order::release; +inline constexpr memory_order memory_order_acq_rel = memory_order::acq_rel; +inline constexpr memory_order memory_order_seq_cst = memory_order::seq_cst; +``` + +这 6 个常量,每一个常量都表示不同的内存次序。 + +大体来说我们可以将它们分为三类。 + +1. `memory_order_relaxed` 宽松定序:不是定序约束,**仅对此操作要求原子性**。 +2. `memory_order_seq_cst` 序列一致定序,这是库中所有原子操作的**默认行为**,也是**最严格的内存次序**,是**绝对安全**的。 + +剩下的就是第三类。 + +### 其它概念 + + + +### `x86` 和 `ARM` 的内存模型:强一致性与弱一致性 + + + +**内存模型是软件与实现之间的一种约定契约**。它定义了在多线程或并发环境中,如何对内存操作的顺序和一致性进行规范,以确保程序的正确性和可靠性。 + +C++ 标准为我们定义了 C++ 标准内存模型,使我们能够无需关心底层硬件环境就编写出跨平台的应用程序。不过,了解底层硬件架构的内存模型对扩展知识面和深入理解编程细节也非常有帮助。 + +最经典与常见的两种 CPU 指令集架构就是:`x86` 与 `ARM`。 + +- `x86` 架构:是一种复杂指令集计算([CISC](https://zh.wikipedia.org/wiki/%E8%A4%87%E9%9B%9C%E6%8C%87%E4%BB%A4%E9%9B%86%E9%9B%BB%E8%85%A6))架构,因其强大的性能被广泛应用于桌面电脑、笔记本电脑和服务器中。`x86` 架构采用的是 TSO(Total Store Order)[**内存一致性模型**](https://jamesbornholt.com/blog/memory-models/),是一种**强一致性模型**,**简化了多线程编程中的内存同步问题**(后文中会提到)。 + +- `ARM` 架构:是一种精简指令集计算([RISC](https://zh.wikipedia.org/wiki/%E7%B2%BE%E7%AE%80%E6%8C%87%E4%BB%A4%E9%9B%86%E8%AE%A1%E7%AE%97%E6%9C%BA))架构,因其高能效和低功耗特点广泛应用于移动设备、嵌入式系统和物联网设备中。`ARM` 架构采用的是**弱序内存模型**([weakly-ordered memory](https://developer.arm.com/documentation/102336/0100/Memory-ordering)),允许**更灵活**的内存优化,但这需要程序员使用内存屏障等机制来确保正确性。 + +这两种架构在设计理念和应用领域上存在显著差异,这也是它们在不同应用场景中表现出色的原因。 + +如果你从事嵌入式系统或者学术研究等,可能也听说过 `RISC-V` 架构,它目前在国内的应用也逐渐增多。 + +RISC-V 是一种开源的精简指令集计算(RISC)架构,旨在提供一种高效、模块化且开放的指令集。与 x86 和 ARM 架构不同,RISC-V 的设计目标是简化指令集,同时保持高度的灵活性和扩展性。它在内存模型方面也有自己独特的特性。 + +RISC-V 采用的也是**弱序内存模型**(weakly-ordered memory model),这与 x86 的强一致性模型(TSO)和 ARM 的弱一致性模型有所不同。你可能会有疑问: + +- `ARM` 和 `RISC-V` 都是弱序内存模型,为什么不同? + +各位一定要区分,这种强弱其实也只是一种分类而已,不同的指令集架构大多都还是有所不同的,并不会完全一样。例如: `x86` 的 TSO(Total Store Order)是强一致性模型的一种,但并不是所有强一致性模型都是 TSO。 + +### 宽松定序 + +### 释放-获取定序 + +### 释放-消费定序 + +### 序列一致定序 + +### 与 `volatile` 的关系 + + diff --git "a/md/06\345\215\217\347\250\213.md" "b/md/06\345\215\217\347\250\213.md" index a87de6b1..eeb2e994 100644 --- "a/md/06\345\215\217\347\250\213.md" +++ "b/md/06\345\215\217\347\250\213.md" @@ -1,7 +1,23 @@ # 协程 +## 前言 + 既然是“**现代**” C++ 并发编程教程,怎么能不聊协程呢? -C++20 引入了协程语法,新增了三个用作协程的关键字:`co_await`、`co_yield`、`co_return`。但并未给出标准**协程库**。协程库在 C++23 被引入。 +C++20 引入了协程语法,新增了三个用作协程的关键字:`co_await`、`co_yield`、`co_return`。但并未给出标准**协程库**,协程库在 C++23 被引入。 + +希望您拥有 `gcc14`、`clang19`,`Visual Studio 2022 17.11`。 + +我们假设您对 C++20 的协程一无所知、假设您对协程这个概念一无所知、假设您不了解其它语言的协程实现(如 Python、java)。 + +--- + +绝大多数人对协程基本可以说是一无所知,但是应该都听过这个名字,大概是因为这些编程语言都在新版本中引入它作为核心语言特性。 + +这带来了许多的热度,不过这并不完全算是好事,许多的营销号一样的讲述,基本全部都是错误的。 + +据我所知,在我在 B站发布正经 C++20 协程的教学视频之前,几乎所有打着 C++ 旗号说什么协程的,都是胡言乱语。不过也有一些不错的,如:[**等疾风**](https://space.bilibili.com/35186937)、[**happyyang的百草园**](https://space.bilibili.com/312883756),都出过至少算作正经的 C++20 协程的教学视频。 + +- **C++20 的协程是复杂的**。 -希望您拥有 `gcc14`、`clang18`,最新的 MSVC。 +不管是使用上还是概念上,引入了许多新颖的做法。 diff --git a/md/README.md b/md/README.md index c195ac73..0c942e8f 100644 --- a/md/README.md +++ b/md/README.md @@ -21,6 +21,8 @@   我们的代码风格较为简洁明了,命名全部使用下划线连接,而不是驼峰命名法。花括号通常只占一行,简短的代码可以不额外占行。一般初始化时使用 `{}`,而非 `()` 或者 `=` 。这样简单直观,避免歧义和许多问题。`#include` 引入头文件时需要在尖括号或引号前后加空格。 ```cpp +#include + struct move_only{ move_only() { std::puts("默认构造"); } move_only(move_only&&)noexcept { std::puts("移动构造"); } diff --git "a/md/\350\257\246\347\273\206\345\210\206\346\236\220/01thread\347\232\204\346\236\204\351\200\240\344\270\216\346\272\220\347\240\201\350\247\243\346\236\220.md" "b/md/\350\257\246\347\273\206\345\210\206\346\236\220/01thread\347\232\204\346\236\204\351\200\240\344\270\216\346\272\220\347\240\201\350\247\243\346\236\220.md" index 89785d3a..cf67dabb 100644 --- "a/md/\350\257\246\347\273\206\345\210\206\346\236\220/01thread\347\232\204\346\236\204\351\200\240\344\270\216\346\272\220\347\240\201\350\247\243\346\236\220.md" +++ "b/md/\350\257\246\347\273\206\345\210\206\346\236\220/01thread\347\232\204\346\236\204\351\200\240\344\270\216\346\272\220\347\240\201\350\247\243\346\236\220.md" @@ -131,7 +131,7 @@ void _Start(_Fn&& _Fx, _Args&&... _Ax) { 4. `constexpr auto _Invoker_proc = _Get_invoke<_Tuple>(make_index_sequence<1 + sizeof...(_Args)>{})` - - 调用 [`_Get_invoke`](https://github.com/microsoft/STL/blob/8e2d724cc1072b4052b14d8c5f81a830b8f1d8cb/stl/inc/thread#L65-L68) 函数,传入 `_Tuple` 类型和一个参数序列的索引序列(为了遍历形参包)。这个函数用于获取一个函数指针,指向了一个静态成员函数 [`_Invoke`](https://github.com/microsoft/STL/blob/8e2d724cc1072b4052b14d8c5f81a830b8f1d8cb/stl/inc/thread#L55-L63),用来实际执行线程。这两个函数都非常的简单,我们来看看: + - 调用 [`_Get_invoke`](https://github.com/microsoft/STL/blob/8e2d724cc1072b4052b14d8c5f81a830b8f1d8cb/stl/inc/thread#L65-L68) 函数,传入 `_Tuple` 类型和一个参数序列的索引序列(为了遍历形参包)。这个函数用于获取一个函数指针,指向了一个静态成员函数 [`_Invoke`](https://github.com/microsoft/STL/blob/8e2d724cc1072b4052b14d8c5f81a830b8f1d8cb/stl/inc/thread#L55-L63),它是线程实际执行的函数。这两个函数都非常的简单,我们来看看: ```cpp template @@ -202,3 +202,40 @@ void _Start(_Fn&& _Fx, _Args&&... _Ax) { 我们这里的源码解析涉及到的 C++ 技术很多,我们也没办法每一个都单独讲,那会显得文章很冗长,而且也不是重点。 相信你也感受到了,**不会模板,你阅读标准库源码,是无稽之谈**,市面上很多教程教学,教导一些实现容器,过度简化了,真要去出错了去看标准库的代码,那是不现实的。不需要模板的水平有多高,也不需要会什么元编程,但是基本的需求得能做到,得会,这里推荐一下:[**现代C++模板教程**](https://github.com/Mq-b/Modern-Cpp-templates-tutorial)。 + +--- + +学习完了也不要忘记了回答最初的问题: + +1. **如何做到的默认按值复制?** + + `_Start` 的第一行代码展示了这一点。我们将传入的所有参数包装成一个元组类型,这些类型先经过 `decay_t` 处理,去除了引用与 cv 限定,自然就实现了默认复制。 + + ```cpp + using _Tuple = tuple, decay_t<_Args>...>; + ``` + +2. **为什么需要 `std::ref` ?** + + 实现中将类型先经过 `decay` 处理,如果要传递引用,则必须用类包装一下才行,使用 `std::ref` 函数就会返回一个包装对象。 + +3. **如何支持只能移动的对象?** + + 参数通过完美转发,最终调用时使用 `std::move`,这在线程实际执行的函数 `_Invoke` 中体现出来: + + ```cpp + _STD invoke(_STD move(_STD get<_Indices>(_Tup))...); + ``` + +4. 如何做到接受任意[可调用](https://zh.cppreference.com/w/cpp/named_req/Callable)对象? + + 源码的实现很简单,主要是通过两层包装,最终将 `void*` 指针转换到原类型,然后使用 `std::invoke` 进行调用。 + +5. **如何创建的线程?** + + MSVC STL 调用 Win32 API `_beginthreadex` 创建线程;libstdc++ 调用 `__gthread_create` 函数创建线程,在 Windows 上实际上就是调用 `CreateThread`。 + `_beginthreadex` 和 `CreateThread` 都是微软提供的用于创建线程的 C 风格接口,它们的主要区别在于前者依赖于 C 运行时库,而后者更适合纯 Windows API 的情况。使用 `_beginthreadex` 可以确保正确初始化和清理 C 运行时库资源,而 `CreateThread` 则适用于不依赖于 C 运行时库的环境。 + +6. **传递参数一节中的:“*`std::thread` 内部会将保有的参数副本转换为右值表达式进行传递*”到底是如何做到的?** + + 这就是第三个问题,差不多,无非是最后调用 `std::invoke` 函数之前,先 `std::move` 了。 diff --git "a/md/\350\257\246\347\273\206\345\210\206\346\236\220/03async\344\270\216future\346\272\220\347\240\201\350\247\243\346\236\220.md" "b/md/\350\257\246\347\273\206\345\210\206\346\236\220/03async\344\270\216future\346\272\220\347\240\201\350\247\243\346\236\220.md" index 82dcb6dd..85fb2db5 100644 --- "a/md/\350\257\246\347\273\206\345\210\206\346\236\220/03async\344\270\216future\346\272\220\347\240\201\350\247\243\346\236\220.md" +++ "b/md/\350\257\246\347\273\206\345\210\206\346\236\220/03async\344\270\216future\346\272\220\347\240\201\350\247\243\346\236\220.md" @@ -1,2 +1,794 @@ # `st::async` 与 `std::future` 源码解析 +## 前言 + +和之前一样的,我们以 MSVC STL 的实现进行讲解。 + +`std::future`,即未来体,是用来管理一个共享状态的类模板,用于等待关联任务的完成并获取其返回值。它自身不包含状态,需要通过如 `std::async` 之类的函数进行初始化。`std::async` 函数模板返回一个已经初始化且具有共享状态的 `std::future` 对象。 + +因此,所有操作的开始应从 `std::async` 开始讲述。 + +需要注意的是,它们的实现彼此之间会共用不少设施,在讲述 `std::async` 源码的时候,对于 `std::future` 的内容同样重要。 + +> MSVC STL 很早之前就不支持 C++11 了,它的实现完全基于 **C++14**,出于某些原因 **C++17** 的一些库(如 [`invoke`](https://zh.cppreference.com/w/cpp/utility/functional/invoke), _v 变量模板)被向后移植到了 **C++14** 模式,所以即使是 C++11 标准库设施,实现中可能也是使用到了 C++14、17 的东西。 +> +> 注意,不用感到奇怪。 + +## `std::async` + +```cpp +_EXPORT_STD template +_NODISCARD_ASYNC future<_Invoke_result_t, decay_t<_ArgTypes>...>> async( + launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args) { + // manages a callable object launched with supplied policy + using _Ret = _Invoke_result_t, decay_t<_ArgTypes>...>; + using _Ptype = typename _P_arg_type<_Ret>::type; + _Promise<_Ptype> _Pr( + _Get_associated_state<_Ret>(_Policy, _Fake_no_copy_callable_adapter<_Fty, _ArgTypes...>( + _STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...))); + + return future<_Ret>(_From_raw_state_tag{}, _Pr._Get_state_for_future()); +} + +_EXPORT_STD template +_NODISCARD_ASYNC future<_Invoke_result_t, decay_t<_ArgTypes>...>> async( + _Fty&& _Fnarg, _ArgTypes&&... _Args) { + // manages a callable object launched with default policy + return _STD async(launch::async | launch::deferred, _STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...); +} +``` + +这段代码最直观的信息是,函数模板 `std::async` 有两个重载,其中第二个重载只是给了一个执行策略并将参数全部转发,调用第一个重载。也就是不指明执行策略的时候就会匹配到第二个重载版本。因此我们也只需要关注第二个版本了。 + +1. **模板参数和函数体外部信息**: + + - `_EXPOPT_STD` 是一个宏,当 `_BUILD_STD_MODULE` 宏定义且启用了 C++20 时,会被定义为 `export`,以便导出模块;否则它为空。 + + - `_Fty` 表示可调用对象的类型。 + - `_ArgTypes` 是一个类型形参包,表示调用该可调用对象所需的参数类型。 + - `_NODISCARD_ASYNC` 是一个宏,表示属性 `[[nodiscard]]`,用于标记此函数的返回值不应被忽略。 + +2. 函数返回类型: + + ```cpp + future<_Invoke_result_t, decay_t<_ArgTypes>...>> + ``` + + 虽然看起来复杂,但实际上是通过 `_Invoke_result_t` 获取可调用对象的返回类型。与标准库中的 [`std::invoke_result_t`](https://zh.cppreference.com/w/cpp/types/result_of) 基本相同。 + + 可以举一个使用 `std::invoke_result_t` 的例子: + + ```cpp + template + std::future,std::decay_t...>> + test_fun(Fty&& f,ArgTypes&&... args){ + return std::async(std::launch::async, std::forward(f), std::forward(args)...); + } + + auto result = test_fun([](int) {return 1; }, 1); // std::future + ``` + + 值得注意的是,所有类型在传递前都进行了 [`decay`](https://zh.cppreference.com/w/cpp/types/decay) 处理,也就是说不存在引用类型,是默认按值传递与 `std::thread` 的行为一致。 + +3. 函数形参: + + ```cpp + async(launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args) + ``` + + `launch _Policy`: 表示任务的执行策略,可以是 `launch::async`(表示异步执行)或 `launch::deferred`(表示延迟执行),或者两者的组合。 + + `_Fty&& _Fnarg`: 可调用对象,通过完美转发机制将其转发给实际的异步任务。 + + `_ArgTypes&&... _Args`: 调用该可调用对象时所需的参数,同样通过完美转发机制进行转发。 + +4. `using _Ret = _Invoke_result_t, decay_t<_ArgTypes>...>;` + + `using _Ptype = typename _P_arg_type<_Ret>::type;` + + - 定义 `_Ret` 类型别名,它是使用 `_ArgTypes` 类型参数调用 `_Fty` 类型的可调用对象后得到的结果类型。也就是我们传入的可调用对象的返回类型;同样使用了 `_Invoke_result_t`(等价于 [`std::invoke_result_t`](https://zh.cppreference.com/w/cpp/types/result_of) ) 与 `decay_t`。 + + - 其实 `_Ptype` 的定义确实在大多数情况下和 `_Ret` 是相同的,类模板 _P_arg_type 只是为了处理引用类型以及 void 的情况,参见 `_P_arg_type` 的实现: + + ```cpp + template + struct _P_arg_type { // type for functions returning T + using type = _Fret; + }; + + template + struct _P_arg_type<_Fret&> { // type for functions returning reference to T + using type = _Fret*; + }; + + template <> + struct _P_arg_type { // type for functions returning void + using type = int; + }; + ``` + + `_Ptype`:处理异步任务返回值的方式类型,它在语义上强调了异步任务返回值的处理方式,具有不同的实现逻辑和使用场景。在当前我们难以直接展示它的作用,不过可以推测,这个“`P`” 表示的是后文将使用的 `_Promise` 类模板。也就是说,定义 `_Ptype` 是为了配合 `_Promise` 的使用。我们将会在后文详细探讨 `_Promise` 类型的内部实现,并进一步解释 `_Ptype` 的具体作用。 + +5. `_Promise<_Ptype> _Pr` + + [`_Promise`](https://github.com/microsoft/STL/blob/f54203f/stl/inc/future#L999-L1055) 类型本身不重要,很简单,关键还在于其存储的数据成员。 + + ```cpp + template + class _Promise { + public: + _Promise(_Associated_state<_Ty>* _State_ptr) noexcept : _State(_State_ptr, false), _Future_retrieved(false) {} + + _Promise(_Promise&&) = default; + + _Promise& operator=(_Promise&&) = default; + + void _Swap(_Promise& _Other) noexcept { + _State._Swap(_Other._State); + _STD swap(_Future_retrieved, _Other._Future_retrieved); + } + + const _State_manager<_Ty>& _Get_state() const noexcept { + return _State; + } + _State_manager<_Ty>& _Get_state() noexcept { + return _State; + } + + _State_manager<_Ty>& _Get_state_for_set() { + if (!_State.valid()) { + _Throw_future_error2(future_errc::no_state); + } + + return _State; + } + + _State_manager<_Ty>& _Get_state_for_future() { + if (!_State.valid()) { + _Throw_future_error2(future_errc::no_state); + } + + if (_Future_retrieved) { + _Throw_future_error2(future_errc::future_already_retrieved); + } + + _Future_retrieved = true; + return _State; + } + + bool _Is_valid() const noexcept { + return _State.valid(); + } + + bool _Is_ready() const noexcept { + return _State._Is_ready(); + } + + _Promise(const _Promise&) = delete; + _Promise& operator=(const _Promise&) = delete; + + private: + _State_manager<_Ty> _State; + bool _Future_retrieved; + }; + ``` + + `_Promise` 类模板是对 **[`_State_manager`](https://github.com/microsoft/STL/blob/f54203f/stl/inc/future#L688-L825) 类模板的包装**,并增加了一个表示状态的成员 `_Future_retrieved`。 + + 状态成员用于跟踪 `_Promise` 是否已经调用过 `_Get_state_for_future()` 成员函数;它默认为 `false`,在**第一次**调用 `_Get_state_for_future()` 成员函数时被置为 `true`,如果二次调用,就会抛出 [`future_errc::future_already_retrieved`](https://zh.cppreference.com/w/cpp/thread/future_errc) 异常。 + + > 这类似于 `std::promise` 调用 [`get_future()`](https://zh.cppreference.com/w/cpp/thread/promise/get_future) 成员函数。[测试](https://godbolt.org/z/8anc9b3PT)。 + + `_Promise` 的构造函数接受的却不是 `_State_manager` 类型的对象,而是 `_Associated_state` 类型的指针,用来初始化数据成员 `_State`。 + + ```cpp + _State(_State_ptr, false) + ``` + + 这是因为实际上 `_State_manager` 类型的[实现](https://github.com/microsoft/STL/blob/f54203f/stl/inc/future#L822-L824)就是保有了 [`Associated_state`](https://github.com/microsoft/STL/blob/f54203f/stl/inc/future#L198-L445) 指针,以及一个状态成员: + + ```cpp + private: + _Associated_state<_Ty>* _Assoc_state = nullptr; + bool _Get_only_once = false; + ``` + + 也可以简单理解 `_State_manager` 又是对 `Associated_state` 的包装,其中的大部分接口实际上是调用 `_Assoc_state` 的成员函数,如: + + ```cpp + void wait() const { // wait for signal + if (!valid()) { + _Throw_future_error2(future_errc::no_state); + } + + _Assoc_state->_Wait(); + } + ``` + + - **一切的重点,最终在 `Associated_state` 上**。 + + 然而它也是最为复杂的,我们在讲 `std::thread`-构造源码解析 中提到过一句话: + + > - **了解一个庞大的类,最简单的方式就是先看它的数据成员有什么**。 + + ```cpp + public: + conditional_t, _Ty, _Result_holder<_Ty>> _Result; + exception_ptr _Exception; + mutex _Mtx; + condition_variable _Cond; + bool _Retrieved; + int _Ready; + bool _Ready_at_thread_exit; // TRANSITION, ABI + bool _Has_stored_result; + bool _Running; + ``` + + 这是 `Associated_state` 的数据成员,其中有许多的 `bool` 类型的状态成员,同时最为明显重要的三个设施是:**异常指针**、**互斥量**、**条件变量**。 + + 根据这些数据成员我们就能很轻松的猜测出 `Associated_state` 模板类的作用和工作方式。 + + - **异常指针**:用于存储异步任务中可能发生的异常,以便在调用 `future::get` 时能够重新抛出异常。 + - **互斥量和条件变量**:用于在异步任务和等待任务之间进行同步。当异步任务完成时,条件变量会通知等待的任务。 + + **`_Associated_state` 模板类负责管理异步任务的状态,包括结果的存储、异常的处理以及任务完成的通知。它是实现 `std::future` 和 `std::promise` 的核心组件之一**,通过 `_State_manager` 和 `_Promise` 类模板对其进行封装和管理,提供更高级别的接口和功能。 + + ```plaintext + +---------------------+ + | _Promise<_Ty> | + |---------------------| + | - _State | -----> +---------------------+ + | - _Future_retrieved | | _State_manager<_Ty> | + +---------------------+ |----------------------| + | - _Assoc_state | -----> +-------------------------+ + | - _Get_only_once | | _Associated_state<_Ty>* | + +----------------------+ +-------------------------+ + ``` + + 上图是 `_Promise`、_`State_manager`、`_Associated_state` 之间的**包含关系示意图**,理解这个关系对我们后面**非常重要**。 + + 到此就可以了,我们不需要在此处就详细介绍这三个类,但是你需要大概的看一下,这非常重要。 + +6. 初始化 `_Promie` 对象: + + `_Get_associated_state<_Ret>(_Policy, _Fake_no_copy_callable_adapter<_Fty, _ArgTypes...>(_STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...))` + + 很明显,这是一个函数调用,将我们 `std::async` 的参数全部转发给它,它是重要而直观的。 + + [`_Get_associated_state`](https://github.com/microsoft/STL/blob/f54203f/stl/inc/future#L1400-L1410) 函数根据启动模式(`launch`)来决定创建的异步任务状态对象类型: + + ```cpp + template + _Associated_state::type>* _Get_associated_state(launch _Psync, _Fty&& _Fnarg) { + // construct associated asynchronous state object for the launch type + switch (_Psync) { // select launch type + case launch::deferred: + return new _Deferred_async_state<_Ret>(_STD forward<_Fty>(_Fnarg)); + case launch::async: // TRANSITION, fixed in vMajorNext, should create a new thread here + default: + return new _Task_async_state<_Ret>(_STD forward<_Fty>(_Fnarg)); + } + } + ``` + + `_Get_associated_state` 函数返回一个 `_Associated_state` 指针,该指针指向一个新的 `_Deferred_async_state` 或 `_Task_async_state` 对象。这两个类分别对应于异步任务的两种不同执行策略:**延迟执行**和**异步执行**。 + + > 其实就是父类指针指向了子类对象,注意 **`_Associated_state` 是有虚函数的**,子类进行覆盖,这很重要。比如在后续聊 `std::future` 的 `get()` 成员函数的时候就会讲到 + + > 这段代码也很好的说明在 MSVC STL 中,`launch::async | launch::deferred` 和 `launch::async` 的行为是相同的,即都是异步执行。 + + --- + + **[`_Task_async_state`](https://github.com/microsoft/STL/blob/f54203f/stl/inc/future#L654-L686) 与 `_Deferred_async_state` 类型** + + ```cpp + template + class _Task_async_state : public _Packaged_state<_Rx()> + template + class _Deferred_async_state : public _Packaged_state<_Rx()> + ``` + + `_Task_async_state` 与 `_Deferred_async_state` 都继承自 [`_Packaged_state`](https://github.com/microsoft/STL/blob/f54203f/stl/inc/future#L462-L597),用于异步执行任务。它们的构造函数都接受一个函数对象,并将其转发给基类 `_Packaged_state` 的构造函数。 + + **`_Packaged_state` 类型只有一个数据成员 `std::function` 类型的对象 `_Fn`,它用来存储需要执行的异步任务**,而它又继承自 `_Associated_state`。 + + ```cpp + template + class _Packaged_state<_Ret(_ArgTypes...)> + : public _Associated_state<_Ret> + ``` + + ```mermaid + classDiagram + class _Associated_state { + ... + } + + class _Packaged_state { + -std::function _Fn + } + + class _Task_async_state { + -::Concurrency::task _Task + } + + class _Deferred_async_state { + } + + _Associated_state <|-- _Packaged_state : 继承 + _Packaged_state <|-- _Task_async_state : 继承 + _Packaged_state <|-- _Deferred_async_state : 继承 + + ``` + + 我们直接先看 `_Task_async_state` 与 `_Deferred_async_state` 类型的构造函数实现即可: + + ```cpp + template + _Task_async_state(_Fty2&& _Fnarg) : _Mybase(_STD forward<_Fty2>(_Fnarg)) { + _Task = ::Concurrency::create_task([this]() { // do it now + this->_Call_immediate(); + }); + + this->_Running = true; + } + template + _Deferred_async_state(const _Fty2& _Fnarg) : _Packaged_state<_Rx()>(_Fnarg) {} + + template + _Deferred_async_state(_Fty2&& _Fnarg) : _Packaged_state<_Rx()>(_STD forward<_Fty2>(_Fnarg)) {} + ``` + + `_Task_async_state` 它的数据成员: + + ```cpp + private: + ::Concurrency::task _Task; + ``` + + `_Task_async_state` 的实现使用到了微软自己实现的 [并行模式库](https://learn.microsoft.com/zh-cn/cpp/parallel/concrt/parallel-patterns-library-ppl?view=msvc-170)(PPL),简而言之 `launch::async` 策略并不是单纯的创建线程让任务执行,而是使用了微软的 `::Concurrency::create_task` ,它从**线程池**中获取线程并执行任务返回包装对象。 + + `this->_Call_immediate();` 是调用 `_Task_async_state` 的父类 `_Packaged_state` 的成员函数 `_Call_immediate` 。 + + **`_Packaged_state` 有三个偏特化**,`_Call_immediate` 自然也拥有三个不同版本,用来应对我们传入的函数对象**返回类型**的三种情况: + + - 返回普通类型 [`_Packaged_state<_Ret(_ArgTypes...)>`](https://github.com/microsoft/STL/blob/f54203f/stl/inc/future#L554) + + ```cpp + void _Call_immediate(_ArgTypes... _Args) { + _TRY_BEGIN + // 调用函数对象并捕获异常 传递返回值 + this->_Set_value(_Fn(_STD forward<_ArgTypes>(_Args)...), false); + _CATCH_ALL + // 函数对象抛出异常就记录 + this->_Set_exception(_STD current_exception(), false); + _CATCH_END + } + ``` + + - 返回引用类型 [`_Packaged_state<_Ret&(_ArgTypes...)>`](https://github.com/microsoft/STL/blob/f54203f/stl/inc/future#L510) + + ```cpp + void _Call_immediate(_ArgTypes... _Args) { + _TRY_BEGIN + // 调用函数对象并捕获异常 传递返回值的地址 + this->_Set_value(_STD addressof(_Fn(_STD forward<_ArgTypes>(_Args)...)), false); + _CATCH_ALL + // 函数对象抛出异常就记录 + this->_Set_exception(_STD current_exception(), false); + _CATCH_END + } + ``` + + - 返回 void 类型 [`_Packaged_state`](https://github.com/microsoft/STL/blob/f54203f/stl/inc/future#L554) + + ```cpp + void _Call_immediate(_ArgTypes... _Args) { // call function object + _TRY_BEGIN + // 调用函数对象并捕获异常 因为返回 void 不获取返回值 而是直接 _Set_value 传递一个 1 + _Fn(_STD forward<_ArgTypes>(_Args)...); + this->_Set_value(1, false); + _CATCH_ALL + // 函数对象抛出异常就记录 + this->_Set_exception(_STD current_exception(), false); + _CATCH_END + } + ``` + + 说白了,无非是把返回引用类型的可调用对象返回的引用获取地址传递给 `_Set_value`,把返回 void 类型的可调用对象传递一个 1 表示正确执行的状态给 `_Set_value`。 + + `_Call_immediate` 则又调用了父类 `_Associated_state` 的成员函数(`_Set_value`、`_set_exception`),传递的可调用对象执行结果,以及可能的异常,将结果或异常存储在 `_Associated_state` 中。 + + `_Deferred_async_state` 并不会在线程中执行任务,但它同样调用 `_Call_immediate` 函数执行保有的函数对象,它有一个 `_Run_deferred_function` 函数: + + ```cpp + void _Run_deferred_function(unique_lock& _Lock) override { // run the deferred function + _Lock.unlock(); + _Packaged_state<_Rx()>::_Call_immediate(); + _Lock.lock(); + } + ``` + + 然后也就和上面说的没什么区别了 。 + +7. 返回 `std::future` + + `return future<_Ret>(_From_raw_state_tag{}, _Pr._Get_state_for_future());` + + 它选择到了 `std::future` 的构造函数是: + + ```cpp + future(_From_raw_state_tag, const _Mybase& _State) noexcept : _Mybase(_State, true) {} + ``` + + > ```cpp + > using _Mybase = _State_manager<_Ty*>; + > ``` + + `_From_raw_state_tag` 是一个空类,并没有什么特殊作用,只是为了区分重载。 + + `_Get_state_for_future` 代码如下: + + ```cpp + _State_manager<_Ty>& _Get_state_for_future() { + if (!_State.valid()) { + _Throw_future_error2(future_errc::no_state); + } + + if (_Future_retrieved) { + _Throw_future_error2(future_errc::future_already_retrieved); + } + + _Future_retrieved = true; + return _State; + } + ``` + + 检查状态,修改状态,返回底层 `_State` ,完成转移状态。 + + 总而言之这行代码通过调用 `std::future` 的特定构造函数,将 `_Promise` 对象中的 `_State_manager` 状态转移到 `std::future` 对象中,从而创建并返回一个 `std::future` 对象。这使得 `std::future` 可以访问并管理异步任务的状态,包括获取任务的结果或异常,并等待任务的完成。 + +## `std::future` + +先前的 `std::async` 的内容非常之多,希望各位开发者不要搞晕了,其实重中之重主要是那几个类,关系图如下: + +```plaintext ++---------------------+ +| _Promise<_Ty> | +|---------------------| +| - _State | -----> +---------------------+ +| - _Future_retrieved | | _State_manager<_Ty> | ++---------------------+ |----------------------| + | - _Assoc_state | -----> +-------------------------+ + | - _Get_only_once | | _Associated_state<_Ty>* | + +----------------------+ +-------------------------+ +``` + +> `_Promise`、_`State_manager`、`_Associated_state` 之间的**包含关系示意图**。 + +```mermaid +classDiagram + class _Associated_state { + ... + } + + class _Packaged_state { + -std::function _Fn + } + + class _Task_async_state { + -::Concurrency::task _Task + } + + class _Deferred_async_state { + } + + _Associated_state <|-- _Packaged_state : 继承 + _Packaged_state <|-- _Task_async_state : 继承 + _Packaged_state <|-- _Deferred_async_state : 继承 + +``` + +> `_Asscociated_state`、`_Packaged_state`、`_Task_async_state`、`_Deferred_async_state` **继承关系示意图**。 + +这其中的 `_Associated_state`、`_State_manager` 类型是我们的核心,它在后续 `std::future` 乃至其它并发设施都有众多使用。 + +--- + +介绍 `std::future` 的源码我认为无需过多篇幅或者示例,引入过多的源码实现等等从头讲解,只会让各位开发者感觉复杂难。 + +我们直接从它的最重要、常见的 `get()`、`wait()` 成员函数开始即可。 + +```cpp +std::future future = std::async([] { return 0; }); +future.get(); +``` + +我们先前已经详细介绍过了 `std::async` 返回 `std::future` 的步骤。以上这段代码,唯一的问题是:*`future.get()` 做了什么?* + +```cpp +_EXPORT_STD template +class future : public _State_manager<_Ty> { + // class that defines a non-copyable asynchronous return object that holds a value +private: + using _Mybase = _State_manager<_Ty>; + +public: + static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>, + "T in future must meet the Cpp17Destructible requirements (N4950 [futures.unique.future]/4)."); + + future() = default; + + future(future&& _Other) noexcept : _Mybase(_STD move(_Other), true) {} + + future& operator=(future&&) = default; + + future(_From_raw_state_tag, const _Mybase& _State) noexcept : _Mybase(_State, true) {} + + _Ty get() { + // block until ready then return the stored result or throw the stored exception + future _Local{_STD move(*this)}; + return _STD move(_Local._Get_value()); + } + + _NODISCARD shared_future<_Ty> share() noexcept { + return shared_future<_Ty>(_STD move(*this)); + } + + future(const future&) = delete; + future& operator=(const future&) = delete; +}; +``` + +> `std::future` 其实还有两种特化,不过整体大差不差。 +> +> ```cpp +> template +> class future<_Ty&> : public _State_manager<_Ty*> +> ``` +> +> ```cpp +> template <> +> class future : public _State_manager +> ``` +> +> 也就是对返回类型为引用和 void 的情况了。其实先前已经聊过很多次了,无非就是内部的返回引用实际按指针操作,返回 void,那么也得给个 1。参见前面的 `_Call_immediate` 实现。 + +可以看到 `std::future` 整体代码实现很少,很简单,那是因为其实现细节都在其父类 `_State_manager` 。然而 `_State_manager` 又保有一个 `_Associated_state<_Ty>*` 类型的成员。而 **`_Associated_state` 又是一切的核心**,之前已经详细描述过了。 + +阅读 `std::future` 的源码你可能注意到了一个问题:*没有 `wait()`*成员函数?* + +它的定义来自于父类 `_State_manager` : + +```cpp +void wait() const { // wait for signal + if (!valid()) { + _Throw_future_error2(future_errc::no_state); + } + + _Assoc_state->_Wait(); +} +``` + +然而这还不够,实际上还需要调用了 `_Associated_state` 的 `wait()` 成员函数: + +```cpp +virtual void _Wait() { // wait for signal + unique_lock _Lock(_Mtx); + _Maybe_run_deferred_function(_Lock); + while (!_Ready) { + _Cond.wait(_Lock); + } +} +``` + +先使用锁进行保护,然后调用函数,再循环等待任务执行完毕。`_Maybe_run_deferred_function`: + +```cpp +void _Maybe_run_deferred_function(unique_lock& _Lock) { // run a deferred function if not already done + if (!_Running) { // run the function + _Running = true; + _Run_deferred_function(_Lock); + } +} +``` + +`_Run_deferred_function` 相信你不会陌生,在讲述 `std::async` 源码中其实已经提到了,就是解锁然后调用 `_Call_immediate` 罢了。 + +```cpp +void _Run_deferred_function(unique_lock& _Lock) override { // run the deferred function + _Lock.unlock(); + _Packaged_state<_Rx()>::_Call_immediate(); + _Lock.lock(); +} +``` + +> `_Call_immediate` 就是执行我们实际传入的函数对象,先前已经提过。 + +在 `_Wait` 函数中调用 `_Maybe_run_deferred_function` 是为了确保延迟执行(`launch::deferred`)的任务能够在等待前被启动并执行完毕。这样,在调用 `wait` 时可以正确地等待任务完成。 + +至于下面的循环等待部分: + +```cpp +while (!_Ready) { + _Cond.wait(_Lock); +} +``` + +这段代码使用了条件变量、互斥量、以及一个状态对象,主要目的有两个: + +1. **避免虚假唤醒**: + - 条件变量的 `wait` 函数在被唤醒后,会重新检查条件(即 `_Ready` 是否为 `true`),确保只有在条件满足时才会继续执行。这防止了由于虚假唤醒导致的错误行为。 +2. **等待 `launch::async` 的任务在其它线程执行完毕**: + - 对于 `launch::async` 模式的任务,这段代码确保当前线程会等待任务在另一个线程中执行完毕,并接收到任务完成的信号。只有当任务完成并设置 `_Ready` 为 `true` 后,条件变量才会被通知,从而结束等待。 + +这样,当调用 `wait` 函数时,可以保证无论任务是 `launch::deferred` 还是 `launch::async` 模式,当前线程都会正确地等待任务的完成信号,然后继续执行。 + +--- + +`wait()` 介绍完了,那么接下来就是 `get()` : + +```cpp +// std::future +void get() { + // block until ready then return or throw the stored exception + future _Local{_STD move(*this)}; + _Local._Get_value(); +} +// std::future +_Ty get() { + // block until ready then return the stored result or throw the stored exception + future _Local{_STD move(*this)}; + return _STD move(_Local._Get_value()); +} +// std::future +_Ty& get() { + // block until ready then return the stored result or throw the stored exception + future _Local{_STD move(*this)}; + return *_Local._Get_value(); +} +``` + +在第四章的 “*future 的状态变化*”一节中我们也详细聊过 `get()` 成员函数。由于 future 本身有三个特化,`get()` 成员函数自然那也有三个版本,不过总体并无多大区别。 + +它们都是将当前对象(`*this`)的共享状态转移给了这个局部对象 `_Local`,然后再去调用父类`_State_manager` 的成员函数 `_Get_value()` 获取值并返回。而局部对象 `_Local` 在函数结束时析构。这意味着当前对象(`*this`)失去共享状态,并且状态被完全销毁。 + +`_Get_value()` : + +```cpp +_Ty& _Get_value() const { + if (!valid()) { + _Throw_future_error2(future_errc::no_state); + } + + return _Assoc_state->_Get_value(_Get_only_once); +} +``` + +先进行一下状态判断,如果拥有共享状态则继续,调用 `_Assoc_state` 的成员函数 `_Get_value` ,传递 `_Get_only_once` 参数,其实就是代表这个成员函数只能调用一次,次参数是里面进行状态判断的而已。 + +`_Assoc_state` 的类型是 ` _Associated_state<_Ty>* ` ,是一个指针类型,它实际会指向自己的子类对象,我们在讲 `std::async` 源码的时候提到了,它必然指向 `_Deferred_async_state` 或者 `_Task_async_state`。 + +`_Assoc_state->_Get_value` 这其实是个多态调用,父类有这个虚函数: + +```cpp +virtual _Ty& _Get_value(bool _Get_only_once) { + unique_lock _Lock(_Mtx); + if (_Get_only_once && _Retrieved) { + _Throw_future_error2(future_errc::future_already_retrieved); + } + + if (_Exception) { + _STD rethrow_exception(_Exception); + } + + // TRANSITION: `_Retrieved` should be assigned before `_Exception` is thrown so that a `future::get` + // that throws a stored exception invalidates the future (N4950 [futures.unique.future]/17) + _Retrieved = true; + _Maybe_run_deferred_function(_Lock); + while (!_Ready) { + _Cond.wait(_Lock); + } + + if (_Exception) { + _STD rethrow_exception(_Exception); + } + + if constexpr (is_default_constructible_v<_Ty>) { + return _Result; + } else { + return _Result._Held_value; + } +} + +``` + +但是子类 `_Task_async_state` 进行了重写,以 `launch::async` 策略创建的 future,那么实际会调用 `_Task_async_state::_Get_value` : + +```cpp +_State_type& _Get_value(bool _Get_only_once) override { + // return the stored result or throw stored exception + _Task.wait(); + return _Mybase::_Get_value(_Get_only_once); +} +``` + +> `_Deferred_async_state` 则没有进行重写,就是直接调用父类虚函数。 + +`_Task` 就是 `::Concurrency::task _Task;`,调用 `wait()` 成员函数确保任务执行完毕。 + +`_Mybase::_Get_value(_Get_only_once)` 其实又是回去调用父类的虚函数了。 + +> ### `_Get_value` 方法详细解释 + +1. **状态检查**: + - 如果`_Get_only_once`为真并且结果已被检索过,则抛出`future_already_retrieved`异常。 +2. **异常处理**: + - 如果存在存储的异常,重新抛出该异常。 +3. **标记结果已被检索**: + - 将`_Retrieved`设置为`true`。 +4. **执行延迟函数**: + - 调用`_Maybe_run_deferred_function`来运行可能的延迟任务。这个函数很简单,就是单纯的执行延时任务而已,在讲述 `wait` 成员函数的时候已经讲完了。 +5. **等待结果就绪**: + - 如果结果尚未准备好,等待条件变量通知结果已就绪。(这里和 `std::async` 和 `std::future` 的组合无关,因为如果是 `launch::async` 模式创建的任务,重写的 `_Get_value` 是先调用了 `_Task.wait();` 确保异步任务执行完毕,此处根本无需等待它) +6. **再次检查异常**: + - 再次检查是否有存储的异常,并重新抛出它。 +7. **返回结果**: + - 如果`_Ty`是默认可构造的,返回结果`_Result`。 + - 否则,返回`_Result._Held_value`。 + +`_Result` 是通过执行 `_Call_immediate` 函数,然后 `_Call_immediate` 再执行 `_Set_value` ,`_Set_value` 再执行 `_Emplace_result`,`_Emplace_result` 再执行 `_Emplace_result` 获取到我们执行任务的值的。以 `Ty` 的偏特化为例: + +```cpp +// _Packaged_state +void _Call_immediate(_ArgTypes... _Args) { + _TRY_BEGIN + // 调用函数对象并捕获异常 传递返回值 + this->_Set_value(_Fn(_STD forward<_ArgTypes>(_Args)...), false); + _CATCH_ALL + // 函数对象抛出异常就记录 + this->_Set_exception(_STD current_exception(), false); + _CATCH_END +} + +// _Asscoiated_state +void _Set_value(const _Ty& _Val, bool _At_thread_exit) { // store a result + unique_lock _Lock(_Mtx); + _Set_value_raw(_Val, &_Lock, _At_thread_exit); +} +void _Set_value_raw(const _Ty& _Val, unique_lock* _Lock, bool _At_thread_exit) { + // store a result while inside a locked block + if (_Already_has_stored_result()) { + _Throw_future_error2(future_errc::promise_already_satisfied); + } + + _Emplace_result(_Val); + _Do_notify(_Lock, _At_thread_exit); +} +template +void _Emplace_result(_Ty2&& _Val) { + // TRANSITION, incorrectly assigns _Result when _Ty is default constructible + if constexpr (is_default_constructible_v<_Ty>) { + _Result = _STD forward<_Ty2>(_Val); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + } else { + ::new (static_cast(_STD addressof(_Result._Held_value))) _Ty(_STD forward<_Ty2>(_Val)); + _Has_stored_result = true; + } +} +``` + +## 总结 + +好了,到此也就可以了。 + +你不会期待我们将每一个成员函数都分析一遍吧?首先是没有必要,其次是篇幅限制。 + +`std::future` 的继承关系让人感到头疼,但是如果耐心的看了一遍,全部搞明白了继承关系, `std::async` 如何创建的 `std::future` 也就没有问题了。 + +其实各位不用着急完全理解,可以慢慢看,至少有许多的显著的信息,比如: + +1. `sttd::future` 的很多部分,如 `get()` 成员函数实现中,实际使用了虚函数。 +2. `std::async` 创建 `std::future` 对象中,内部其实也有父类指针指向子类对象,以及多态调用。 +3. `std::async` 的非延迟执行策略,使用到了自家的 PPL 库。 +4. 微软的 `std::async` 策略实现并不符合标准,不区分 `launch::async | launch::deferred` 和 `launch::async`。 +5. `std::future` 内部使用到了互斥量、条件变量、异常指针等设施。 diff --git "a/md/\350\257\246\347\273\206\345\210\206\346\236\220/04\347\272\277\347\250\213\346\261\240.md" "b/md/\350\257\246\347\273\206\345\210\206\346\236\220/04\347\272\277\347\250\213\346\261\240.md" new file mode 100644 index 00000000..89aea9b2 --- /dev/null +++ "b/md/\350\257\246\347\273\206\345\210\206\346\236\220/04\347\272\277\347\250\213\346\261\240.md" @@ -0,0 +1,523 @@ +# 线程池 + +## 前言 + +我相信,已经阅读到这里的各位,不会对“*线程池*”这个词感到陌生。大部分开发者早就自己使用、学习,乃至实现过线程池。那不如我们先来进行一下基础的名词解释。 + +- ***什么叫线程池?*** + +“**线程**”没什么好说的,是 CPU 调度的最小单位,也是操作系统的一种抽象资源。 + +“**池**”?水池装着水,线程池则是装着线程,是一种抽象的指代。 + +抽象的来说,可以当做是一个池子中存放了一堆线程,**故称作线程池**。简而言之,线程池是指代一组**预先创建的**、**可以复用的线程集合**。这些线程由线程池管理,用于执行多个任务而**无需频繁地创建和销毁**线程。 + +```mermaid +graph TD + subgraph 线程池 + 线程1 + 线程2 + 线程3 + 线程4 + 线程5 + ... + end + + 任务队列[任务队列] + 调度器 --> 线程1 + 调度器 --> 线程2 + 调度器 --> 线程3 + 调度器 --> 线程4 + 调度器 --> 线程5 + + 任务1 --> 调度器 + 任务2 --> 调度器 + 任务3 --> 调度器 + 任务4 --> 调度器 + 任务5 --> 调度器 + 任务6 --> 调度器 + 任务7 --> 调度器 + + 线程1 --> 执行任务1[执行任务1] + 线程2 --> 执行任务2[执行任务2] + 线程3 --> 执行任务3[执行任务3] + 线程4 --> 执行任务4[执行任务4] + 线程5 --> 执行任务5[执行任务5] + + 执行任务1 --> 休眠1[休眠等待] + 执行任务2 --> 休眠2[休眠等待] + 执行任务3 --> 休眠3[休眠等待] + 执行任务4 --> 休眠4[休眠等待] + 执行任务5 --> 休眠5[休眠等待] + + 任务6 --> 调度器 --> 唤醒线程1[唤醒线程1] + 唤醒线程1 -.-> 线程1 + 线程1 --> 执行任务6[执行任务6] + 执行任务6 --> 休眠1 + + 任务7 --> 调度器 --> 唤醒线程2[唤醒线程2] + 唤醒线程2 -.-> 线程2 + 线程2 --> 执行任务7[执行任务7] + 执行任务7 --> 休眠2 + +``` + +> 这是一个典型的线程池结构。线程池包含一个**任务队列**,当有新任务加入时,调度器会将任务分配给线程池中的空闲线程进行执行。线程在执行完任务后会进入**休眠状态**,等待**调度器**的下一次**唤醒**。当有新的任务加入队列,并且有线程处于休眠状态时,调度器会唤醒休眠的线程,并分配新的任务给它们执行。线程执行完新任务后,会再次进入休眠状态,直到有新的任务到来,调度器**才可能**会再次唤醒它们。 +> +> 图中线程1 就是被调度器分配了任务1,执行完毕后休眠,然而新任务的到来让调度器再次将它唤醒,去执行任务6,执行完毕后继续休眠。 + +使用线程池的益处我们已经加粗了,然而这其实并不是“*线程池*”独有的,任何创建和销毁存在较大开销的设施,都可以进行所谓的“***池化***”。 + +常见的还有:**套接字连接池**、**数据库连接池**、**内存池**、**对象池**。 + +--- + +了解以上这些基础概念是第一步也是最后一步,随着水平的提升,对这些概念的理解也会逐渐提升。 + +## 市面上常见的线程池 + +在了解了线程池的基本概念与运行逻辑后,我们不用着急就尝试实现。我们可以先来聊一聊,使用一下市面上常见的那些 C++ 线程池设施,了解它们提供的功能,接口设计的方式。 + +### `boost::asio::thread_pool` + +[`boost::asio::thread_pool`](https://think-async.com/Asio/asio-1.11.0/doc/asio/reference/thread_pool.html) 是 [`Boost.Asio`](https://www.boost.org/doc/libs/1_85_0/doc/html/boost_asio.html) 库提供的一种线程池实现。 + +> Asio 是一个跨平台的 C++ 库,用于**网络**和低级 I/O 编程,使用 **现代C++** 方法为开发人员提供一致的异步模型。 + +使用方法: + +1. 创建线程池对象,指定或让 Asio 自动决定线程数量。 + +2. 提交任务:通过 [`boost::asio::post`](https://beta.boost.org/doc/libs/1_82_0/doc/html/boost_asio/reference/post.html) 函数模板提交任务到线程池中。 + +3. 阻塞,直到池中的线程完成任务。 + +```cpp +#include +#include + +std::mutex m; + +void print_task(int n) { + std::lock_guard lc{ m }; + std::cout << "Task " << n << " is running on thr: " << + std::this_thread::get_id() << '\n'; +} + +int main() { + boost::asio::thread_pool pool{ 4 }; // 创建一个包含 4 个线程的线程池 + + for (int i = 0; i < 10; ++i) { + boost::asio::post(pool, [i] { print_task(i); }); + } + + pool.join(); // 等待所有任务执行完成 +} +``` + +> [运行](https://godbolt.org/z/41445Kab5)测试。 + +- 创建线程池时,指定线程数量,线程池会创建对应数量的线程。 + +- 使用 `boost::asio::post` 提交任务,任务会被添加到任务队列中。 + +- 线程池中的线程会从任务队列中取出任务并执行,任务执行完毕后,线程继续取下一个任务或者休眠。 + +- 调用 join 方法等待所有任务执行完毕并关闭线程池。 + +如果我们不自己指明线程池的线程数量,那么 Asio 会根据函数 [`default_thread_pool_size`](https://github.com/boostorg/asio/blob/44238d033e1503c694782925d647811380a067c2/include/boost/asio/impl/thread_pool.ipp#L53-L58) 计算并返回一个**线程池的默认线程数量**。它根据系统的硬件并发能力来决定使用的线程数,通常是硬件并发能力的两倍。 + +```cpp +inline long default_thread_pool_size() +{ + std::size_t num_threads = thread::hardware_concurrency() * 2; + num_threads = num_threads == 0 ? 2 : num_threads; + return static_cast(num_threads); +} + +thread_pool::thread_pool() + : scheduler_(add_scheduler(new detail::scheduler(*this, 0, false))), + num_threads_(detail::default_thread_pool_size()) +``` + +代码很简单,就是 `thread::hardware_concurrency() * 2` 而已,至于下面的判断是因为 `std::thread::hardware_concurrency()` 在某些特殊情况下可能返回 `0`(例如硬件并发能力无法被检测时),那那将 `num_threads` 设置为 2,确保线程池至少有 2 个线程。 + +--- + +Boost.Asio 的线程池对象在[析构](https://github.com/boostorg/asio/blob/44238d033e1503c694782925d647811380a067c2/include/boost/asio/impl/thread_pool.ipp#L98-L103)时会自动调用相关的清理方法,但你也可以手动进行控制。 + +```cpp +thread_pool::~thread_pool() +{ + stop(); // 停止接收新任务 + join(); // 等待所有线程完成 + shutdown(); // 最终清理,释放资源 +} +``` + +- `stop` :修改内部的标志位存在使得线程池能够识别何时需要停止接收新的任务,以及关闭还没开始执行的任务,然后唤醒所有线程。 +- `join()` :等待所有线程完成它们的工作,确保所有线程都已终止。 +- `shutdown()` :进行最终的清理,释放资源,确保线程池的完全清理和资源的正确释放 + +> 此处可阅读部分源码,帮助理解与记忆 + +析构函数先调用了 `stop()` ,然后再进行 `join()` 。那如果我们没有提前显式调用 `join()` 成员函数,**可能导致一些任务没有执行,析构函数并不会等待所有任务执行完毕**: + +```cpp +boost::asio::thread_pool pool{ 4 }; + +for (int i = 0; i < 10; ++i) { + boost::asio::post(pool, [i] { print_task(i); }); +} +``` + +> [运行](https://godbolt.org/z/MPoxrY9Yo)测试。 + +因为析构函数并不是阻塞直到执行完所有任务,而是先**停止**,再 `join()` 以及 `shutdown()`。 + +`Boost.Asio` 提供的线程池使用十分简单,接口高度封装,几乎无需关心底层具体实现,易于使用。 + +我们的操作几乎只需创建线程池对象、将任务加入线程池、在需要时调用 `join()`。 + +```cpp +boost::asio::thread_pool pool{4}; // 创建线程池 +boost::asio::post(pool, task); // 将任务加入线程池 +pool.join(); // 等待任务完成 (或者析构自动调用) +``` + +### `QThreadPool` + +[`QThreadPool`](https://doc.qt.io/qt-6/qthreadpool.html) 是 Qt 提供的线程池实现,它是用来管理自家的 `QThreads` 的集合。 + +```cpp +#include +#include +#include +#include + +struct MyTask : public QRunnable{ + void run() override { + qDebug() << "🐢🐢🐢🐢🐢"; + } +}; + +int main(int argc, char *argv[]){ + QCoreApplication app(argc, argv); + + QThreadPool *threadPool = QThreadPool::globalInstance(); + + // 线程池最大线程数 + qDebug()<< threadPool->maxThreadCount(); + + for (int i = 0; i < 10; ++i) { + MyTask *task = new MyTask{}; + threadPool->start(task); + } + // 当前活跃线程数 10 + qDebug()<activeThreadCount(); + + app.exec(); +} +``` + +与 `Asio.thread_pool` 不同,`QThreadPool` 采用单例模式,通过静态成员函数 `QThreadPool::globalInstance()` 获取对象实例(不过也可以自己创建)。默认情况下,`QThreadPool` 线程池的最大线程数为当前硬件支持的并发线程数,例如在我的硬件上为 `20`,这点也和 `Asio.thread_pool` 不同。 + +`QThreadPool` 依赖于 Qt 的事件循环,因此我们使用了 `QCoreApplication`。 + +而将任务添加到线程池中的做法非常古老原始,我们需要**自定义一个类型继承并重写虚函数 `run`**,创建任务对象,然后将任务对象传递给线程池的 `start` 方法。 + +> 这种方法过于原始,如果读者学过 `java` 相信也不会陌生。我们实现的线程池不会是如此。 + +在 Qt6,引入了一个 [`start`](https://doc.qt.io/qt-6/qthreadpool.html#start-1) 的重载版本: + +```cpp +template > +void QThreadPool::start(Callable &&functionToRun, int priority) +{ + start(QRunnable::create(std::forward(functionToRun)), priority); +} +``` + +它相当于是对[`start` 原始版本](https://doc.qt.io/qt-5/qthreadpool.html#start)的: + +```cpp +void start(QRunnable *runnable, int priority = 0); +``` + +> [源码](https://codebrowser.dev/qt6/qtbase/src/corelib/thread/qthreadpool.cpp.html#_ZN11QThreadPool5startEP9QRunnablei)。 + +进行的一个**包装**,以支持任何的[*可调用(*Callable*)*](https://zh.cppreference.com/w/cpp/named_req/Callable)类型,而无需再繁琐的继承重写 `run` 函数。 + +```cpp +threadPool->start([=]{ + qDebug()< - “*普通的能够满足日常开发需*求的” +> +> 其实绝大部分开发者使用线程池,只是为了不重复多次创建线程罢了。所以只需要一个提供一个外部接口,可以传入任务到任务队列,然后安排线程去执行。无非是使用条件变量、互斥量、原子标志位,这些东西,就足够编写一个满足绝大部分业务需求的线程池。 + +我们先编写一个**最基础的**线程池,首先确定它的数据成员: + +```cpp +class ThreadPool { + std::mutex mutex_; + std::condition_variable cv_; + std::atomic stop_; + std::atomic num_threads_; + std::queue tasks_; + std::vector pool_; +}; +``` + +1. **`std::mutex mutex_`** + - 用于保护共享资源(如任务队列)在多线程环境中的访问,避免数据竞争。 + +2. **`std::condition_variable cv_`** + - 用于线程间的同步,允许线程等待特定条件(如新任务加入队列)并在条件满足时唤醒线程。 + +3. **`std::atomic stop_`** + - 指示线程池是否停止。 + +4. **`std::atomic num_threads_`** + - 表示线程池中的线程数量。 + +5. **`std::queue tasks_`** + - 任务队列,存储等待执行的任务,任务按提交顺序执行。 + +6. **`std::vector pool_`** + + - 线程容器,存储管理线程对象,每个线程从任务队列中获取任务并执行。 + +**标头依赖**: + +```cpp +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +``` + +提供构造析构函数,以及一些外部接口:`submit()`、`start()`、`stop()`、`join()`,也就完成了: + +```cpp +inline std::size_t default_thread_pool_size()noexcept { + std::size_t num_threads = std::thread::hardware_concurrency() * 2; + num_threads = num_threads == 0 ? 2 : num_threads; + return num_threads; +} + +class ThreadPool { +public: + using Task = std::packaged_task; + + ThreadPool(const ThreadPool&) = delete; + ThreadPool& operator=(const ThreadPool&) = delete; + + ThreadPool(std::size_t num_thread = default_thread_pool_size()) + : stop_{ false }, num_threads_{ num_thread } { + start(); + } + + ~ThreadPool() { + stop(); + } + + void stop() { + stop_.store(true); + cv_.notify_all(); + for (auto& thread : pool_) { + if (thread.joinable()) { + thread.join(); + } + } + pool_.clear(); + } + + template + std::future, std::decay_t...>> submit(F&& f, Args&&...args) { + using RetType = std::invoke_result_t, std::decay_t...>; + if (stop_.load()) { + throw std::runtime_error("ThreadPool is stopped"); + } + + auto task = std::make_shared>( + std::bind(std::forward(f), std::forward(args)...)); + std::future ret = task->get_future(); + + { + std::lock_guard lc{ mutex_ }; + tasks_.emplace([task] {(*task)(); }); + } + cv_.notify_one(); + return ret; + } + + void start() { + for (std::size_t i = 0; i < num_threads_; ++i) { + pool_.emplace_back([this] { + while (!stop_) { + Task task; + { + std::unique_lock lc{ mutex_ }; + cv_.wait(lc, [this] {return stop_ || !tasks_.empty(); }); + if (tasks_.empty()) + return; + task = std::move(tasks_.front()); + tasks_.pop(); + } + task(); + } + }); + } + } + +private: + std::mutex mutex_; + std::condition_variable cv_; + std::atomic stop_; + std::atomic num_threads_; + std::queue tasks_; + std::vector pool_; +}; +``` + +**测试 demo**: + +```cpp +int main() { + ThreadPool pool{ 4 }; // 创建一个有 4 个线程的线程池 + std::vector> futures; // future 集合,获取返回值 + + for (int i = 0; i < 10; ++i) { + futures.emplace_back(pool.submit(print_task, i)); + } + + for (int i = 0; i < 10; ++i) { + futures.emplace_back(pool.submit(print_task2, i)); + } + + int sum = 0; + for (auto& future : futures) { + sum += future.get(); // get() 成员函数 阻塞到任务执行完毕,获取返回值 + } + std::cout << "sum: " << sum << '\n'; +} // 析构自动 stop() +``` + +**可能的[运行结果](https://godbolt.org/z/n7Tana59x)**: + +```shell +Task 0 is running on thr: 6900 +Task 1 is running on thr: 36304 +Task 5 is running on thr: 36304 +Task 3 is running on thr: 6900 +Task 7 is running on thr: 6900 +Task 2 is running on thr: 29376 +Task 6 is running on thr: 36304 +Task 4 is running on thr: 31416 +🐢🐢🐢 1 🐉🐉🐉 +Task 9 is running on thr: 29376 +🐢🐢🐢 0 🐉🐉🐉 +Task 8 is running on thr: 6900 +🐢🐢🐢 2 🐉🐉🐉 +🐢🐢🐢 6 🐉🐉🐉 +🐢🐢🐢 4 🐉🐉🐉 +🐢🐢🐢 5 🐉🐉🐉 +🐢🐢🐢 3 🐉🐉🐉 +🐢🐢🐢 7 🐉🐉🐉 +🐢🐢🐢 8 🐉🐉🐉 +🐢🐢🐢 9 🐉🐉🐉 +sum: 90 +``` + +> 如果等待线程池对象调用析构函数,那么效果如同 `asio::thread_pool`,会先进行 `stop`,这可能导致一些任务无法执行。不过我们在最后**循环遍历了 `futures`**,调用 `get()` 成员函数,不存在这个问题。 + +它支持**任意可调用类型**,当然也包括非静态成员函数。我们使用了 [`std::decay_t`](https://zh.cppreference.com/w/cpp/types/decay),所以参数的传递其实是**按值复制**,而不是引用传递,这一点和大部分库的设计一致。示例如下: + +```cpp +struct X { + void f(const int& n) const { + std::osyncstream{ std::cout } << &n << '\n'; + } +}; + +int main() { + ThreadPool pool{ 4 }; // 创建一个有 4 个线程的线程池 + + X x; + int n = 6; + std::cout << &n << '\n'; + auto t = pool.submit(&X::f, &x, n); // 默认复制,地址不同 + auto t2 = pool.submit(&X::f, &x, std::ref(n)); + t.wait(); + t2.wait(); +} // 析构自动 stop() +``` + +> [运行](https://godbolt.org/z/vY458T44e)测试。 + +我们的线程池的 `submit` 成员函数在传递参数的行为上,与先前介绍的 `std::thread` 和 `std::async` 等设施基本一致。 + +我们稍微介绍线程池的接口: + + **构造函数和析构函数:** + +- **构造函数**:初始化线程池并**启动线程**。 + +- **析构函数**:停止线程池并等待所有线程结束。 + +**外部接口:** + +- **`stop()`**:停止线程池,通知所有线程退出(不会等待所有任务执行完毕)。 +- **`submit()`**:将任务提交到任务队列,并返回一个`std::future`对象用于获取任务结果以及确保任务执行完毕。 +- **`start()`**:启动线程池,创建并启动指定数量的线程。 + +我们并没有提供一个功能强大的所谓的“***调度器***”,我们只是利用条件变量和互斥量,让操作系统自行调度而已,它并不具备设置任务优先级之类的调度功能。 + +当然,你可能还希望我们的线程池具备更多功能或改进,比如控制任务优先级、设置最大线程数量、返回当前活跃线程数等。此外,异常处理也是一个值得考虑的方面。 + +有些功能实现起来非常简单,而有些则需要更多的思考和设计。不过,这些功能超出了本次讲解的范围。如果有兴趣,可以尝试自行优化我们提供的线程池实现,添加更多的功能。我们给出的线程池实现简单完善且直观,用来学习再好不过。 + +## 总结 + +在本章中我们详细的介绍了: + +- 线程池的基本概念。 + +- 市面上常见的线程池的设计与使用, `boost::asio::thread_pool`、`QThreadPool`。 +- 实现一个简易的线程池。 + +总体而言,内容并不构成太大的难度。 + +**课后作业**:自己实现一个线程池,可以参考我们给出的线程池实现增加功能,提交到 `homework` 文件夹中。 diff --git "a/md/\350\257\246\347\273\206\345\210\206\346\236\220/README.md" "b/md/\350\257\246\347\273\206\345\210\206\346\236\220/README.md" index 9f7fbd0f..f76617b3 100644 --- "a/md/\350\257\246\347\273\206\345\210\206\346\236\220/README.md" +++ "b/md/\350\257\246\347\273\206\345\210\206\346\236\220/README.md" @@ -1,3 +1,4 @@ # 详细分析 -放一些详细分析源码实现之类的内容。 +存放一些源码分析,以及一些其它非知识点,而是应用的造轮子(如线程池),或者其它扩展讲解。 + diff --git a/package-lock.json b/package-lock.json index 0285aea6..77a76001 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,19 +4,27 @@ "requires": true, "packages": { "": { + "dependencies": { + "chart.js": "^4.4.3", + "echarts": "^5.5.1", + "flowchart": "^1.2.0", + "flowchart.ts": "^3.0.0", + "katex": "^0.16.10", + "mermaid": "^10.9.1", + "vuepress-plugin-search-pro": "^2.0.0-rc.50" + }, "devDependencies": { - "@vuepress/bundler-vite": "2.0.0-rc.9", - "vue": "^3.4.23", - "vuepress": "2.0.0-rc.9", - "vuepress-plugin-search-pro": "^2.0.0-rc.39", - "vuepress-theme-hope": "2.0.0-rc.40" + "@vuepress/bundler-vite": "2.0.0-rc.14", + "@vuepress/plugin-search": "^2.0.0-rc.37", + "vue": "^3.4.27", + "vuepress": "2.0.0-rc.14", + "vuepress-theme-hope": "2.0.0-rc.50" } }, "node_modules/@babel/parser": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", - "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", - "dev": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -24,68 +32,348 @@ "node": ">=6.0.0" } }, - "node_modules/@docsearch/css": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", - "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==", - "dev": true, + "node_modules/@braintree/sanitize-url": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", + "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], "optional": true, - "peer": true + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/@docsearch/js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.6.0.tgz", - "integrity": "sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==", - "dev": true, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "optional": true, - "peer": true, - "dependencies": { - "@docsearch/react": "3.6.0", - "preact": "^10.0.0" + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/@docsearch/react": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", - "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", - "dev": true, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], "optional": true, - "peer": true, - "dependencies": { - "@algolia/autocomplete-core": "1.9.3", - "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.6.0", - "algoliasearch": "^4.19.1" - }, - "peerDependencies": { - "@types/react": ">= 16.8.0 < 19.0.0", - "react": ">= 16.8.0 < 19.0.0", - "react-dom": ">= 16.8.0 < 19.0.0", - "search-insights": ">= 1 < 3" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "search-insights": { - "optional": true - } + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" @@ -97,8 +385,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" }, "node_modules/@lit-labs/ssr-dom-shim": { "version": "1.2.0", @@ -116,171 +408,93 @@ } }, "node_modules/@mdit-vue/plugin-component": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-component/-/plugin-component-2.1.2.tgz", - "integrity": "sha512-n1HcAC82l912HhtiMSxl5pQLKBYbPok/IcdGRD49rTt53NXBqct68qo58+7jvsj+f8Lmo7kjD+em3tP4BSgl0A==", - "dev": true, + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-component/-/plugin-component-2.1.3.tgz", + "integrity": "sha512-9AG17beCgpEw/4ldo/M6Y/1Rh4E1bqMmr/rCkWKmCAxy9tJz3lzY7HQJanyHMJufwsb3WL5Lp7Om/aPcQTZ9SA==", "dependencies": { - "@types/markdown-it": "^14.0.1", + "@types/markdown-it": "^14.1.1", "markdown-it": "^14.1.0" } }, - "node_modules/@mdit-vue/plugin-component/node_modules/@types/markdown-it": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.0.1.tgz", - "integrity": "sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, "node_modules/@mdit-vue/plugin-frontmatter": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-frontmatter/-/plugin-frontmatter-2.1.2.tgz", - "integrity": "sha512-2YOVOsMRtf11bZ6mEB4xoWD6RG5X0Ex+g/1c1iXoYUMUahlZnz9flXUM6WAE++HsLR3Wkvd5FNhGUArrcxn0dA==", - "dev": true, + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-frontmatter/-/plugin-frontmatter-2.1.3.tgz", + "integrity": "sha512-KxsSCUVBEmn6sJcchSTiI5v9bWaoRxe68RBYRDGcSEY1GTnfQ5gQPMIsM48P4q1luLEIWurVGGrRu7u93//LDQ==", "dependencies": { "@mdit-vue/types": "2.1.0", - "@types/markdown-it": "^14.0.1", + "@types/markdown-it": "^14.1.1", "gray-matter": "^4.0.3", "markdown-it": "^14.1.0" } }, - "node_modules/@mdit-vue/plugin-frontmatter/node_modules/@types/markdown-it": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.0.1.tgz", - "integrity": "sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, "node_modules/@mdit-vue/plugin-headers": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-headers/-/plugin-headers-2.1.2.tgz", - "integrity": "sha512-YkBTlHeG4seTcXqrVPvPY6utvkHd2qs7QGU5aM3S5CxUH5l0/SzIRv+irhFMfIRsXQ7zwx2vJVSwJz+n2TFhSA==", - "dev": true, + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-headers/-/plugin-headers-2.1.3.tgz", + "integrity": "sha512-AcL7a7LHQR3ISINhfjGJNE/bHyM0dcl6MYm1Sr//zF7ZgokPGwD/HhD7TzwmrKA9YNYCcO9P3QmF/RN9XyA6CA==", "dependencies": { - "@mdit-vue/shared": "2.1.2", + "@mdit-vue/shared": "2.1.3", "@mdit-vue/types": "2.1.0", - "@types/markdown-it": "^14.0.1", + "@types/markdown-it": "^14.1.1", "markdown-it": "^14.1.0" } }, - "node_modules/@mdit-vue/plugin-headers/node_modules/@types/markdown-it": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.0.1.tgz", - "integrity": "sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==", - "dev": true, + "node_modules/@mdit-vue/plugin-sfc": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-sfc/-/plugin-sfc-2.1.3.tgz", + "integrity": "sha512-Ezl0dNvQNS639Yl4siXm+cnWtQvlqHrg+u+lnau/OHpj9Xh3LVap/BSQVugKIV37eR13jXXYf3VaAOP1fXPN+w==", "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@mdit-vue/types": "2.1.0", + "@types/markdown-it": "^14.1.1", + "markdown-it": "^14.1.0" } }, - "node_modules/@mdit-vue/plugin-sfc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-sfc/-/plugin-sfc-2.1.2.tgz", - "integrity": "sha512-wjbFvkUcCcfxc1x33SwqbWSM3WYPJOdlmX9IJQd9y6C7ALujy6Orx1gWn5j0hfke1kIuohvjeJ/K0LqF4oYO4g==", - "dev": true, + "node_modules/@mdit-vue/plugin-title": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-title/-/plugin-title-2.1.3.tgz", + "integrity": "sha512-XWVOQoZqczoN97xCDrnQicmXKoqwOjIymIm9HQnRXhHnYKOgJPW1CxSGhkcOGzvDU1v0mD/adojVyyj/s6ggWw==", "dependencies": { + "@mdit-vue/shared": "2.1.3", "@mdit-vue/types": "2.1.0", - "@types/markdown-it": "^14.0.1", + "@types/markdown-it": "^14.1.1", "markdown-it": "^14.1.0" } }, - "node_modules/@mdit-vue/plugin-sfc/node_modules/@types/markdown-it": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.0.1.tgz", - "integrity": "sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==", - "dev": true, + "node_modules/@mdit-vue/plugin-toc": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-toc/-/plugin-toc-2.1.3.tgz", + "integrity": "sha512-41Q+iXpLHZt0zJdApVwoVt7WF6za/xUjtjEPf90Z3KLzQO01TXsv48Xp9BsrFHPcPcm8tiZ0+O1/ICJO80V/MQ==", "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@mdit-vue/shared": "2.1.3", + "@mdit-vue/types": "2.1.0", + "@types/markdown-it": "^14.1.1", + "markdown-it": "^14.1.0" } }, - "node_modules/@mdit-vue/plugin-title": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-title/-/plugin-title-2.1.2.tgz", - "integrity": "sha512-BXgrpuRp6aI/CV/V3jH3zailG9rZH8f094RSky7PE2wTx3c2hnavz3SB3y3TVDv5UC7BGk/uPfBjdYh8/ejsSg==", - "dev": true, + "node_modules/@mdit-vue/shared": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@mdit-vue/shared/-/shared-2.1.3.tgz", + "integrity": "sha512-27YI8b0VVZsAlNwaWoaOCWbr4eL8B04HxiYk/y2ktblO/nMcOEOLt4p0RjuobvdyUyjHvGOS09RKhq7qHm1CHQ==", "dependencies": { - "@mdit-vue/shared": "2.1.2", "@mdit-vue/types": "2.1.0", - "@types/markdown-it": "^14.0.1", + "@types/markdown-it": "^14.1.1", "markdown-it": "^14.1.0" } }, - "node_modules/@mdit-vue/plugin-title/node_modules/@types/markdown-it": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.0.1.tgz", - "integrity": "sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "node_modules/@mdit-vue/plugin-toc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@mdit-vue/plugin-toc/-/plugin-toc-2.1.2.tgz", - "integrity": "sha512-G3t9NjhTvl5cYZ9VSsMzVkYjdUrC9nOZE+oxIzpUTtHRH8NtRvoynUDzEDzRnoRDe29cdWDKTAYaeiHUF+TAvQ==", - "dev": true, - "dependencies": { - "@mdit-vue/shared": "2.1.2", - "@mdit-vue/types": "2.1.0", - "@types/markdown-it": "^14.0.1", - "markdown-it": "^14.1.0" - } - }, - "node_modules/@mdit-vue/plugin-toc/node_modules/@types/markdown-it": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.0.1.tgz", - "integrity": "sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "node_modules/@mdit-vue/shared": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@mdit-vue/shared/-/shared-2.1.2.tgz", - "integrity": "sha512-5+YHKRyULDqMZsYq+8Ttev0P/osgAoNm2OPYrJtvxLfc1jyrZNiDUCjO2jec7Nk3qyGVZe6FKtXTNLVE+ZRhZw==", - "dev": true, - "dependencies": { - "@mdit-vue/types": "2.1.0", - "@types/markdown-it": "^14.0.1", - "markdown-it": "^14.1.0" - } - }, - "node_modules/@mdit-vue/shared/node_modules/@types/markdown-it": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.0.1.tgz", - "integrity": "sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, "node_modules/@mdit-vue/types": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@mdit-vue/types/-/types-2.1.0.tgz", - "integrity": "sha512-TMBB/BQWVvwtpBdWD75rkZx4ZphQ6MN0O4QB2Bc0oI5PC2uE57QerhNxdRZ7cvBHE2iY2C+BUNUziCfJbjIRRA==", - "dev": true + "integrity": "sha512-TMBB/BQWVvwtpBdWD75rkZx4ZphQ6MN0O4QB2Bc0oI5PC2uE57QerhNxdRZ7cvBHE2iY2C+BUNUziCfJbjIRRA==" }, "node_modules/@mdit/plugin-alert": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-alert/-/plugin-alert-0.8.0.tgz", - "integrity": "sha512-mxA/lhOyDDR6/qSAegGG/XZRjUbr1wjwdULudbpkA/CCQi6piW9D0Z8crDQGYz4KPQM9Bgx4Ac81QFSzHOV66Q==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-alert/-/plugin-alert-0.12.0.tgz", + "integrity": "sha512-4OyGK1PZrJbmEF/kS6GKmmG1nlN5h/CyIPZV8lRgnlWLFB37JiEz3EHusPAXAoMtw7VGNFaIcl7OT/I5yyz1JQ==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -289,19 +503,19 @@ } }, "node_modules/@mdit/plugin-align": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-align/-/plugin-align-0.8.0.tgz", - "integrity": "sha512-OJPYzSdmT0UZj/QTvnKYE4GelAL0OD8bNIPxpidXbFd3IqYv/8+xMjT6XeR+R3oZEvtbYSc2e1MmO5fo3DopJA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-align/-/plugin-align-0.12.0.tgz", + "integrity": "sha512-rvA+xzaVrlsr44s7XD/xadO3lF0QYWCbeSrOS2dhOroNCIOy4RotVP/1tQPr84eqm4oXcxXF0cbjFuwUgE1jYw==", "dev": true, "dependencies": { - "@mdit/plugin-container": "0.8.0", - "@types/markdown-it": "^13.0.7" + "@mdit/plugin-container": "0.12.0", + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -310,18 +524,18 @@ } }, "node_modules/@mdit/plugin-attrs": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-attrs/-/plugin-attrs-0.8.0.tgz", - "integrity": "sha512-ewmx5i+b3M4CRJNDpDNBA0YTHa1snn+adDsDDpDtPPSzCH1NhtWXdzwI0TrcCQUnueeSEEWX/wY4ESo+NRkBNQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-attrs/-/plugin-attrs-0.12.0.tgz", + "integrity": "sha512-J0MBwBq958lBtdIcEo02mUIO4ubl2YK+bY799T2SusrLTf3FZsq8+d/OiLTUtovfxaphD7F6yqo8M61AiOpq+w==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -330,18 +544,18 @@ } }, "node_modules/@mdit/plugin-container": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-container/-/plugin-container-0.8.0.tgz", - "integrity": "sha512-uWK3t0CWssintcmT5PTJVhAwbstcD+SrtijQKs6BhLRtGGgHJ9mOf0ybGjlJhn4077yFFTHmaCIT3K+n5ZVjPg==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-container/-/plugin-container-0.12.0.tgz", + "integrity": "sha512-61bWK1ek6Rn4o12/BIKTWgGU0miB9ENcXE19H5D4DRhwG5+4+0zp2U6hRLf/mE73+mRYin7iKVzcwwEsqs+u8w==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -350,15 +564,15 @@ } }, "node_modules/@mdit/plugin-demo": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-demo/-/plugin-demo-0.8.0.tgz", - "integrity": "sha512-yFRXnp3Lj0g4H9ImzHKQwwgtSykrL/BDNEQzql9fdA9FbSygfu0CIxfm+A8lsVos8cAvdsgxy3gILySxpfR89g==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-demo/-/plugin-demo-0.12.0.tgz", + "integrity": "sha512-+KDUOgcvnMtBN/uYWlhIFuWkTJexuxstq8ERy9q7vOiu8Go85qCb27h0RSToKBTmmGy+XqfU2EdJclYPWBupJQ==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -367,18 +581,18 @@ } }, "node_modules/@mdit/plugin-figure": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-figure/-/plugin-figure-0.8.0.tgz", - "integrity": "sha512-/o4RoKjnkdWc+K7m6mR7BAu2J79yYE38s8HUc8iKk9v+e9j1E+6LeXcpx1LoPnHzUhT4EO2QmUsv+kAaPFfZYw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-figure/-/plugin-figure-0.12.0.tgz", + "integrity": "sha512-3nfcGI+uM0f6AqHZrEr8kSMBI6T2+fKKQXtCbvWQqQ+P3iGgf34Ay2eAtuMDcDGqyfNuR6e8aLoOeY2QWuEynA==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -387,33 +601,33 @@ } }, "node_modules/@mdit/plugin-footnote": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-footnote/-/plugin-footnote-0.8.0.tgz", - "integrity": "sha512-AaX1rfkJwq9vLX+H/a+XQ3ZxahOXrnMLr5dVZfNdazjqdDEJ7Cc/A7UFtLfOM19F2w3EgvcHR1gbINxIVDn/eg==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-footnote/-/plugin-footnote-0.12.0.tgz", + "integrity": "sha512-9B+bJdMndCPoA9De9bxRm4/fyz02PHRcttOyuyPJ3G+wCAgIN1c/7CB8ViT1YJuECUjLogJQ/rrgqh7f0LTqLQ==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" } }, "node_modules/@mdit/plugin-img-lazyload": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-img-lazyload/-/plugin-img-lazyload-0.8.0.tgz", - "integrity": "sha512-Rrlf2FzOxxyszbv3DpkIwEgmYKmtwHdxIO+Whkn0a9QckxnEKkaGl5KARCnM7LqX2fhEyFLgnfkr3onVOJG54g==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-img-lazyload/-/plugin-img-lazyload-0.12.0.tgz", + "integrity": "sha512-6R42ieXzwkB5BKKZi+ZefqeP/fBG5qo7Sqtl72ewSVqEQ30bgxpk6nkrPI2orRob4tb6z0F/c+R8h6PW5MkTOw==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -422,18 +636,18 @@ } }, "node_modules/@mdit/plugin-img-mark": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-img-mark/-/plugin-img-mark-0.8.0.tgz", - "integrity": "sha512-4P6z2QOfLHLMSXUP4mB/2Rnd6KeHmJBkUXJWJhybcXoIG5S5FDTFHJxOycSP4eGzfdOYAWSlkx6XwXEUGGZz5w==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-img-mark/-/plugin-img-mark-0.12.0.tgz", + "integrity": "sha512-HkIUwlTg/xPsBi4PG+5dsMnsb7wdiJzELSCEUfdAJTg55nksonHfyV2pFpr87MML4nuZlZK9JHt+Bm2BBDSVSw==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -442,18 +656,18 @@ } }, "node_modules/@mdit/plugin-img-size": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-img-size/-/plugin-img-size-0.8.0.tgz", - "integrity": "sha512-r+LbAizP/hw5SisY44VbHEnR7XUKpcHM2k2fwu5wb1+V1crxeigG4sa8rzrJEddU+k6uCl27yL5FTGbHjAl82Q==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-img-size/-/plugin-img-size-0.12.0.tgz", + "integrity": "sha512-fCcF5gc+ba6gQ5ebrKuI8bK/gFbj8mbeN45FHmBsFDFsfTHa0Xij2v8iok0nP8YEIVj71y8XYojsqCWs6avong==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -462,16 +676,16 @@ } }, "node_modules/@mdit/plugin-include": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-include/-/plugin-include-0.8.0.tgz", - "integrity": "sha512-e8Z8q5VkJ6UX04tTgELraupB/MdHio7hkdYT71wBJ6UQuhSmFv/xMOxFfTcGKH5yzsbEM45BtAFHzSXIi3dMCw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-include/-/plugin-include-0.12.0.tgz", + "integrity": "sha512-8pnmp7s1TjbtoBIa/YhYpEivOpeVSyhkQoQrGq1UoaEcTbXqmFwShGkAW3zUYZVFYTl74PgL/UqJnrUojegJQg==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7", + "@types/markdown-it": "^14.1.1", "upath": "^2.0.1" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -479,22 +693,22 @@ } } }, - "node_modules/@mdit/plugin-katex": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-katex/-/plugin-katex-0.8.0.tgz", - "integrity": "sha512-u7CX3Xv5nuc2bu2sHrk1nil83/9ETKTBMmy0icbW8zlqBC0ykLo1xTCEBXmdhXtnJtPi9f/wUZVs6iMZrJzbNg==", + "node_modules/@mdit/plugin-katex-slim": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-katex-slim/-/plugin-katex-slim-0.12.0.tgz", + "integrity": "sha512-s2MJGXFZT7u8IUTmy6K1rxxAdYRmGggu0m860siyUrThL112xLN9r3jmXZ83epgi4UA/gLkRDAU5vF6R2JtyjQ==", "dev": true, "dependencies": { - "@mdit/plugin-tex": "0.8.0", + "@mdit/plugin-tex": "0.12.0", "@types/katex": "^0.16.7", - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { "katex": "^0.16.9", - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "katex": { @@ -506,18 +720,18 @@ } }, "node_modules/@mdit/plugin-mark": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-mark/-/plugin-mark-0.8.0.tgz", - "integrity": "sha512-1hImu8FskIZ9dumWD2VIyB5USyVGwGY2IuaPxYO25tFvMZkhu4rYBjkSK8x+vXExwp94OLzFUlGgVl94S+nw9w==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-mark/-/plugin-mark-0.12.0.tgz", + "integrity": "sha512-BDFwbV/tbgUGL8KF2ymYNLEXT2KNBLe8D0rshDrbB4Iko1U2DywACQkmaUbYBJ1VCn7/dff35at9fWrm3QjrwQ==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -525,21 +739,21 @@ } } }, - "node_modules/@mdit/plugin-mathjax": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-mathjax/-/plugin-mathjax-0.8.0.tgz", - "integrity": "sha512-y016KQHa3PoXDUIcQseISMAz5q2mZJ/qocEs2EABT4PjquXPEh/4rw7Ql7KX9gf/SQIUyzj8hYs4bHyRZc6x4w==", + "node_modules/@mdit/plugin-mathjax-slim": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-mathjax-slim/-/plugin-mathjax-slim-0.12.0.tgz", + "integrity": "sha512-bLM+JnCTN/3XiyKb64Yhpx014VYLfHBexua4n92cUyoKR9g3waB0loF1WMlg6GdyCTc7OvrUSceNjwWj3YRogg==", "dev": true, "dependencies": { - "@mdit/plugin-tex": "0.8.0", - "@types/markdown-it": "^13.0.7", + "@mdit/plugin-tex": "0.12.0", + "@types/markdown-it": "^14.1.1", "upath": "^2.0.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0", + "markdown-it": "^14.1.0", "mathjax-full": "^3.2.2" }, "peerDependenciesMeta": { @@ -551,19 +765,57 @@ } } }, + "node_modules/@mdit/plugin-plantuml": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-plantuml/-/plugin-plantuml-0.12.0.tgz", + "integrity": "sha512-m1pk6PA9+kWUs8kylLqjnQ7Lex68x3c4Ato8zAh+omkhugfWzuQXfFiXRiJ9C7wkdqHoJx/E5XobP3HJnhCpoA==", + "dev": true, + "dependencies": { + "@mdit/plugin-uml": "0.12.0", + "@types/markdown-it": "^14.1.1" + }, + "peerDependencies": { + "markdown-it": "^14.1.0" + }, + "peerDependenciesMeta": { + "markdown-it": { + "optional": true + } + } + }, + "node_modules/@mdit/plugin-spoiler": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-spoiler/-/plugin-spoiler-0.12.0.tgz", + "integrity": "sha512-7yu+Gz000O0OxGnGYOoj77Am3WgH4GwzOvwCp7tPLexkJwTve8MyT9In/NEPFaRw8fmgXwthC0gKq4Ubh1+8DA==", + "dev": true, + "dependencies": { + "@types/markdown-it": "^14.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "markdown-it": "^14.1.0" + }, + "peerDependenciesMeta": { + "markdown-it": { + "optional": true + } + } + }, "node_modules/@mdit/plugin-stylize": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-stylize/-/plugin-stylize-0.8.0.tgz", - "integrity": "sha512-oNFI3Z7UTxP8CKxS3CIuawLmsyrc0n9jIw9mPzUcPNp+LtYmLktfZc3FIRlqpUUq34YwHTH3yihayBRdSkVV6A==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-stylize/-/plugin-stylize-0.12.0.tgz", + "integrity": "sha512-5bzZvmjEpGTdwBax9jaDbCBhD1snEx6uTHVUG9HD/L5koKrL86+ox9E5FGeiMiD1dtxeMgL+WqBzV44nRE9ZPg==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -572,18 +824,18 @@ } }, "node_modules/@mdit/plugin-sub": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-sub/-/plugin-sub-0.8.0.tgz", - "integrity": "sha512-oqCcmJVJykESgNJ4fFmDKKxRRQddwkXWIT4PjF83XSeXHxTOz8gMfke/V1mE7BAfKKCLP4io8HbrYfvIiOTZ4A==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-sub/-/plugin-sub-0.12.0.tgz", + "integrity": "sha512-27kKkSVkymc+2RNc5XOYkeXip5PgHZPUnHpxUvkpnairLwyHsXb8/gzr9zd5arVkip86rcdy9LIvnF7zO0dNVQ==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -592,18 +844,18 @@ } }, "node_modules/@mdit/plugin-sup": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-sup/-/plugin-sup-0.8.0.tgz", - "integrity": "sha512-5/uE2lONNjCgGDXC8jZ265tzefjUNQNakmK4PSCI4D5jD80xFrxc6MKh70VLCOL8Xk6COK/K9f0SAU2lwa97Tg==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-sup/-/plugin-sup-0.12.0.tgz", + "integrity": "sha512-3bEDW5/y1UDVU8LVbFsqUvNcMW6orp16uCdRGYCNZ3/IeK7Qj1/9a3wfhScIoI8xRUE6M3JLv41sGBFXLHwi1w==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -612,15 +864,15 @@ } }, "node_modules/@mdit/plugin-tab": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-tab/-/plugin-tab-0.8.0.tgz", - "integrity": "sha512-SNa1S14Buuy564egiUTkU9HTTNFrEURJZLqA1+jr/2xYCdICPym0FWcB0cLtBl3lrQZkFtbxhzC6ws5JBt/ERQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-tab/-/plugin-tab-0.12.0.tgz", + "integrity": "sha512-ZDTEDxHoekcFA5Al+NLizn8Nf0kj6ABkNBAc/VxbQoVQdjZNQtGY2dOPeWW0I96Rao+Aw+IpYRCLFIfb/KtExw==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -629,18 +881,18 @@ } }, "node_modules/@mdit/plugin-tasklist": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-tasklist/-/plugin-tasklist-0.8.0.tgz", - "integrity": "sha512-vfOTZdXIL/jk/ConUqCODI5WuqgB9qiBGc+wIa7UMhe73KcpwFeGFJVQZm9AvjhXDDYqznJxSMVRP/TN7TxVVw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-tasklist/-/plugin-tasklist-0.12.0.tgz", + "integrity": "sha512-MPmuLJrqHYR2xI7ST9Xtw/xj+6Xoq7kUvcGuXWdMMNT11DcU1KppkR8QBHov437NFYh6aGyjrHUVeM4T5Ls8yg==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -649,18 +901,18 @@ } }, "node_modules/@mdit/plugin-tex": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-tex/-/plugin-tex-0.8.0.tgz", - "integrity": "sha512-uh4kOhwBVEESz6dMmHk4Hn/AVfVtUhMA1UKpwMc1EL9qelodJ0YzSYfNXp6d/PS+E1l53yp8nMZK90DUO+3vpA==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-tex/-/plugin-tex-0.12.0.tgz", + "integrity": "sha512-ejeSgSeZvcI5P4hFFQ4q5pHrZBGO2fQWVGm6dZ3BhX4ldoV8LjCIzkcMMXhrhSOVjwHnqmF6xOh9EvI0jzak1w==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -669,18 +921,18 @@ } }, "node_modules/@mdit/plugin-uml": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@mdit/plugin-uml/-/plugin-uml-0.8.0.tgz", - "integrity": "sha512-6TOVxLhmdzV7bzjlJCRP5uCFq62Xwk2ZAeYUK3RLx9lgM3s2Mww5ENhdysnQMd7VQlUHsPmp4XIMBZZjPddg3g==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@mdit/plugin-uml/-/plugin-uml-0.12.0.tgz", + "integrity": "sha512-EfVMmq0CwLJcssxhkvGS2ESenNNEMeK04j702Z9v3am1M9DdEj6zHTrHQd9tA0jNVuFY8ZlmMgDfkkG5k6Rm3Q==", "dev": true, "dependencies": { - "@types/markdown-it": "^13.0.7" + "@types/markdown-it": "^14.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { - "markdown-it": "^14.0.0" + "markdown-it": "^14.1.0" }, "peerDependenciesMeta": { "markdown-it": { @@ -692,7 +944,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -705,7 +956,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -714,7 +964,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -723,24 +972,222 @@ "node": ">= 8" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz", - "integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" ] }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==" + }, + "node_modules/@shikijs/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.10.0.tgz", + "integrity": "sha512-BZcr6FCmPfP6TXaekvujZcnkFmJHZ/Yglu97r/9VjzVndQA56/F4WjUKtJRQUnK59Wi7p/UTAOekMfCJv7jnYg==", + "dev": true + }, + "node_modules/@shikijs/transformers": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-1.10.0.tgz", + "integrity": "sha512-5Eu/kuJu7/CzAjFlTJkyyPoLTLSVQZ31Ps81cjIeR/3PDJ2RUuX1/R8d0qFziBKToym1LXbNiXoJQq0mg5+Cwg==", + "dev": true, + "dependencies": { + "shiki": "1.10.0" + } + }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", - "dev": true, "engines": { "node": ">=18" }, @@ -749,16 +1196,33 @@ } }, "node_modules/@stackblitz/sdk": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@stackblitz/sdk/-/sdk-1.9.0.tgz", - "integrity": "sha512-3m6C7f8pnR5KXys/Hqx2x6ylnpqOak6HtnZI6T5keEO0yT+E4Spkw37VEbdwuC+2oxmjdgq6YZEgiKX7hM1GmQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@stackblitz/sdk/-/sdk-1.10.0.tgz", + "integrity": "sha512-IcvE9Xifo2c4/f+yNqjFM/OW5VTBPLed3TxsQ+n8n81Py358IqD5w0IYfFgV5gbDjp2g5H5YK2/Shls/kQNTWQ==", "dev": true }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" + }, + "node_modules/@types/d3-time": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dev": true, "dependencies": { "@types/ms": "*" } @@ -767,13 +1231,12 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "devOptional": true }, "node_modules/@types/fs-extra": { "version": "11.0.4", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", - "dev": true, "dependencies": { "@types/jsonfile": "*", "@types/node": "*" @@ -782,14 +1245,12 @@ "node_modules/@types/hash-sum": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha512-UP28RddqY8xcU0SCEp9YKutQICXpaAq9N8U2klqF5hegGha7KzTOL8EdhIIV3bOSGBzjEpN9bU/d+nNZBdJYVw==", - "dev": true + "integrity": "sha512-UP28RddqY8xcU0SCEp9YKutQICXpaAq9N8U2klqF5hegGha7KzTOL8EdhIIV3bOSGBzjEpN9bU/d+nNZBdJYVw==" }, "node_modules/@types/jsonfile": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -801,51 +1262,58 @@ "dev": true }, "node_modules/@types/linkify-it": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", - "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", - "dev": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==" }, "node_modules/@types/markdown-it": { - "version": "13.0.7", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.7.tgz", - "integrity": "sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA==", - "dev": true, + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@types/linkify-it": "^5", + "@types/mdurl": "^2" } }, "node_modules/@types/markdown-it-emoji": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/markdown-it-emoji/-/markdown-it-emoji-2.0.5.tgz", - "integrity": "sha512-iJLsmCNpSWKtV6Ia3mLSjcXJPEt7ubGG342z+hGvYx++TpM19oTUrJcI7XjbOqRQ+W2UQ323E7B0eCLwlgT/9g==", - "dev": true, + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/markdown-it-emoji/-/markdown-it-emoji-3.0.1.tgz", + "integrity": "sha512-cz1j8R35XivBqq9mwnsrP2fsz2yicLhB8+PDtuVkKOExwEdsVBNI+ROL3sbhtR5occRZ66vT0QnwFZCqdjf3pA==", + "dependencies": { + "@types/markdown-it": "^14" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", + "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", "dependencies": { - "@types/markdown-it": "*" + "@types/unist": "^2" } }, "node_modules/@types/mdurl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", - "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==" }, "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", - "dev": true + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", - "dev": true, + "version": "20.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", + "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", "dependencies": { "undici-types": "~5.26.4" } }, + "node_modules/@types/raphael": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@types/raphael/-/raphael-2.3.9.tgz", + "integrity": "sha512-K1dZwoLNvEN+mvleFU/t2swG9Z4SE5Vub7dA5wDYojH0bVTQ8ZAP+lNsl91t1njdu/B+roSEL4QXC67I7Hpiag==" + }, "node_modules/@types/sax": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", @@ -861,17 +1329,21 @@ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "dev": true }, + "node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + }, "node_modules/@types/web-bluetooth": { "version": "0.0.20", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", - "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", - "dev": true + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==" }, "node_modules/@vitejs/plugin-vue": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz", - "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==", - "dev": true, + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz", + "integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==", + "devOptional": true, "engines": { "node": "^18.0.0 || >=20.0.0" }, @@ -881,284 +1353,278 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.4.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.23.tgz", - "integrity": "sha512-HAFmuVEwNqNdmk+w4VCQ2pkLk1Vw4XYiiyxEp3z/xvl14aLTUBw2OfVH3vBcx+FtGsynQLkkhK410Nah1N2yyQ==", - "dev": true, + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.31.tgz", + "integrity": "sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==", "dependencies": { - "@babel/parser": "^7.24.1", - "@vue/shared": "3.4.23", + "@babel/parser": "^7.24.7", + "@vue/shared": "3.4.31", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.23.tgz", - "integrity": "sha512-t0b9WSTnCRrzsBGrDd1LNR5HGzYTr7LX3z6nNBG+KGvZLqrT0mY6NsMzOqlVMBKKXKVuusbbB5aOOFgTY+senw==", - "dev": true, + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.31.tgz", + "integrity": "sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==", "dependencies": { - "@vue/compiler-core": "3.4.23", - "@vue/shared": "3.4.23" + "@vue/compiler-core": "3.4.31", + "@vue/shared": "3.4.31" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.23.tgz", - "integrity": "sha512-fSDTKTfzaRX1kNAUiaj8JB4AokikzStWgHooMhaxyjZerw624L+IAP/fvI4ZwMpwIh8f08PVzEnu4rg8/Npssw==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.24.1", - "@vue/compiler-core": "3.4.23", - "@vue/compiler-dom": "3.4.23", - "@vue/compiler-ssr": "3.4.23", - "@vue/shared": "3.4.23", + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.31.tgz", + "integrity": "sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==", + "dependencies": { + "@babel/parser": "^7.24.7", + "@vue/compiler-core": "3.4.31", + "@vue/compiler-dom": "3.4.31", + "@vue/compiler-ssr": "3.4.31", + "@vue/shared": "3.4.31", "estree-walker": "^2.0.2", - "magic-string": "^0.30.8", + "magic-string": "^0.30.10", "postcss": "^8.4.38", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.23", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.23.tgz", - "integrity": "sha512-hb6Uj2cYs+tfqz71Wj6h3E5t6OKvb4MVcM2Nl5i/z1nv1gjEhw+zYaNOV+Xwn+SSN/VZM0DgANw5TuJfxfezPg==", - "dev": true, + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.31.tgz", + "integrity": "sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==", "dependencies": { - "@vue/compiler-dom": "3.4.23", - "@vue/shared": "3.4.23" + "@vue/compiler-dom": "3.4.31", + "@vue/shared": "3.4.31" } }, "node_modules/@vue/devtools-api": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.1.tgz", - "integrity": "sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==", - "dev": true + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.3.tgz", + "integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==" }, "node_modules/@vue/reactivity": { - "version": "3.4.23", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.23.tgz", - "integrity": "sha512-GlXR9PL+23fQ3IqnbSQ8OQKLodjqCyoCrmdLKZk3BP7jN6prWheAfU7a3mrltewTkoBm+N7qMEb372VHIkQRMQ==", - "dev": true, + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.31.tgz", + "integrity": "sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q==", "dependencies": { - "@vue/shared": "3.4.23" + "@vue/shared": "3.4.31" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.23", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.23.tgz", - "integrity": "sha512-FeQ9MZEXoFzFkFiw9MQQ/FWs3srvrP+SjDKSeRIiQHIhtkzoj0X4rWQlRNHbGuSwLra6pMyjAttwixNMjc/xLw==", - "dev": true, + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.31.tgz", + "integrity": "sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw==", "dependencies": { - "@vue/reactivity": "3.4.23", - "@vue/shared": "3.4.23" + "@vue/reactivity": "3.4.31", + "@vue/shared": "3.4.31" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.23", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.23.tgz", - "integrity": "sha512-RXJFwwykZWBkMiTPSLEWU3kgVLNAfActBfWFlZd0y79FTUxexogd0PLG4HH2LfOktjRxV47Nulygh0JFXe5f9A==", - "dev": true, + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.31.tgz", + "integrity": "sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==", "dependencies": { - "@vue/runtime-core": "3.4.23", - "@vue/shared": "3.4.23", + "@vue/reactivity": "3.4.31", + "@vue/runtime-core": "3.4.31", + "@vue/shared": "3.4.31", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.23", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.23.tgz", - "integrity": "sha512-LDwGHtnIzvKFNS8dPJ1SSU5Gvm36p2ck8wCZc52fc3k/IfjKcwCyrWEf0Yag/2wTFUBXrqizfhK9c/mC367dXQ==", - "dev": true, + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.31.tgz", + "integrity": "sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==", "dependencies": { - "@vue/compiler-ssr": "3.4.23", - "@vue/shared": "3.4.23" + "@vue/compiler-ssr": "3.4.31", + "@vue/shared": "3.4.31" }, "peerDependencies": { - "vue": "3.4.23" + "vue": "3.4.31" } }, "node_modules/@vue/shared": { - "version": "3.4.23", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.23.tgz", - "integrity": "sha512-wBQ0gvf+SMwsCQOyusNw/GoXPV47WGd1xB5A1Pgzy0sQ3Bi5r5xm3n+92y3gCnB3MWqnRDdvfkRGxhKtbBRNgg==", - "dev": true + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.31.tgz", + "integrity": "sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==" }, "node_modules/@vuepress/bundler-vite": { - "version": "2.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@vuepress/bundler-vite/-/bundler-vite-2.0.0-rc.9.tgz", - "integrity": "sha512-GcM2eSqW2mPY5xXX4i5kuZujvwUeiTpsLX5kgau9LzPox+FdA3SMUkppCY3hsou2o2RxXPTfjocE7OlYQrUqvA==", - "dev": true, - "dependencies": { - "@vitejs/plugin-vue": "^5.0.4", - "@vuepress/client": "2.0.0-rc.9", - "@vuepress/core": "2.0.0-rc.9", - "@vuepress/shared": "2.0.0-rc.9", - "@vuepress/utils": "2.0.0-rc.9", + "version": "2.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@vuepress/bundler-vite/-/bundler-vite-2.0.0-rc.14.tgz", + "integrity": "sha512-kttbowYITMCX3ztz78Qb6bMfXRv/GEpNu+nALksu7j/QJQ0gOzI2is68PatbmzZRWOufVsf1Zf0A8BwolmVcXA==", + "devOptional": true, + "dependencies": { + "@vitejs/plugin-vue": "^5.0.5", + "@vuepress/client": "2.0.0-rc.14", + "@vuepress/core": "2.0.0-rc.14", + "@vuepress/shared": "2.0.0-rc.14", + "@vuepress/utils": "2.0.0-rc.14", "autoprefixer": "^10.4.19", "connect-history-api-fallback": "^2.0.0", "postcss": "^8.4.38", - "postcss-load-config": "^5.0.3", - "rollup": "^4.13.0", - "vite": "~5.2.2", - "vue": "^3.4.21", - "vue-router": "^4.3.0" + "postcss-load-config": "^6.0.1", + "rollup": "^4.18.0", + "vite": "~5.3.1", + "vue": "^3.4.29", + "vue-router": "^4.3.3" } }, "node_modules/@vuepress/cli": { - "version": "2.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@vuepress/cli/-/cli-2.0.0-rc.9.tgz", - "integrity": "sha512-uv7Xmv3QmPpzCaUAq0oKEwp2tY64AO+7mxamgr7tr+t6FEnCYqr+X0nLlH17UtMkmGWIsbHLIlMjteprxGxIMg==", - "dev": true, + "version": "2.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@vuepress/cli/-/cli-2.0.0-rc.14.tgz", + "integrity": "sha512-oYJX1nE6/ohF2tzUtpBAFxRr4MF2kdtab3+AQ897esXzrciQnE2LxPQZ8BUOn6Jb3XYW12FXDdkHrr82rN6XnQ==", "dependencies": { - "@vuepress/core": "2.0.0-rc.9", - "@vuepress/shared": "2.0.0-rc.9", - "@vuepress/utils": "2.0.0-rc.9", + "@vuepress/core": "2.0.0-rc.14", + "@vuepress/shared": "2.0.0-rc.14", + "@vuepress/utils": "2.0.0-rc.14", "cac": "^6.7.14", "chokidar": "^3.6.0", - "envinfo": "^7.11.1", - "esbuild": "~0.20.2" + "envinfo": "^7.13.0", + "esbuild": "~0.21.5" }, "bin": { "vuepress-cli": "bin/vuepress.js" } }, "node_modules/@vuepress/client": { - "version": "2.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@vuepress/client/-/client-2.0.0-rc.9.tgz", - "integrity": "sha512-V5jA6L1nHQ8tXBshRHBJKei7HPFonGxFzmVK5yjj2Ho/Xtp/SD9rBS6dyYd5CSkKRGQDgy19Z+BUUPXtdI1qzg==", - "dev": true, + "version": "2.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@vuepress/client/-/client-2.0.0-rc.14.tgz", + "integrity": "sha512-ULwxOiWoUi15HWQ6qH60gWjxSXB0797uExCUa4HgHV/8SpIqv4SHFn6jqjo7qCzOxuTqj1RT47JH3oWfUF4XPA==", "dependencies": { - "@vue/devtools-api": "^6.6.1", - "@vuepress/shared": "2.0.0-rc.9", - "vue": "^3.4.21", - "vue-router": "^4.3.0" + "@vue/devtools-api": "^6.6.3", + "@vuepress/shared": "2.0.0-rc.14", + "vue": "^3.4.29", + "vue-router": "^4.3.3" } }, "node_modules/@vuepress/core": { - "version": "2.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@vuepress/core/-/core-2.0.0-rc.9.tgz", - "integrity": "sha512-uvMkIqYJ7vjfYEC91rMmT8YJt8xXnob5YYY3TzlwWUSEv4yoV3nlVu0l6Zfhenx/7FwKaxRJ/ePlUGIgUHBcBw==", - "dev": true, + "version": "2.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@vuepress/core/-/core-2.0.0-rc.14.tgz", + "integrity": "sha512-Ly3fypjXGUgPzjfbXKJeyd59jxJgXkhxhWAGkH/rRyQeV8Nr7Wo1ah3H1MeGhlCRGH1T9Yd3Bz9W7QMoyWFfmg==", "dependencies": { - "@vuepress/client": "2.0.0-rc.9", - "@vuepress/markdown": "2.0.0-rc.9", - "@vuepress/shared": "2.0.0-rc.9", - "@vuepress/utils": "2.0.0-rc.9", - "vue": "^3.4.21" + "@vuepress/client": "2.0.0-rc.14", + "@vuepress/markdown": "2.0.0-rc.14", + "@vuepress/shared": "2.0.0-rc.14", + "@vuepress/utils": "2.0.0-rc.14", + "vue": "^3.4.29" } }, "node_modules/@vuepress/helper": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/helper/-/helper-2.0.0-rc.24.tgz", - "integrity": "sha512-qXC+tXTKfZ7eJ+h3wYC/7Q903Tbqcz9Vqxku63R6pmcpbsRtt3l8XQRdJ/LMT5yX0wZln4Qzx1NY6S4psr0lzw==", - "dev": true, + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/helper/-/helper-2.0.0-rc.37.tgz", + "integrity": "sha512-Sa2H6EqRuG0+521Z5WN7I8EQNwLwFe7U+1KtV01zFp2BcehsgD3EigBjBP7hl01ubb4T8wR7CxWv7cGVA4bPcw==", "dependencies": { - "@vue/shared": "^3.4.21", + "@vue/shared": "^3.4.29", "cheerio": "1.0.0-rc.12", "fflate": "^0.8.2", "gray-matter": "^4.0.3", - "vue": "^3.4.21" + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, - "node_modules/@vuepress/markdown": { - "version": "2.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@vuepress/markdown/-/markdown-2.0.0-rc.9.tgz", - "integrity": "sha512-e7as2ar3RQp0bUyMiwBPi7L/G2fzscb3s0BywNcAwubFR22o0/dBEYRYdrN0clPQ2FXpPxF6AFj4aD7O1heCbw==", + "node_modules/@vuepress/highlighter-helper": { + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/highlighter-helper/-/highlighter-helper-2.0.0-rc.37.tgz", + "integrity": "sha512-l7qxuJJP0+zxDd42UctS0Oc240cCN7BvxfEx6XJfaYmn2Yncrbbk15gS9tUT3jeXB959JGm8uUhxpPP0/4w3kw==", "dev": true, - "dependencies": { - "@mdit-vue/plugin-component": "^2.0.0", - "@mdit-vue/plugin-frontmatter": "^2.0.0", - "@mdit-vue/plugin-headers": "^2.0.0", - "@mdit-vue/plugin-sfc": "^2.0.0", - "@mdit-vue/plugin-title": "^2.0.0", - "@mdit-vue/plugin-toc": "^2.0.0", - "@mdit-vue/shared": "^2.0.0", - "@mdit-vue/types": "^2.0.0", - "@types/markdown-it": "^13.0.7", - "@types/markdown-it-emoji": "^2.0.4", - "@vuepress/shared": "2.0.0-rc.9", - "@vuepress/utils": "2.0.0-rc.9", + "peerDependencies": { + "vuepress": "2.0.0-rc.14" + } + }, + "node_modules/@vuepress/markdown": { + "version": "2.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@vuepress/markdown/-/markdown-2.0.0-rc.14.tgz", + "integrity": "sha512-9xr693gkp71qwEbQLxpo1ybhJ+lA2k5SiuFUgqqrmR2a8CSL3gcmKEGM+y7GMnHvL63U2dYlc9pUOtJ5rG9O0Q==", + "dependencies": { + "@mdit-vue/plugin-component": "^2.1.3", + "@mdit-vue/plugin-frontmatter": "^2.1.3", + "@mdit-vue/plugin-headers": "^2.1.3", + "@mdit-vue/plugin-sfc": "^2.1.3", + "@mdit-vue/plugin-title": "^2.1.3", + "@mdit-vue/plugin-toc": "^2.1.3", + "@mdit-vue/shared": "^2.1.3", + "@mdit-vue/types": "^2.1.0", + "@types/markdown-it": "^14.1.1", + "@types/markdown-it-emoji": "^3.0.1", + "@vuepress/shared": "2.0.0-rc.14", + "@vuepress/utils": "2.0.0-rc.14", "markdown-it": "^14.1.0", - "markdown-it-anchor": "^8.6.7", + "markdown-it-anchor": "^9.0.1", "markdown-it-emoji": "^3.0.0", "mdurl": "^2.0.0" } }, "node_modules/@vuepress/plugin-active-header-links": { - "version": "2.0.0-rc.21", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-active-header-links/-/plugin-active-header-links-2.0.0-rc.21.tgz", - "integrity": "sha512-6i9TfGDV1zfszQ5aw6bV+/UvPdBWt3VxN2WB4Dg5o1g8Qn4z5CI6AW6VfLKRyaKUD+Rzj6W+Ikgx4xnF5RZAdA==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-active-header-links/-/plugin-active-header-links-2.0.0-rc.37.tgz", + "integrity": "sha512-VLM0JXgdShRAR38smp/M72sctDIVPgW4E+fBpGaP4iG+JOywXbLp5MfiO/r1ww62k7LXRI8g8/hYjlhyeMGnEg==", "dev": true, "dependencies": { - "@vueuse/core": "^10.9.0", - "vue": "^3.4.21" + "@vueuse/core": "^10.11.0", + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, "node_modules/@vuepress/plugin-back-to-top": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-back-to-top/-/plugin-back-to-top-2.0.0-rc.24.tgz", - "integrity": "sha512-cU5KtsuqUBcDiNlAD+I2NaaEd7ZRDldWPggJMgE7VvhEQ8uJMOq4ogh2IabeqGZ26XiUYuGnNrp4JK5mDkQlvw==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-back-to-top/-/plugin-back-to-top-2.0.0-rc.37.tgz", + "integrity": "sha512-R9rAXZCYdnBADvcbY2V4xtGOHS11mAIV98MbpixP1CYGaRdOGMcp4oJwfsGqwE1Iyi/nBgBaVFHexyQFkx7Rag==", "dev": true, "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24", - "@vueuse/core": "^10.9.0", - "vue": "^3.4.21" + "@vuepress/helper": "2.0.0-rc.37", + "@vueuse/core": "^10.11.0", + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, "node_modules/@vuepress/plugin-blog": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-blog/-/plugin-blog-2.0.0-rc.24.tgz", - "integrity": "sha512-EEpJcTHhlB6/LWXWdhBN3f9dFRrkOJSWw9KyD/7/GBImqbPKrdWh2y6VZejUvZBK+1Onv0/KEXMgE3zI3LAB/g==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-blog/-/plugin-blog-2.0.0-rc.37.tgz", + "integrity": "sha512-rJBM/P7PNhKkbz8UfQjY9qo1zxJZlF31vBgx1inhGS2tP1KJoUPTZdIRk35TphIKnA/4Xp6c7KLAL0RT4R21bw==", "dev": true, "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24", + "@vuepress/helper": "2.0.0-rc.37", "chokidar": "^3.6.0", - "vue": "^3.4.21" + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, "node_modules/@vuepress/plugin-catalog": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-catalog/-/plugin-catalog-2.0.0-rc.24.tgz", - "integrity": "sha512-MkJ14qOd0KoKb8cmFqT0tPNK9REJNP8bm1dZBdYOrqX8mDgt4nq2EyVOZTBZWqaYyXekJZyNfXkN4i556/8x+w==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-catalog/-/plugin-catalog-2.0.0-rc.37.tgz", + "integrity": "sha512-NTTQZNtiO3cXtZ1/UK0ZKFjU3zUsg6KjCFme75vDAzS9TaO9G4c/RLlHKnmfMz/eGTwbiaoZPNcpR9xlGP0c9A==", "dev": true, "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24", - "vue": "^3.4.21" + "@vuepress/helper": "2.0.0-rc.37", + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, "node_modules/@vuepress/plugin-comment": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-comment/-/plugin-comment-2.0.0-rc.24.tgz", - "integrity": "sha512-Kl5LHCbyoTIXFZwMmJa4f8neMbebC4ZhASf8cnfdNTBf6XRVbSH1fGJKGFK1lUm3EcjjBHIAuZIrlMWPmepUGQ==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-comment/-/plugin-comment-2.0.0-rc.37.tgz", + "integrity": "sha512-SmwOdMzdHRV8LHSRmhwFibHesfaa3iRZDGMGcIYI5o294R1XO+QCVanV/KJ4GcNvpqrIlZpYN6z5CJrSz1jLxg==", "dev": true, "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24", + "@vuepress/helper": "2.0.0-rc.37", "giscus": "^1.5.0", - "vue": "^3.4.21" + "vue": "^3.4.29" }, "peerDependencies": { "@waline/client": "^3.1.0", - "artalk": "^2.7.3", - "sass-loader": "^14.0.0", + "artalk": "^2.8.7", "twikoo": "^1.5.0", - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" }, "peerDependenciesMeta": { "@waline/client": { @@ -1167,263 +1633,291 @@ "artalk": { "optional": true }, - "sass-loader": { - "optional": true - }, "twikoo": { "optional": true } } }, "node_modules/@vuepress/plugin-copy-code": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-copy-code/-/plugin-copy-code-2.0.0-rc.24.tgz", - "integrity": "sha512-anLxeQqTiU+LNdEBK7EUbeIcU0YEh6x9Bs9SQV4wrdFKjYc5x2U4Gf9l9NagOSf1e3c0QZRYL3wLQ5q3XZnGjA==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-copy-code/-/plugin-copy-code-2.0.0-rc.37.tgz", + "integrity": "sha512-Z8rvn3n+SsEUmyoR2rYo/AYUPbOsJravtVh4COVqc40FT6gxlZPLlUcr4XV4Nh0ja87oZn/FubbZMkWfU8MyOA==", "dev": true, "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24", - "@vueuse/core": "^10.9.0", - "vue": "^3.4.21" + "@vuepress/helper": "2.0.0-rc.37", + "@vueuse/core": "^10.11.0", + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, "node_modules/@vuepress/plugin-copyright": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-copyright/-/plugin-copyright-2.0.0-rc.24.tgz", - "integrity": "sha512-sposicjAxALPhXb6TBVq5x6dE2/87OvwyB4RFs0kCeUjE4Tg7WKj2E28vdRQtNGE0P8MC0D0qJbi/ORbg9UObw==", - "dev": true, - "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24", - "@vueuse/core": "^10.9.0", - "vue": "^3.4.21" - }, - "peerDependencies": { - "vuepress": "2.0.0-rc.9" - } - }, - "node_modules/@vuepress/plugin-docsearch": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-docsearch/-/plugin-docsearch-2.0.0-rc.24.tgz", - "integrity": "sha512-RWJSzRu7+fCO3//rYGO3EBq3+MkrGRyZx0k8nV1CiubpTa0aYftxxYoZdQH4UJslTuKhNShlYsIzxYNMYAf7uw==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-copyright/-/plugin-copyright-2.0.0-rc.37.tgz", + "integrity": "sha512-V7gTq7nQLzl6S/hd+H775yLUCOtWYWcH6epZMPJJn82iuqscP07lMLcw4SfAyILgHsvi5sK64HAbRVEo3QEc9w==", "dev": true, - "optional": true, - "peer": true, "dependencies": { - "@docsearch/css": "^3.6.0", - "@docsearch/js": "^3.6.0", - "@docsearch/react": "^3.6.0", - "@vuepress/helper": "~2.0.0-rc.24", - "@vueuse/core": "^10.9.0", - "ts-debounce": "^4.0.0", - "vue": "^3.4.21" + "@vuepress/helper": "2.0.0-rc.37", + "@vueuse/core": "^10.11.0", + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, - "node_modules/@vuepress/plugin-external-link-icon": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-external-link-icon/-/plugin-external-link-icon-2.0.0-rc.24.tgz", - "integrity": "sha512-kry1EFkv6WaGOCzk9vRGHGcAuQHNVQ/jDEgtagUFaRk5+HtCQB60VzhmFdwM08DC2XAmDieBLm1MMR0T2DdHSw==", + "node_modules/@vuepress/plugin-git": { + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-git/-/plugin-git-2.0.0-rc.37.tgz", + "integrity": "sha512-+p+EkcWsoErJXKdVZQBfazJ6Q8YFRXXjfDrb1qUSF5wCUfC7QgD6Vsgt5MmB9YZjbhj1Yf/xeZcDVB6qz8B+GA==", "dev": true, "dependencies": { - "vue": "^3.4.21" + "execa": "^9.2.0" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, - "node_modules/@vuepress/plugin-git": { - "version": "2.0.0-rc.22", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-git/-/plugin-git-2.0.0-rc.22.tgz", - "integrity": "sha512-+T50AdCZ68Pkld4r8IEHTXLugfNVCxxPp2G1hlI/lpQ6IZcpLbswMI6l9xbbo15RrOBg/V0jkim/B/jaaVIM6A==", + "node_modules/@vuepress/plugin-links-check": { + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-links-check/-/plugin-links-check-2.0.0-rc.37.tgz", + "integrity": "sha512-9YelTHKFKADKmSdGxEVyF5LE6r9rg95ia695tQ695n/ov+2/iC0Ez6Kc0ceaqdaCPMq8Q5OsLAh2/hC2N/H15Q==", "dev": true, "dependencies": { - "execa": "^8.0.1" + "@vuepress/helper": "2.0.0-rc.37" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, - "node_modules/@vuepress/plugin-links-check": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-links-check/-/plugin-links-check-2.0.0-rc.24.tgz", - "integrity": "sha512-+HPIutNZhMP2eSf1Gb217WLCQlQhsMkebTfuZYyDSGGvY5TQmXOAuu/X7Xwh1lJlml9asPUXTcFe2cZcEtHHIA==", + "node_modules/@vuepress/plugin-notice": { + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-notice/-/plugin-notice-2.0.0-rc.37.tgz", + "integrity": "sha512-dUoHOZpvV3K8eVPNhTz6Bh+aZzM+Trk+JZumLRqywQ2f5Kn+unU3VTjJHrfWUIYFuwcmWjWbKdz0D35B48UVFA==", "dev": true, "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24" + "@vuepress/helper": "2.0.0-rc.37", + "@vueuse/core": "^10.11.0", + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, "node_modules/@vuepress/plugin-nprogress": { - "version": "2.0.0-rc.21", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-nprogress/-/plugin-nprogress-2.0.0-rc.21.tgz", - "integrity": "sha512-qpGA76195SyfpuQC1Pb9LwgCYIp/zg+BBDnexukJMdLjP1KnaU7HLhS5NnRNIWv8E+IC61zLvlh/wRox17QE+w==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-nprogress/-/plugin-nprogress-2.0.0-rc.37.tgz", + "integrity": "sha512-HEv6AzAG3U7Q5cAkeuQhMV2JhASB/2veTQ85OHNyWUzZ5yTC/Kx5PfQOb87X6LWK51eZA5vImibeZbFJ1mkE3g==", "dev": true, "dependencies": { - "vue": "^3.4.21" + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, "node_modules/@vuepress/plugin-photo-swipe": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-photo-swipe/-/plugin-photo-swipe-2.0.0-rc.24.tgz", - "integrity": "sha512-2Rvi8ODFJgIDDfrXzt7ynY3nizCiEte2Cna4W73bH1+s9PMiOoa5rQ54/r+jbLe4Nw5Iw4x+PXcRN8fDQPllKg==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-photo-swipe/-/plugin-photo-swipe-2.0.0-rc.37.tgz", + "integrity": "sha512-b9QR16jsx34z5TQvFvO3vMaPcuCD05OBvoFJ4nzu3P9cCZxIAfx3fw5wjIXR2wpKIiGLvkPhFOxNCywKFPAeBA==", "dev": true, "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24", - "@vueuse/core": "^10.9.0", - "photoswipe": "^5.4.3", - "vue": "^3.4.21" + "@vuepress/helper": "2.0.0-rc.37", + "@vueuse/core": "^10.11.0", + "photoswipe": "^5.4.4", + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, - "node_modules/@vuepress/plugin-prismjs": { - "version": "2.0.0-rc.21", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-prismjs/-/plugin-prismjs-2.0.0-rc.21.tgz", - "integrity": "sha512-dMTCu/TZ1QCmTHXL4THVeh9gWzuqkJV8qhck5U77OP1qmgyf+r529A+MTOgp3ddcph1Yzb/FRb2orlefHk+yNQ==", + "node_modules/@vuepress/plugin-reading-time": { + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-reading-time/-/plugin-reading-time-2.0.0-rc.37.tgz", + "integrity": "sha512-jvg9pYkj0+PTv1P+H0Yk2RcXIKuz8LNwa05Ratf+pDppdi0ImnIBMNzk6gsQuBOjemol+1NNStYRu2Oo1IUudA==", "dev": true, "dependencies": { - "prismjs": "^1.29.0" + "@vuepress/helper": "2.0.0-rc.37", + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, - "node_modules/@vuepress/plugin-reading-time": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-reading-time/-/plugin-reading-time-2.0.0-rc.24.tgz", - "integrity": "sha512-La6dgul551Xp2Iacs1URZnLX5YdakfJWFfE9vIhhX/Q1+slUGRVftFLh/nb0oVUrsXNeRlqCUncTyilg51Q1fA==", + "node_modules/@vuepress/plugin-rtl": { + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-rtl/-/plugin-rtl-2.0.0-rc.37.tgz", + "integrity": "sha512-XgxfO0y6sVG1rMiRVYT/DNZegOEeZurqLioZvoOVtVo7HASF1qKVrfgRwSoorVnZwpg7VtWf0U27RCO9A8BC4Q==", "dev": true, "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24", - "vue": "^3.4.21" + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, - "node_modules/@vuepress/plugin-rtl": { - "version": "2.0.0-rc.21", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-rtl/-/plugin-rtl-2.0.0-rc.21.tgz", - "integrity": "sha512-r+4aP898KsFbF6m1J0e+776ZlSE9yaHr9zsMlib1GEUDcqP/OykMYaNKwRsOMB1eFXNmymgHlXFvswBGEHxS7w==", - "dev": true, + "node_modules/@vuepress/plugin-sass-palette": { + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-sass-palette/-/plugin-sass-palette-2.0.0-rc.37.tgz", + "integrity": "sha512-/dy47g27diqwbupYsoNTTgqsEv26AbC/rrypFLb6qQwYd+/1sO/b6osAqpqbm1u7mGq5J3nCGLp6mtK9gVLpTg==", "dependencies": { - "vue": "^3.4.21" + "@vuepress/helper": "2.0.0-rc.37", + "chokidar": "^3.6.0", + "sass": "^1.77.6" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" - } + "sass-loader": "^14.0.0", + "vuepress": "2.0.0-rc.14" + }, + "peerDependenciesMeta": { + "sass-loader": { + "optional": true + } + } }, "node_modules/@vuepress/plugin-search": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-search/-/plugin-search-2.0.0-rc.24.tgz", - "integrity": "sha512-GgjKMCb+IUYTo/Nui7vCIzQV5KpZD05VknhPmy3aySYXGd55vem8IVDVOV71CU1mX9qnHnQt5DFX2sBtmu2SRQ==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-search/-/plugin-search-2.0.0-rc.37.tgz", + "integrity": "sha512-shFojdAi8Jj+/4w8Sgnl8UxssOWd/8HMF/5RGqa3rgKxNiCCzndLag8OJmRE1xbMnkdKvtPlA0UoOgG9qDom9g==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "chokidar": "^3.6.0", - "vue": "^3.4.21" + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, "node_modules/@vuepress/plugin-seo": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-seo/-/plugin-seo-2.0.0-rc.24.tgz", - "integrity": "sha512-E0dRCNqV6RLoVV4j8xchmlsnlR7OyPQxWmWrk20mBiyluRI05OXdb20ZQbYJe3PfK8f8DnyETzob943HBg3sVA==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-seo/-/plugin-seo-2.0.0-rc.37.tgz", + "integrity": "sha512-H6bM4jYKwFABP/JGocDC7CD5SACMdFf9rE5lci5/VYJybvBr5XXDXHXk9KSy01jupPjmAjsGp5Fzqk2ggeqQIQ==", + "dev": true, + "dependencies": { + "@vuepress/helper": "2.0.0-rc.37" + }, + "peerDependencies": { + "vuepress": "2.0.0-rc.14" + } + }, + "node_modules/@vuepress/plugin-shiki": { + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-shiki/-/plugin-shiki-2.0.0-rc.37.tgz", + "integrity": "sha512-3VBobLYjj8dnWMohy4HK/eCRiZ/9DzCG4hMStBJiIr6R5kyf3yVc7kc5rWSiOqq8yrNwkgNafKPuAaZYHKuJdw==", "dev": true, "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24" + "@shikijs/transformers": "^1.7.0", + "@vuepress/helper": "2.0.0-rc.37", + "@vuepress/highlighter-helper": "2.0.0-rc.37", + "nanoid": "^5.0.7", + "shiki": "^1.7.0" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" + } + }, + "node_modules/@vuepress/plugin-shiki/node_modules/nanoid": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", + "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" } }, "node_modules/@vuepress/plugin-sitemap": { - "version": "2.0.0-rc.24", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-sitemap/-/plugin-sitemap-2.0.0-rc.24.tgz", - "integrity": "sha512-su5ZD8vGuNpbqmb+uCOzWXCZ0eii8wnkdhn4V1xtmmXsrmYDr0FFHp61Ebb6EYAquB3HH1v3hWdfLRMU9DM6VQ==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-sitemap/-/plugin-sitemap-2.0.0-rc.37.tgz", + "integrity": "sha512-mL9aKIkiSEJizVcsreY7O+FZmTT/coV4iR2P5OQ5oyXbcByMN+omirsupHBHsWl2zH/EZe2FklByDEDUPpg3MQ==", "dev": true, "dependencies": { - "@vuepress/helper": "~2.0.0-rc.24", - "sitemap": "^7.1.1" + "@vuepress/helper": "2.0.0-rc.37", + "sitemap": "^8.0.0" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, "node_modules/@vuepress/plugin-theme-data": { - "version": "2.0.0-rc.21", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-theme-data/-/plugin-theme-data-2.0.0-rc.21.tgz", - "integrity": "sha512-vLXvTKx4gWXY6oVaJ9Z2ECnojnKQuXBIe1ZGIAwJdxCYfr6aaqggrVvmphB8BwTURh0XAuis/l6YTcMrs0bX8Q==", + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-theme-data/-/plugin-theme-data-2.0.0-rc.37.tgz", + "integrity": "sha512-xzdYyScZcS5aDbk/2bQAuEofe6wqgtX2Y/6SUqsGlV4A59D+xIfPbNUZhO0gbUX//ofYGaxgKQps0OrvPkwMVg==", "dev": true, "dependencies": { - "@vue/devtools-api": "^6.6.1", - "vue": "^3.4.21" + "@vue/devtools-api": "^6.6.3", + "vue": "^3.4.29" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, - "node_modules/@vuepress/shared": { - "version": "2.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@vuepress/shared/-/shared-2.0.0-rc.9.tgz", - "integrity": "sha512-XfI6CWNv4/Vp9Iew6GJil9RUSy1rM7zGdjwikr0j3Rkh55q3f00w1wud47wE9kxRqsZ0PIvsMget5CxEn5rA/w==", + "node_modules/@vuepress/plugin-watermark": { + "version": "2.0.0-rc.37", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-watermark/-/plugin-watermark-2.0.0-rc.37.tgz", + "integrity": "sha512-lHNkxpIB0yV9RdzVRFQMQ1J2jhYVYJCyeZyZmnNMQ5P/iPnhrAOFLO/opHnCuGUun/ObxhQELKLA09qlzrS0Vg==", "dev": true, "dependencies": { - "@mdit-vue/types": "^2.0.0" + "@vuepress/helper": "2.0.0-rc.37", + "vue": "^3.4.29", + "watermark-js-plus": "^1.5.1" + }, + "peerDependencies": { + "vuepress": "2.0.0-rc.14" + } + }, + "node_modules/@vuepress/shared": { + "version": "2.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@vuepress/shared/-/shared-2.0.0-rc.14.tgz", + "integrity": "sha512-VDDnPpz4x1Q07richcVRGbc4qc2RG/6bKoEYSImofTFzvdmHer538ouv8kD2SNU10UrSOpxxUiphnhlhNIe03A==", + "dependencies": { + "@mdit-vue/types": "^2.1.0" } }, "node_modules/@vuepress/utils": { - "version": "2.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@vuepress/utils/-/utils-2.0.0-rc.9.tgz", - "integrity": "sha512-qk6Pel4JVKYKxp3bWxyvnwchvx3QaCWc7SqUw7L6qUo/um+0U2U45L0anWoAfckw12RXYhoIEbJ9UZpueiKOPg==", - "dev": true, + "version": "2.0.0-rc.14", + "resolved": "https://registry.npmjs.org/@vuepress/utils/-/utils-2.0.0-rc.14.tgz", + "integrity": "sha512-1h/5qcKBeIhIg6SZM2IoZVOaIdFSeQ1CdEWadqQWy1uwupEeVrU3QPkjFyn0vUt0O/EuuVqQcLLC8OuS/wldNw==", "dependencies": { "@types/debug": "^4.1.12", "@types/fs-extra": "^11.0.4", "@types/hash-sum": "^1.0.2", - "@vuepress/shared": "2.0.0-rc.9", - "debug": "^4.3.4", + "@vuepress/shared": "2.0.0-rc.14", + "debug": "^4.3.5", "fs-extra": "^11.2.0", "globby": "^14.0.1", "hash-sum": "^2.0.0", "ora": "^8.0.1", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "upath": "^2.0.1" } }, "node_modules/@vueuse/core": { - "version": "10.9.0", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.9.0.tgz", - "integrity": "sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==", - "dev": true, + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz", + "integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==", "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.9.0", - "@vueuse/shared": "10.9.0", - "vue-demi": ">=0.14.7" + "@vueuse/metadata": "10.11.0", + "@vueuse/shared": "10.11.0", + "vue-demi": ">=0.14.8" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/core/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", - "dev": true, + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", "hasInstallScript": true, "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", @@ -1446,31 +1940,28 @@ } }, "node_modules/@vueuse/metadata": { - "version": "10.9.0", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.9.0.tgz", - "integrity": "sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==", - "dev": true, + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz", + "integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==", "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { - "version": "10.9.0", - "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.9.0.tgz", - "integrity": "sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==", - "dev": true, + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.0.tgz", + "integrity": "sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==", "dependencies": { - "vue-demi": ">=0.14.7" + "vue-demi": ">=0.14.8" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", - "dev": true, + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", "hasInstallScript": true, "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", @@ -1492,36 +1983,10 @@ } } }, - "node_modules/algoliasearch": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", - "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@algolia/cache-browser-local-storage": "4.23.3", - "@algolia/cache-common": "4.23.3", - "@algolia/cache-in-memory": "4.23.3", - "@algolia/client-account": "4.23.3", - "@algolia/client-analytics": "4.23.3", - "@algolia/client-common": "4.23.3", - "@algolia/client-personalization": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/logger-common": "4.23.3", - "@algolia/logger-console": "4.23.3", - "@algolia/recommend": "4.23.3", - "@algolia/requester-browser-xhr": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/requester-node-http": "4.23.3", - "@algolia/transporter": "4.23.3" - } - }, "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -1548,7 +2013,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1567,7 +2031,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -1576,7 +2039,7 @@ "version": "10.4.19", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", - "dev": true, + "devOptional": true, "funding": [ { "type": "opencollective", @@ -1628,7 +2091,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "engines": { "node": ">=8" }, @@ -1639,26 +2101,24 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "dev": true, + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", + "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -1674,10 +2134,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "update-browserslist-db": "^1.0.16" }, "bin": { "browserslist": "cli.js" @@ -1690,7 +2150,6 @@ "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, "engines": { "node": ">=8" } @@ -1705,10 +2164,10 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001610", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001610.tgz", - "integrity": "sha512-QFutAY4NgaelojVMjY63o6XlZyORPaLfyMnsl3HgnWdJUcX6K0oaJymHjH8PT5Gk7sTm8rvC/c5COUQKXqmOMA==", - "dev": true, + "version": "1.0.30001638", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001638.tgz", + "integrity": "sha512-5SuJUJ7cZnhPpeLHaH0c/HPAnAHZvS6ElWyHK9GSIbVOQABLzowiI2pjmpvZ1WEbkyz46iFd4UXlOHR5SqgfMQ==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -1728,7 +2187,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -1736,11 +2194,30 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chart.js": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", + "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/cheerio": { "version": "1.0.0-rc.12", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "dev": true, "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", @@ -1761,7 +2238,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "dev": true, "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", @@ -1778,7 +2254,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -1802,173 +2277,625 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dev": true, "dependencies": { "restore-cursor": "^4.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "devOptional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/create-codepen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/create-codepen/-/create-codepen-2.0.0.tgz", + "integrity": "sha512-ehJ0Zw5RSV2G4+/azUb7vEZWRSA/K9cW7HDock1Y9ViDexkgSJUZJRcObdw/YAWeXKjreEQV9l/igNSsJ1yw5A==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/cytoscape": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.0.tgz", + "integrity": "sha512-l590mjTHT6/Cbxp13dGPC2Y7VXdgc+rUeF8AnF/JPzhjNevbDJfObnJgaSjlldOgBQZbue+X6IUZ7r5GAgvauQ==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" } }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12" } }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" } }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "d3-path": "1" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", "dependencies": { - "ansi-regex": "^5.0.1" + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", "dependencies": { - "color-name": "~1.1.4" + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" }, "engines": { - "node": ">=7.0.0" + "node": ">=12" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "dev": true, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "engines": { - "node": ">=0.8" + "node": ">=12" } }, - "node_modules/create-codepen": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/create-codepen/-/create-codepen-1.0.1.tgz", - "integrity": "sha512-XzSWwGCFNeOnNGp3KdCDGaKq4Cp1SvjzpPGQqO0tj1HT3BhksLdl/xQ2ZEY4+0MQ3m1I/K1Fvpm4GGMthtamyA==", - "dev": true + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "d3-array": "2 - 3" }, "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" + "d3-time": "1 - 3" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "engines": { + "node": ">=12" } }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", "engines": { - "node": ">= 6" + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" } }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", + "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", + "dependencies": { + "d3": "^7.8.2", + "lodash-es": "^4.17.21" + } }, "node_modules/dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", - "dev": true + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dependencies": { "ms": "2.1.2" }, @@ -1990,6 +2917,42 @@ "node": ">=0.10.0" } }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/dijkstrajs": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", @@ -2000,7 +2963,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -2014,7 +2976,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, "funding": [ { "type": "github", @@ -2026,7 +2987,6 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, "dependencies": { "domelementtype": "^2.3.0" }, @@ -2037,11 +2997,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.5.tgz", + "integrity": "sha512-lwG+n5h8QNpxtyrJW/gJWckL+1/DQiYMX8f7t8Z2AZTPw1esVrqjI63i7Zc2Gz0aKzLVMYC1V1PL/ky+aY/NgA==" + }, "node_modules/domutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -2051,17 +3015,30 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/echarts": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.1.tgz", + "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.0" + } + }, "node_modules/electron-to-chromium": { - "version": "1.4.738", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.738.tgz", - "integrity": "sha512-lwKft2CLFztD+vEIpesrOtCrko/TFnEJlHFdRhazU7Y/jx5qc4cqsocfVrBg4So4gGe9lvxnbLIoev47WMpg+A==", - "dev": true + "version": "1.4.815", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.815.tgz", + "integrity": "sha512-OvpTT2ItpOXJL7IGcYakRjHCt8L5GrrN/wHCQsRB4PQa1X9fe+X9oen245mIId7s14xvArCGSTIq644yPUKKLg==", + "devOptional": true + }, + "node_modules/elkjs": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", + "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==" }, "node_modules/emoji-regex": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", - "dev": true + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" }, "node_modules/encode-utf8": { "version": "1.0.3", @@ -2073,7 +3050,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -2082,10 +3058,9 @@ } }, "node_modules/envinfo": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.12.0.tgz", - "integrity": "sha512-Iw9rQJBGpJRd3rwXm9ft/JiGoAZmLxxJZELYDQoPRZ4USVhkKtIcNBPw6U+/K2mBpaqM25JSV6Yl4Az9vO2wJg==", - "dev": true, + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz", + "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==", "bin": { "envinfo": "dist/cli.js" }, @@ -2094,10 +3069,9 @@ } }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "dev": true, + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -2106,36 +3080,36 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } @@ -2144,7 +3118,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2156,54 +3129,44 @@ "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/eve-raphael": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/eve-raphael/-/eve-raphael-0.5.0.tgz", + "integrity": "sha512-jrxnPsCGqng1UZuEp9DecX/AuSyAszATSjf4oEcRxvfxa1Oux4KkIPKBAAWWnpdwfARtr+Q0o9aPYWjsROD7ug==" }, "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.3.0.tgz", + "integrity": "sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==", "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^7.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^5.2.0", + "pretty-ms": "^9.0.0", "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" }, "engines": { - "node": ">=16.17" + "node": "^18.19.0 || >=20.5.0" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/execa/node_modules/mimic-fn": { + "node_modules/execa/node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2213,7 +3176,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -2225,7 +3187,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, "dependencies": { "is-extendable": "^0.1.0" }, @@ -2237,7 +3198,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -2253,7 +3213,6 @@ "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -2261,14 +3220,26 @@ "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "dev": true + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2289,11 +3260,40 @@ "node": ">=8" } }, + "node_modules/flowchart": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/flowchart/-/flowchart-1.2.0.tgz", + "integrity": "sha512-n8gn08AzGwj/4aALo1DP5Xfb/Fr0BDrs6kn/z7zT2FvWzHSt8keDP1yN7NP1YGiDks9e5e/Q/gWHRGzC1JK0/w==", + "dependencies": { + "underscore": "^1.8.3" + }, + "bin": { + "flowchart": "index.js" + } + }, + "node_modules/flowchart.ts": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/flowchart.ts/-/flowchart.ts-3.0.0.tgz", + "integrity": "sha512-yD+0wVJHvV6SutpNpao5S0hQp+7tnPuETtPORIodGEuUAP8LzegVVSUy+NFDWU9pUO2F1E+PikBD83PddoCfnw==", + "dependencies": { + "@types/raphael": "^2.3.9", + "raphael": "^2.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^18.0.0 || >= 20" + } + }, + "node_modules/flowchart.ts/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "dev": true, + "devOptional": true, "engines": { "node": "*" }, @@ -2306,7 +3306,6 @@ "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -2316,6 +3315,19 @@ "node": ">=14.14" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2329,7 +3341,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", - "dev": true, "engines": { "node": ">=18" }, @@ -2338,12 +3349,15 @@ } }, "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2362,7 +3376,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -2371,10 +3384,9 @@ } }, "node_modules/globby": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz", - "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==", - "dev": true, + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", "dependencies": { "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.2", @@ -2393,14 +3405,12 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/gray-matter": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "dev": true, "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", @@ -2414,14 +3424,12 @@ "node_modules/hash-sum": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", - "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", - "dev": true + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==" }, "node_modules/htmlparser2": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -2437,34 +3445,49 @@ } }, "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-7.0.0.tgz", + "integrity": "sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, "engines": { - "node": ">=16.17.0" + "node": ">=0.10.0" } }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, "engines": { "node": ">= 4" } }, "node_modules/immutable": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", - "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", - "dev": true + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", + "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -2476,7 +3499,6 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2485,7 +3507,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2503,7 +3524,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -2515,7 +3535,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "dev": true, "engines": { "node": ">=12" }, @@ -2527,18 +3546,27 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2548,7 +3576,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", - "dev": true, "engines": { "node": ">=18" }, @@ -2559,14 +3586,12 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/js-yaml": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -2579,7 +3604,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, "dependencies": { "universalify": "^2.0.0" }, @@ -2587,20 +3611,52 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/katex": { + "version": "0.16.10", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz", + "integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" + }, "node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", - "dev": true, + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "devOptional": true, "engines": { "node": ">=14" }, @@ -2612,15 +3668,14 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, "dependencies": { "uc.micro": "^2.0.0" } }, "node_modules/lit": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.3.tgz", - "integrity": "sha512-l4slfspEsnCcHVRTvaP7YnkTZEZggNFywLEIhQaGhYDczG+tu/vlgm/KaWIEjIp+ZyV20r2JnZctMb8LeLCG7Q==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.4.tgz", + "integrity": "sha512-q6qKnKXHy2g1kjBaNfcoLlgbI3+aSOZ9Q4tiGa9bGYXq5RBXxkVTqTIVmP2VWMp29L4GyvCFm8ZQ2o56eUAMyA==", "dev": true, "dependencies": { "@lit/reactive-element": "^2.0.4", @@ -2629,9 +3684,9 @@ } }, "node_modules/lit-element": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.5.tgz", - "integrity": "sha512-iTWskWZEtn9SyEf4aBG6rKT8GABZMrTWop1+jopsEOgEcugcXJGKuX5bEbkq9qfzY+XB4MAgCaSPwnNpdsNQ3Q==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.6.tgz", + "integrity": "sha512-U4sdJ3CSQip7sLGZ/uJskO5hGiqtlpxndsLr6mt3IQIjheg93UKYeGQjWMRql1s/cXNOaRrCzC2FQwjIwSUqkg==", "dev": true, "dependencies": { "@lit-labs/ssr-dom-shim": "^1.2.0", @@ -2640,9 +3695,9 @@ } }, "node_modules/lit-html": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.3.tgz", - "integrity": "sha512-FwIbqDD8O/8lM4vUZ4KvQZjPPNx7V1VhT7vmRB8RBAO0AU6wuTVdoXiu2CivVjEGdugvcbPNBLtPE1y0ifplHA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.4.tgz", + "integrity": "sha512-yKKO2uVv7zYFHlWMfZmqc+4hkmSbFp8jgjdZY9vvR9jr4J8fH6FUMXhr+ljfELgmjpvlF7Z1SJ5n5/Jeqtc9YA==", "dev": true, "dependencies": { "@types/trusted-types": "^2.0.2" @@ -2660,11 +3715,15 @@ "node": ">=8" } }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/log-symbols": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "dev": true, "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" @@ -2680,7 +3739,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, "engines": { "node": ">=12" }, @@ -2688,23 +3746,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/magic-string": { "version": "0.30.10", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", - "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -2713,7 +3758,6 @@ "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -2727,55 +3771,526 @@ } }, "node_modules/markdown-it-anchor": { - "version": "8.6.7", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", - "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", - "dev": true, + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-9.0.1.tgz", + "integrity": "sha512-cBt7aAzmkfX8X7FqAe8EBryiKmToXgMQEEMqkXzWCm0toDtfDYIGboKeTKd8cpNJArJtutrf+977wFJTsvNGmQ==", "peerDependencies": { "@types/markdown-it": "*", "markdown-it": "*" } }, - "node_modules/markdown-it-emoji": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-3.0.0.tgz", - "integrity": "sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg==", - "dev": true - }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true + "node_modules/markdown-it-emoji": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-3.0.0.tgz", + "integrity": "sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg==" + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.1.tgz", + "integrity": "sha512-Mx45Obds5W1UkW1nv/7dHRsbfMM1aOKA2+Pxs/IGHNonygDHwmng8xTHyS9z4KWVi0rbko8gjiBmuwwXQ7tiNA==", + "dependencies": { + "@braintree/sanitize-url": "^6.0.1", + "@types/d3-scale": "^4.0.3", + "@types/d3-scale-chromatic": "^3.0.0", + "cytoscape": "^3.28.1", + "cytoscape-cose-bilkent": "^4.1.0", + "d3": "^7.4.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.10", + "dayjs": "^1.11.7", + "dompurify": "^3.0.5", + "elkjs": "^0.9.0", + "katex": "^0.16.9", + "khroma": "^2.0.0", + "lodash-es": "^4.17.21", + "mdast-util-from-markdown": "^1.3.0", + "non-layered-tidy-tree-layout": "^2.0.2", + "stylis": "^4.1.3", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.0", + "web-worker": "^1.2.0" + } + }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -2786,22 +4301,27 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, "engines": { "node": ">=6" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "engines": { + "node": ">=4" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -2819,13 +4339,17 @@ "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "devOptional": true + }, + "node_modules/non-layered-tidy-tree-layout": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", + "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2834,7 +4358,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -2843,7 +4367,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, "dependencies": { "path-key": "^4.0.0" }, @@ -2858,7 +4381,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, "engines": { "node": ">=12" }, @@ -2870,7 +4392,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -2882,7 +4403,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -2897,7 +4417,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/ora/-/ora-8.0.1.tgz", "integrity": "sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==", - "dev": true, "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^4.0.0", @@ -2952,11 +4471,21 @@ "node": ">=6" } }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, "dependencies": { "entities": "^4.4.0" }, @@ -2968,7 +4497,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "dev": true, "dependencies": { "domhandler": "^5.0.2", "parse5": "^7.0.0" @@ -2990,7 +4518,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -2999,7 +4526,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", - "dev": true, "engines": { "node": ">=12" }, @@ -3008,25 +4534,23 @@ } }, "node_modules/photoswipe": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.3.tgz", - "integrity": "sha512-9UC6oJBK4oXFZ5HcdlcvGkfEHsVrmE4csUdCQhEjHYb3PvPLO3PG7UhnPuOgjxwmhq5s17Un5NUdum01LgBDng==", + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.4.tgz", + "integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==", "dev": true, "engines": { "node": ">= 0.12.0" } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -3044,10 +4568,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "dev": true, + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", "funding": [ { "type": "opencollective", @@ -3064,7 +4587,7 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "source-map-js": "^1.2.0" }, "engines": { @@ -3072,10 +4595,10 @@ } }, "node_modules/postcss-load-config": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-5.0.3.tgz", - "integrity": "sha512-90pBBI5apUVruIEdCxZic93Wm+i9fTrp7TXbgdUCH+/L+2WnfpITSpq5dFU/IPvbv7aNiMlQISpUkAm3fEcvgQ==", - "dev": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -3087,15 +4610,16 @@ } ], "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" + "lilconfig": "^3.1.1" }, "engines": { "node": ">= 18" }, "peerDependencies": { "jiti": ">=1.21.0", - "postcss": ">=8.0.9" + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "jiti": { @@ -3103,6 +4627,12 @@ }, "postcss": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, @@ -3110,34 +4640,26 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/preact": { - "version": "10.20.2", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.20.2.tgz", - "integrity": "sha512-S1d1ernz3KQ+Y2awUxKakpfOg2CEmJmwOP+6igPx6dgr6pgDvenqYviyokWso2rhHvGtTlWWnJDa7RaPbQerTg==", - "dev": true, - "optional": true, - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } + "devOptional": true }, - "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "dev": true, + "node_modules/pretty-ms": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.0.0.tgz", + "integrity": "sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==", + "dependencies": { + "parse-ms": "^4.0.0" + }, "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/punycode.js": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, "engines": { "node": ">=6" } @@ -3164,7 +4686,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -3180,11 +4701,18 @@ } ] }, + "node_modules/raphael": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/raphael/-/raphael-2.3.0.tgz", + "integrity": "sha512-w2yIenZAQnp257XUWGni4bLMVxpUpcIl7qgxEgDIXtmSypYtlNxfXWpOBxs7LBTps5sDwhRnrToJrMUrivqNTQ==", + "dependencies": { + "eve-raphael": "0.5.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -3211,7 +4739,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dev": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -3227,17 +4754,21 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "node_modules/rollup": { - "version": "4.14.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz", - "integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==", - "dev": true, + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "devOptional": true, "dependencies": { "@types/estree": "1.0.5" }, @@ -3249,22 +4780,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.14.3", - "@rollup/rollup-android-arm64": "4.14.3", - "@rollup/rollup-darwin-arm64": "4.14.3", - "@rollup/rollup-darwin-x64": "4.14.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.14.3", - "@rollup/rollup-linux-arm-musleabihf": "4.14.3", - "@rollup/rollup-linux-arm64-gnu": "4.14.3", - "@rollup/rollup-linux-arm64-musl": "4.14.3", - "@rollup/rollup-linux-powerpc64le-gnu": "4.14.3", - "@rollup/rollup-linux-riscv64-gnu": "4.14.3", - "@rollup/rollup-linux-s390x-gnu": "4.14.3", - "@rollup/rollup-linux-x64-gnu": "4.14.3", - "@rollup/rollup-linux-x64-musl": "4.14.3", - "@rollup/rollup-win32-arm64-msvc": "4.14.3", - "@rollup/rollup-win32-ia32-msvc": "4.14.3", - "@rollup/rollup-win32-x64-msvc": "4.14.3", + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", "fsevents": "~2.3.2" } }, @@ -3272,7 +4803,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -3291,11 +4821,31 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/sass": { - "version": "1.75.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.75.0.tgz", - "integrity": "sha512-ShMYi3WkrDWxExyxSZPst4/okE9ts46xZmJDSawJQrnte7M1V9fScVB+uNXOVKRBt0PggHOwoZcn8mYX4trnBw==", - "dev": true, + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -3309,24 +4859,15 @@ } }, "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true }, - "node_modules/search-insights": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.13.0.tgz", - "integrity": "sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "dev": true, "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" @@ -3336,13 +4877,9 @@ } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -3360,7 +4897,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -3372,21 +4908,28 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } }, + "node_modules/shiki": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.10.0.tgz", + "integrity": "sha512-YD2sXQ+TMD/F9BimV9Jn0wj35pqOvywvOG/3PB6hGHyGKlM7TJ9tyJ02jOb2kF8F0HfJwKNYrh3sW7jEcuRlXA==", + "dev": true, + "dependencies": { + "@shikijs/core": "1.10.0" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sitemap": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz", - "integrity": "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.0.tgz", + "integrity": "sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==", "dev": true, "dependencies": { "@types/node": "^17.0.5", @@ -3398,8 +4941,8 @@ "sitemap": "dist/cli.js" }, "engines": { - "node": ">=12.0.0", - "npm": ">=5.6.0" + "node": ">=14.0.0", + "npm": ">=6.0.0" } }, "node_modules/sitemap/node_modules/@types/node": { @@ -3412,7 +4955,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "dev": true, "engines": { "node": ">=14.16" }, @@ -3424,7 +4966,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/slimsearch/-/slimsearch-2.1.1.tgz", "integrity": "sha512-l1utJWal8F/RIheYk88DE2+enI12nIrn5SHt4ih/CNAH81PzkTv2GVBODlLynDJb7xan5hjd8XTL5f0L4cxLQA==", - "dev": true, "engines": { "node": ">=18.18.0" } @@ -3433,7 +4974,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3441,14 +4981,12 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "dev": true, "engines": { "node": ">=18" }, @@ -3460,7 +4998,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", - "dev": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -3477,7 +5014,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -3492,28 +5028,30 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stylis": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -3521,31 +5059,38 @@ "node": ">=8.0" } }, - "node_modules/ts-debounce": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ts-debounce/-/ts-debounce-4.0.0.tgz", - "integrity": "sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==", - "dev": true, - "optional": true, - "peer": true + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "engines": { + "node": ">=6.10" + } + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" + }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unicorn-magic": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", - "dev": true, "engines": { "node": ">=18" }, @@ -3553,11 +5098,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, "engines": { "node": ">= 10.0.0" } @@ -3566,17 +5122,16 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", - "dev": true, "engines": { "node": ">=4", "yarn": "*" } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "devOptional": true, "funding": [ { "type": "opencollective", @@ -3592,8 +5147,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -3602,13 +5157,42 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/vite": { - "version": "5.2.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.9.tgz", - "integrity": "sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==", - "dev": true, + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.2.tgz", + "integrity": "sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==", + "devOptional": true, "dependencies": { - "esbuild": "^0.20.1", + "esbuild": "^0.21.3", "postcss": "^8.4.38", "rollup": "^4.13.0" }, @@ -3658,16 +5242,15 @@ } }, "node_modules/vue": { - "version": "3.4.23", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.23.tgz", - "integrity": "sha512-X1y6yyGJ28LMUBJ0k/qIeKHstGd+BlWQEOT40x3auJFTmpIhpbKLgN7EFsqalnJXq1Km5ybDEsp6BhuWKciUDg==", - "dev": true, + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.31.tgz", + "integrity": "sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==", "dependencies": { - "@vue/compiler-dom": "3.4.23", - "@vue/compiler-sfc": "3.4.23", - "@vue/runtime-dom": "3.4.23", - "@vue/server-renderer": "3.4.23", - "@vue/shared": "3.4.23" + "@vue/compiler-dom": "3.4.31", + "@vue/compiler-sfc": "3.4.31", + "@vue/runtime-dom": "3.4.31", + "@vue/server-renderer": "3.4.31", + "@vue/shared": "3.4.31" }, "peerDependencies": { "typescript": "*" @@ -3679,10 +5262,9 @@ } }, "node_modules/vue-router": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.1.tgz", - "integrity": "sha512-D0h3oyP6vp28BOvxv2hVpiqFTjTJizCf1BuMmCibc8UW0Ll/N80SWqDd/hqPMaZfzW1j+s2s+aTRyBIP9ElzOw==", - "dev": true, + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.0.tgz", + "integrity": "sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==", "dependencies": { "@vue/devtools-api": "^6.5.1" }, @@ -3694,18 +5276,17 @@ } }, "node_modules/vuepress": { - "version": "2.0.0-rc.9", - "resolved": "https://registry.npmjs.org/vuepress/-/vuepress-2.0.0-rc.9.tgz", - "integrity": "sha512-jT1ln2lawdph+vVI6n2JfEUhQIcyc1RQWDdQu9DffhJGywJunFcumnUJudpqd1SNIES2Fz1hVCD6gdrE/rVKOQ==", - "dev": true, - "dependencies": { - "@vuepress/cli": "2.0.0-rc.9", - "@vuepress/client": "2.0.0-rc.9", - "@vuepress/core": "2.0.0-rc.9", - "@vuepress/markdown": "2.0.0-rc.9", - "@vuepress/shared": "2.0.0-rc.9", - "@vuepress/utils": "2.0.0-rc.9", - "vue": "^3.4.21" + "version": "2.0.0-rc.14", + "resolved": "https://registry.npmjs.org/vuepress/-/vuepress-2.0.0-rc.14.tgz", + "integrity": "sha512-t902FYKFF2MavNQjm/I4gN8etl6iX4PETutu4c1Pt7qQjXF6Hp2eurZaW32O5/TaYWsbVG757FwKodRLj9GDng==", + "dependencies": { + "@vuepress/cli": "2.0.0-rc.14", + "@vuepress/client": "2.0.0-rc.14", + "@vuepress/core": "2.0.0-rc.14", + "@vuepress/markdown": "2.0.0-rc.14", + "@vuepress/shared": "2.0.0-rc.14", + "@vuepress/utils": "2.0.0-rc.14", + "vue": "^3.4.29" }, "bin": { "vuepress": "bin/vuepress.js", @@ -3716,8 +5297,8 @@ "node": ">=18.16.0" }, "peerDependencies": { - "@vuepress/bundler-vite": "2.0.0-rc.9", - "@vuepress/bundler-webpack": "2.0.0-rc.9", + "@vuepress/bundler-vite": "2.0.0-rc.14", + "@vuepress/bundler-webpack": "2.0.0-rc.14", "vue": "^3.4.0" }, "peerDependenciesMeta": { @@ -3730,23 +5311,23 @@ } }, "node_modules/vuepress-plugin-components": { - "version": "2.0.0-rc.36", - "resolved": "https://registry.npmjs.org/vuepress-plugin-components/-/vuepress-plugin-components-2.0.0-rc.36.tgz", - "integrity": "sha512-JMIj+1VA+euB4TvmeUt0Fdm4inpAndzLgasadUd/C8j4Jj/99PtoTzBLR7Gxk6LaIYaR9IUsOTKPi3wnS0EOdg==", + "version": "2.0.0-rc.50", + "resolved": "https://registry.npmjs.org/vuepress-plugin-components/-/vuepress-plugin-components-2.0.0-rc.50.tgz", + "integrity": "sha512-xwMgvrsUnjJe9grUAf69IwMlfThPz0lJsx12zXUecN1I17uSbZN6Bh/2POIVm5LTxfRGMgLrQM69DwualGCH/Q==", "dev": true, "dependencies": { - "@stackblitz/sdk": "^1.9.0", - "@vuepress/helper": "2.0.0-rc.24", - "@vueuse/core": "^10.9.0", + "@stackblitz/sdk": "^1.10.0", + "@vuepress/helper": "2.0.0-rc.37", + "@vuepress/plugin-sass-palette": "2.0.0-rc.37", + "@vueuse/core": "^10.11.0", "balloon-css": "^1.2.0", - "create-codepen": "1.0.1", + "create-codepen": "^2.0.0", "qrcode": "^1.5.3", - "vue": "^3.4.21", - "vuepress-plugin-sass-palette": "2.0.0-rc.36", - "vuepress-shared": "2.0.0-rc.36" + "vue": "^3.4.29", + "vuepress-shared": "2.0.0-rc.50" }, "engines": { - "node": ">=18.16.0", + "node": ">=18.19.0", "npm": ">=8", "pnpm": ">=7", "yarn": ">=2" @@ -3757,8 +5338,8 @@ "hls.js": "^1.4.12", "mpegts.js": "^1.7.3", "sass-loader": "^14.0.0", - "vidstack": "^1.11.11", - "vuepress": "2.0.0-rc.9" + "vidstack": "^1.11.21", + "vuepress": "2.0.0-rc.14" }, "peerDependenciesMeta": { "artplayer": { @@ -3782,43 +5363,45 @@ } }, "node_modules/vuepress-plugin-md-enhance": { - "version": "2.0.0-rc.36", - "resolved": "https://registry.npmjs.org/vuepress-plugin-md-enhance/-/vuepress-plugin-md-enhance-2.0.0-rc.36.tgz", - "integrity": "sha512-FvQ4foaqsE13WEXN2TaBqtSEN3tKnkkuDX7daLbNPQ1z0ghaMgcCDJ+ehEwwcNgXSs1vlbTRYL/Tf4M4RC1nfA==", - "dev": true, - "dependencies": { - "@mdit/plugin-alert": "^0.8.0", - "@mdit/plugin-align": "^0.8.0", - "@mdit/plugin-attrs": "^0.8.0", - "@mdit/plugin-container": "^0.8.0", - "@mdit/plugin-demo": "^0.8.0", - "@mdit/plugin-figure": "^0.8.0", - "@mdit/plugin-footnote": "^0.8.0", - "@mdit/plugin-img-lazyload": "^0.8.0", - "@mdit/plugin-img-mark": "^0.8.0", - "@mdit/plugin-img-size": "^0.8.0", - "@mdit/plugin-include": "^0.8.0", - "@mdit/plugin-katex": "^0.8.0", - "@mdit/plugin-mark": "^0.8.0", - "@mdit/plugin-mathjax": "^0.8.0", - "@mdit/plugin-stylize": "^0.8.0", - "@mdit/plugin-sub": "^0.8.0", - "@mdit/plugin-sup": "^0.8.0", - "@mdit/plugin-tab": "^0.8.0", - "@mdit/plugin-tasklist": "^0.8.0", - "@mdit/plugin-tex": "^0.8.0", - "@mdit/plugin-uml": "^0.8.0", - "@types/markdown-it": "^14.0.0", - "@vuepress/helper": "2.0.0-rc.24", - "@vueuse/core": "^10.9.0", + "version": "2.0.0-rc.50", + "resolved": "https://registry.npmjs.org/vuepress-plugin-md-enhance/-/vuepress-plugin-md-enhance-2.0.0-rc.50.tgz", + "integrity": "sha512-1t570DCGDDWvc5+aPf1ERK6ZIvwY1hGKN4KOdFz7zgGzP2EFZXwwW8wioyVgKVfbjTgT2dvkyNbxykRQV6eKEA==", + "dev": true, + "dependencies": { + "@mdit/plugin-alert": "^0.12.0", + "@mdit/plugin-align": "^0.12.0", + "@mdit/plugin-attrs": "^0.12.0", + "@mdit/plugin-container": "^0.12.0", + "@mdit/plugin-demo": "^0.12.0", + "@mdit/plugin-figure": "^0.12.0", + "@mdit/plugin-footnote": "^0.12.0", + "@mdit/plugin-img-lazyload": "^0.12.0", + "@mdit/plugin-img-mark": "^0.12.0", + "@mdit/plugin-img-size": "^0.12.0", + "@mdit/plugin-include": "^0.12.0", + "@mdit/plugin-katex-slim": "^0.12.0", + "@mdit/plugin-mark": "^0.12.0", + "@mdit/plugin-mathjax-slim": "^0.12.0", + "@mdit/plugin-plantuml": "^0.12.0", + "@mdit/plugin-spoiler": "^0.12.0", + "@mdit/plugin-stylize": "^0.12.0", + "@mdit/plugin-sub": "^0.12.0", + "@mdit/plugin-sup": "^0.12.0", + "@mdit/plugin-tab": "^0.12.0", + "@mdit/plugin-tasklist": "^0.12.0", + "@mdit/plugin-tex": "^0.12.0", + "@mdit/plugin-uml": "^0.12.0", + "@types/markdown-it": "^14.1.1", + "@vuepress/helper": "2.0.0-rc.37", + "@vuepress/plugin-sass-palette": "2.0.0-rc.37", + "@vueuse/core": "^10.11.0", "balloon-css": "^1.2.0", "js-yaml": "^4.1.0", - "vue": "^3.4.21", - "vuepress-plugin-sass-palette": "2.0.0-rc.36", - "vuepress-shared": "2.0.0-rc.36" + "vue": "^3.4.29", + "vuepress-shared": "2.0.0-rc.50" }, "engines": { - "node": ">=18.16.0", + "node": ">=18.19.0", "npm": ">=8", "pnpm": ">=7", "yarn": ">=2" @@ -3831,15 +5414,15 @@ "flowchart.ts": "^2.0.0 || ^3.0.0", "katex": "^0.16.0", "kotlin-playground": "^1.23.0", - "markmap-lib": "^0.15.5 || ^0.16.0", - "markmap-toolbar": "^0.15.5 || ^0.16.0", - "markmap-view": "^0.15.5 || ^0.16.0", + "markmap-lib": "^0.17.0", + "markmap-toolbar": "^0.17.0", + "markmap-view": "^0.17.0", "mathjax-full": "^3.2.2", "mermaid": "^10.8.0", "reveal.js": "^5.0.0", "sandpack-vue3": "^3.0.0", "sass-loader": "^14.0.0", - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" }, "peerDependenciesMeta": { "@types/reveal.js": { @@ -3889,16 +5472,6 @@ } } }, - "node_modules/vuepress-plugin-md-enhance/node_modules/@types/markdown-it": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.0.1.tgz", - "integrity": "sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, "node_modules/vuepress-plugin-md-enhance/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3917,57 +5490,29 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/vuepress-plugin-sass-palette": { - "version": "2.0.0-rc.36", - "resolved": "https://registry.npmjs.org/vuepress-plugin-sass-palette/-/vuepress-plugin-sass-palette-2.0.0-rc.36.tgz", - "integrity": "sha512-kOfZHGZxfplVq/z7QtHmsrKfOlR6/s37QA/DilIYGFzj8XVL8h1eJ0ty7J1ySTZFVvDkK7r3TVVQZ2sPIEjbYQ==", - "dev": true, - "dependencies": { - "@vuepress/helper": "2.0.0-rc.24", - "chokidar": "^3.6.0", - "sass": "^1.74.1", - "vuepress-shared": "2.0.0-rc.36" - }, - "engines": { - "node": ">=18.16.0", - "npm": ">=8", - "pnpm": ">=7", - "yarn": ">=2" - }, - "peerDependencies": { - "sass-loader": "^14.0.0", - "vuepress": "2.0.0-rc.9" - }, - "peerDependenciesMeta": { - "sass-loader": { - "optional": true - } - } - }, "node_modules/vuepress-plugin-search-pro": { - "version": "2.0.0-rc.36", - "resolved": "https://registry.npmjs.org/vuepress-plugin-search-pro/-/vuepress-plugin-search-pro-2.0.0-rc.36.tgz", - "integrity": "sha512-xumvvm0/V7WrWJXMs6ZVqFjjN6h4oIEm6YrBpeteBpbG2iUkuF9E2xhVPwhrV/dYzCSkKpR7bA31qp1CngWknA==", - "dev": true, + "version": "2.0.0-rc.50", + "resolved": "https://registry.npmjs.org/vuepress-plugin-search-pro/-/vuepress-plugin-search-pro-2.0.0-rc.50.tgz", + "integrity": "sha512-MhzFjPVfuDed52d9eCEGtADw7zW+JbjYOzoNcGCUqyRazhefZC32oTitcGWdzvhzXbKOu8QeuQX+ZhiQSkDEfw==", "dependencies": { - "@vuepress/helper": "2.0.0-rc.24", - "@vueuse/core": "^10.9.0", + "@vuepress/helper": "2.0.0-rc.37", + "@vuepress/plugin-sass-palette": "2.0.0-rc.37", + "@vueuse/core": "^10.11.0", "cheerio": "1.0.0-rc.12", "chokidar": "^3.6.0", "slimsearch": "^2.1.1", - "vue": "^3.4.21", - "vuepress-plugin-sass-palette": "2.0.0-rc.36", - "vuepress-shared": "2.0.0-rc.36" + "vue": "^3.4.29", + "vuepress-shared": "2.0.0-rc.50" }, "engines": { - "node": ">=18.16.0", + "node": ">=18.19.0", "npm": ">=8", "pnpm": ">=7", "yarn": ">=2" }, "peerDependencies": { "sass-loader": "^14.0.0", - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" }, "peerDependenciesMeta": { "sass-loader": { @@ -3976,84 +5521,85 @@ } }, "node_modules/vuepress-shared": { - "version": "2.0.0-rc.36", - "resolved": "https://registry.npmjs.org/vuepress-shared/-/vuepress-shared-2.0.0-rc.36.tgz", - "integrity": "sha512-a4XLodJk5U8qeon7jNqsyLGNUgOAdVr8YBZD7E9BGiu84+S3P6e5SCJLCLQ17v37jwdLoYwT2Fv67k586GAx7w==", - "dev": true, + "version": "2.0.0-rc.50", + "resolved": "https://registry.npmjs.org/vuepress-shared/-/vuepress-shared-2.0.0-rc.50.tgz", + "integrity": "sha512-iOf4r4LpdXVvFJb2Zh1rmXJW8lrq3ZyJv0bTKGun4yhBKE9sW5ZAt9rT/eVEFMDPLJLFYBfRmF4MZ9c4x8BL6A==", "dependencies": { - "@vuepress/helper": "2.0.0-rc.24", - "@vueuse/core": "^10.9.0", + "@vuepress/helper": "2.0.0-rc.37", + "@vueuse/core": "^10.11.0", "cheerio": "1.0.0-rc.12", - "dayjs": "^1.11.10", - "execa": "^8.0.1", + "dayjs": "^1.11.11", + "execa": "^9.2.0", "fflate": "^0.8.2", "gray-matter": "^4.0.3", - "semver": "^7.6.0", - "vue": "^3.4.21" + "semver": "^7.6.2", + "vue": "^3.4.29" }, "engines": { - "node": ">=18.16.0", + "node": ">=18.19.0", "npm": ">=8", "pnpm": ">=7", "yarn": ">=2" }, "peerDependencies": { - "vuepress": "2.0.0-rc.9" + "vuepress": "2.0.0-rc.14" } }, "node_modules/vuepress-theme-hope": { - "version": "2.0.0-rc.36", - "resolved": "https://registry.npmjs.org/vuepress-theme-hope/-/vuepress-theme-hope-2.0.0-rc.36.tgz", - "integrity": "sha512-kMCmlwPw4S0P0LYnAulaT98L3Z2jD+A4bgQ93rfuCVxmfCJEUs99V6ms9iu/GZ8CqJC10anDqwfJ+SjHCs0V8g==", - "dev": true, - "dependencies": { - "@vuepress/helper": "2.0.0-rc.24", - "@vuepress/plugin-active-header-links": "2.0.0-rc.21", - "@vuepress/plugin-back-to-top": "2.0.0-rc.24", - "@vuepress/plugin-blog": "2.0.0-rc.24", - "@vuepress/plugin-catalog": "2.0.0-rc.24", - "@vuepress/plugin-comment": "2.0.0-rc.24", - "@vuepress/plugin-copy-code": "2.0.0-rc.24", - "@vuepress/plugin-copyright": "2.0.0-rc.24", - "@vuepress/plugin-external-link-icon": "2.0.0-rc.24", - "@vuepress/plugin-git": "2.0.0-rc.22", - "@vuepress/plugin-links-check": "2.0.0-rc.24", - "@vuepress/plugin-nprogress": "2.0.0-rc.21", - "@vuepress/plugin-photo-swipe": "2.0.0-rc.24", - "@vuepress/plugin-prismjs": "2.0.0-rc.21", - "@vuepress/plugin-reading-time": "2.0.0-rc.24", - "@vuepress/plugin-rtl": "2.0.0-rc.21", - "@vuepress/plugin-seo": "2.0.0-rc.24", - "@vuepress/plugin-sitemap": "2.0.0-rc.24", - "@vuepress/plugin-theme-data": "2.0.0-rc.21", - "@vueuse/core": "^10.9.0", + "version": "2.0.0-rc.50", + "resolved": "https://registry.npmjs.org/vuepress-theme-hope/-/vuepress-theme-hope-2.0.0-rc.50.tgz", + "integrity": "sha512-Bgf9JQC/ae+wZ0TJz6LuH9772NJhvaytP705aeNRnLDnlML0yW7laIod67wCsaF3KzCRnOuZ86VvQGNMojo3Bw==", + "dev": true, + "dependencies": { + "@vuepress/helper": "2.0.0-rc.37", + "@vuepress/plugin-active-header-links": "2.0.0-rc.37", + "@vuepress/plugin-back-to-top": "2.0.0-rc.37", + "@vuepress/plugin-blog": "2.0.0-rc.37", + "@vuepress/plugin-catalog": "2.0.0-rc.37", + "@vuepress/plugin-comment": "2.0.0-rc.37", + "@vuepress/plugin-copy-code": "2.0.0-rc.37", + "@vuepress/plugin-copyright": "2.0.0-rc.37", + "@vuepress/plugin-git": "2.0.0-rc.37", + "@vuepress/plugin-links-check": "2.0.0-rc.37", + "@vuepress/plugin-notice": "2.0.0-rc.37", + "@vuepress/plugin-nprogress": "2.0.0-rc.37", + "@vuepress/plugin-photo-swipe": "2.0.0-rc.37", + "@vuepress/plugin-reading-time": "2.0.0-rc.37", + "@vuepress/plugin-rtl": "2.0.0-rc.37", + "@vuepress/plugin-sass-palette": "2.0.0-rc.37", + "@vuepress/plugin-seo": "2.0.0-rc.37", + "@vuepress/plugin-shiki": "2.0.0-rc.37", + "@vuepress/plugin-sitemap": "2.0.0-rc.37", + "@vuepress/plugin-theme-data": "2.0.0-rc.37", + "@vuepress/plugin-watermark": "2.0.0-rc.37", + "@vueuse/core": "^10.11.0", "balloon-css": "^1.2.0", "bcrypt-ts": "^5.0.2", "cheerio": "1.0.0-rc.12", "chokidar": "^3.6.0", "gray-matter": "^4.0.3", - "vue": "^3.4.21", - "vuepress-plugin-components": "2.0.0-rc.36", - "vuepress-plugin-md-enhance": "2.0.0-rc.36", - "vuepress-plugin-sass-palette": "2.0.0-rc.36", - "vuepress-shared": "2.0.0-rc.36" + "vue": "^3.4.29", + "vuepress-plugin-components": "2.0.0-rc.50", + "vuepress-plugin-md-enhance": "2.0.0-rc.50", + "vuepress-shared": "2.0.0-rc.50" }, "engines": { - "node": ">=18.16.0", + "node": ">=18.19.0", "npm": ">=8", "pnpm": ">=7", "yarn": ">=2" }, "peerDependencies": { - "@vuepress/plugin-docsearch": "2.0.0-rc.24", - "@vuepress/plugin-feed": "2.0.0-rc.24", - "@vuepress/plugin-pwa": "2.0.0-rc.24", - "@vuepress/plugin-redirect": "2.0.0-rc.24", - "@vuepress/plugin-search": "2.0.0-rc.24", + "@vuepress/plugin-docsearch": "2.0.0-rc.37", + "@vuepress/plugin-feed": "2.0.0-rc.37", + "@vuepress/plugin-prismjs": "2.0.0-rc.37", + "@vuepress/plugin-pwa": "2.0.0-rc.37", + "@vuepress/plugin-redirect": "2.0.0-rc.37", + "@vuepress/plugin-search": "2.0.0-rc.37", "nodejs-jieba": "^0.1.2", "sass-loader": "^14.0.0", - "vuepress": "2.0.0-rc.9", - "vuepress-plugin-search-pro": "2.0.0-rc.36" + "vuepress": "2.0.0-rc.14", + "vuepress-plugin-search-pro": "2.0.0-rc.50" }, "peerDependenciesMeta": { "@vuepress/plugin-docsearch": { @@ -4062,6 +5608,9 @@ "@vuepress/plugin-feed": { "optional": true }, + "@vuepress/plugin-prismjs": { + "optional": true + }, "@vuepress/plugin-pwa": { "optional": true }, @@ -4082,11 +5631,24 @@ } } }, + "node_modules/watermark-js-plus": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/watermark-js-plus/-/watermark-js-plus-1.5.1.tgz", + "integrity": "sha512-fj82XgrJkqP0a7kcG6U591YT6E0GFq9zdUrqbijbkE9l8aHhOpOogQwk5jaoTaWVqQsBSNFRIc6QWRkHBfHNsA==", + "dev": true, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/web-worker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz", + "integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -4164,24 +5726,6 @@ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yaml": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz", - "integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==", - "dev": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", @@ -4257,6 +5801,25 @@ "engines": { "node": ">=8" } + }, + "node_modules/yoctocolors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.0.tgz", + "integrity": "sha512-FsQpXXeOEe05tcJN4Z2eicuC6+6KiJdBbPOAChanSkwwjZ277XGsh8wh/HaPuGeifTiw/7dgAzabitu2bnDvRg==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zrender": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz", + "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==", + "dependencies": { + "tslib": "2.3.0" + } } } } diff --git a/package.json b/package.json index b9519f07..77a21b83 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,19 @@ "docs:update-package": "npx vp-update" }, "devDependencies": { - "@vuepress/bundler-vite": "2.0.0-rc.9", - "vue": "^3.4.23", - "vuepress": "2.0.0-rc.9", - "vuepress-plugin-search-pro": "^2.0.0-rc.39", - "vuepress-theme-hope": "2.0.0-rc.40" + "@vuepress/bundler-vite": "2.0.0-rc.14", + "@vuepress/plugin-search": "^2.0.0-rc.37", + "vue": "^3.4.27", + "vuepress": "2.0.0-rc.14", + "vuepress-theme-hope": "2.0.0-rc.50" + }, + "dependencies": { + "chart.js": "^4.4.3", + "echarts": "^5.5.1", + "flowchart": "^1.2.0", + "flowchart.ts": "^3.0.0", + "katex": "^0.16.10", + "mermaid": "^10.9.1", + "vuepress-plugin-search-pro": "^2.0.0-rc.50" } }