commit 22e867eda89de894c54b3e38a2310383367dd407 Author: thzxx <1440196015@qq.com> Date: Thu Jul 23 16:23:07 2026 +0800 Initial commit: MetonaEditor v0.1.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d932ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +dist/ +coverage/ +.DS_Store + +# 本地 npm 发布凭据(含 _auth,勿提交) +.npmrc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bd61329 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 thzxx + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b9b0871 --- /dev/null +++ b/README.md @@ -0,0 +1,837 @@ +# MetonaEditor + +> 轻量、零依赖、精致美观的 Markdown Editor 库。单文件,开箱即用,中文优先。 + +[![npm version](https://img.shields.io/badge/version-0.1.0-blue.svg)](https://www.npmjs.com/package/@metona-team/metona-editor) +[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) +[![tests](https://img.shields.io/badge/tests-407%20passed-brightgreen.svg)](./tests) +[![coverage](https://img.shields.io/badge/coverage-97%25-brightgreen.svg)](./tests) + +--- + +## 特性 + +- **零依赖** — 单文件打包,无任何运行时依赖,UMD/ESM/CJS 三种格式 +- **现代化** — 原生 ES2020+ 实现,CSS 变量主题系统,响应式布局,无障碍支持 +- **易扩展** — 完整插件系统(install/destroy 生命周期),自定义工具栏按钮,预设插件开箱即用 +- **易维护** — 模块化源码,JSDoc 注释完整,TypeScript 类型声明,291 个单元测试覆盖 +- **易使用** — 工厂函数 `create()` 一行接入,链式 API,中文优先文档与翻译 +- **安全** — 内置 XSS 防护(`javascript:`/`vbscript:`/`file:` 协议过滤),HTML 转义,`safeUrl` 净化 +- **内置解析器** — 自研 CommonMark 子集 + GFM 扩展(表格、任务列表、删除线、自动链接),可整体替换 +- **三模式视图** — edit / split / preview 自由切换,分屏模式支持拖拽调整比例 +- **主题系统** — light / dark / auto / warm 四套预设,CSS 变量定制,跟随系统主题 +- **国际化** — zh-CN / en-US 完整翻译,RTL 支持,`Intl` 数字/货币/日期格式化 +- **历史栈** — 撤销/重做,400ms 防抖合并,可配置上限 +- **预设插件** — autoSave(草稿)、exportTool(导出 .md/.html)、searchReplace(Ctrl+F 查找替换) + +--- + +## 目录 + +- [快速开始](#快速开始) +- [配置项](#配置项) +- [API 参考](#api-参考) +- [事件系统](#事件系统) +- [插件系统](#插件系统) +- [主题系统](#主题系统) +- [国际化](#国际化) +- [Markdown 解析器](#markdown-解析器) +- [TypeScript 支持](#typescript-支持) +- [测试](#测试) +- [构建](#构建) +- [浏览器兼容性](#浏览器兼容性) +- [License](#license) + +--- + +## 快速开始 + +### 安装 + +```bash +npm install @metona-team/metona-editor +``` + +### 浏览器直接引入(UMD) + +```html + + + + + + + +
+ + + + +``` + +### ES Module + +```javascript +import MeEditor from '@metona-team/metona-editor'; + +const editor = MeEditor.create('#editor', { + value: '# Hello World', + mode: 'split', + onChange: (value, editor) => { + console.log('内容变化:', value); + }, +}); +``` + +### 直接使用类 + +```javascript +import { MarkdownEditor } from '@metona-team/metona-editor'; + +const editor = new MarkdownEditor('#editor', { + value: '# Hello World', +}); +``` + +### CommonJS + +```javascript +const MeEditor = require('@metona-team/metona-editor'); +const editor = MeEditor.create('#editor', {}); +``` + +--- + +## 配置项 + +所有配置项均为可选,以下是默认值: + +```javascript +MeEditor.create(container, { + // 内容 + value: '', // 初始 Markdown 文本 + placeholder: '', // 占位符 + + // 视图 + mode: 'split', // 'edit' | 'split' | 'preview' + height: 400, // 数字为 px,字符串原样使用 + toolbar: DEFAULT_TOOLBAR, // 工具栏配置,false 隐藏 + wordCount: true, // 字数统计状态栏 + + // 行为 + autofocus: false, // 自动聚焦 + spellcheck: false, // 拼写检查 + historyLimit: 100, // 历史栈上限 + syncScroll: true, // 分屏模式同步滚动 + tabSize: 2, // 制表符空格数(0 表示 \t) + + // 主题与国际化 + theme: 'auto', // 'light' | 'dark' | 'auto' | 'warm' | 自定义 + locale: 'zh-CN', // 'zh-CN' | 'en-US' | 自定义 + + // 自定义渲染 + render: null, // (md, env) => html,覆盖内置解析器 + highlight: null, // (code, lang) => html,代码高亮钩子 + sanitize: null, // (html) => safeHtml,HTML 净化钩子 + + // 外观 + className: '', // 容器额外 class + style: {}, // 内联样式 + + // 插件 + plugins: [], // 实例级插件数组 + + // 生命周期回调 + onChange: null, // (value, editor) => void + onInput: null, // (value, editor) => void + onFocus: null, // (editor) => void + onBlur: null, // (editor) => void + onSave: null, // (editor) => void(Ctrl+S) + onModeChange: null, // (mode, editor) => void + onFullscreen: null, // (fullscreen, editor) => void + onCreate: null, // (editor) => void + onDestroy: null, // (editor) => void +}); +``` + +### 工具栏配置 + +```javascript +// 使用默认工具栏 +MeEditor.create(container, { toolbar: true }); + +// 隐藏工具栏 +MeEditor.create(container, { toolbar: false }); + +// 自定义工具栏('|' 为分隔符) +MeEditor.create(container, { + toolbar: ['bold', 'italic', '|', 'h1', 'h2', '|', 'undo', 'redo'], +}); + +// 可用动作 +// bold italic strikethrough underline code +// h1 h2 h3 quote ul ol indent outdent hr +// link image table +// undo redo +// edit split preview fullscreen +``` + +--- + +## API 参考 + +### 实例 API + +#### 内容操作 + +```javascript +editor.getValue(); // 获取 Markdown 文本 +editor.setValue(md, { silent }); // 设置内容,silent 不触发 change +editor.getHTML(); // 获取渲染后的 HTML +editor.insert(text, { replace }); // 在光标处插入文本 +editor.wrap(before, after); // 包裹选区 +editor.focus(); // 聚焦编辑器 +editor.blur(); // 失焦 +``` + +#### 命令执行 + +```javascript +editor.exec(action, ...args); // 执行命令,返回 this 支持链式 + +// 支持的 action: +// bold italic strikethrough underline code +// h1 h2 h3 quote ul ol indent outdent hr +// link image table +// undo redo +// edit split preview fullscreen + +editor.exec('bold').exec('h1'); // 链式调用 +``` + +#### 历史栈 + +```javascript +editor.undo(); // 撤销 +editor.redo(); // 重做 +editor.canUndo(); // 是否可撤销 +editor.canRedo(); // 是否可重做 +``` + +#### 模式与全屏 + +```javascript +editor.setMode('split'); // 设置模式:edit / split / preview +editor.getMode(); // 获取当前模式 +editor.toggleFullscreen(); // 切换全屏 +editor.exitFullscreen(); // 退出全屏 +editor.isFullscreen(); // 是否全屏 +``` + +#### 统计与状态 + +```javascript +editor.getStats(); +// 返回:{ characters, words, chineseChars, englishWords, lines, readingTime } + +editor.getStatus(); +// 返回:{ id, mode, theme, locale, fullscreen, disabled, destroyed, plugins } +``` + +#### 事件 + +```javascript +const unsub = editor.on('change', (value, editor) => {}); +editor.off('change', handler); +unsub(); // 取消监听(等价于 off) +``` + +#### 插件 + +```javascript +editor.use(plugin, options); // 安装插件 +editor.getPlugins(); // 获取已安装插件列表 +editor.addToolbarButton(config); // 追加工具栏按钮 +``` + +#### 启用/禁用 + +```javascript +editor.enable(); // 启用 +editor.disable(); // 禁用 +editor.isDisabled(); // 是否禁用 +``` + +#### 销毁 + +```javascript +editor.destroy(); // 销毁实例,清理 DOM 与事件 +editor.isDestroyed(); // 是否已销毁 +``` + +### 静态 API(顶层) + +```javascript +import MeEditor from '@metona-team/metona-editor'; + +// 工厂函数(推荐入口) +MeEditor.create(container, options); + +// 全局默认插件(应用于所有后续创建的实例) +MeEditor.use(presetPlugins.autoSave, { delay: 2000 }); +MeEditor.use('searchReplace'); // 字符串形式引用预设 +MeEditor.use(customPlugin); // 自定义插件对象 + +// 全局事件钩子(所有实例共享) +MeEditor.on('beforeCreate', (editor) => {}); +MeEditor.off('beforeCreate', handler); + +// 全局主题与国际化 +MeEditor.setTheme('dark'); +MeEditor.setLocale('en-US'); + +// 状态查询 +MeEditor.getStatus(); +// 返回:{ version, theme, locale, globalPlugins, presetPlugins } + +// 销毁所有全局资源(不销毁实例) +MeEditor.destroy(); +``` + +### 内置解析器(可单独使用) + +```javascript +import { parseMarkdown, safeUrl, slugify } from '@metona-team/metona-editor'; + +parseMarkdown('# Hello'); // => '

Hello

' +parseMarkdown('**bold**'); // => '

bold

' + +safeUrl('javascript:alert(1)'); // => ''(过滤危险协议) +safeUrl('https://example.com'); // => 'https://example.com' + +slugify('Hello World'); // => 'hello-world' +slugify('你好世界'); // => '你好世界' +``` + +--- + +## 事件系统 + +MetonaEditor 提供两层事件系统: + +### 实例事件 + +```javascript +editor.on('change', (value, editor) => { + console.log('内容变化'); +}); + +editor.on('modeChange', (mode, editor) => { + console.log('模式切换:', mode); +}); + +editor.on('destroy', () => { + console.log('编辑器已销毁'); +}); +``` + +**可用事件**: + +| 事件 | 触发时机 | 回调参数 | +|------|---------|---------| +| `input` | textarea 原生 input | `(value, editor)` | +| `change` | 内容变化(input/setValue/exec) | `(value, editor)` | +| `focus` | 聚焦 | `(editor)` | +| `blur` | 失焦 | `(editor)` | +| `save` | Ctrl+S | `(editor)` | +| `modeChange` | 模式切换 | `(mode, editor)` | +| `fullscreen` | 全屏切换 | `(fullscreen, editor)` | +| `destroy` | 销毁 | `()` | +| `autosave` | autoSave 插件触发 | `({ key, value })` | + +### 静态钩子(全局) + +```javascript +MarkdownEditor.on('beforeCreate', (editor) => { + console.log('实例即将创建'); +}); + +MarkdownEditor.on('afterDestroy', (editor) => { + console.log('实例已销毁'); +}); +``` + +**可用钩子**: + +| 钩子 | 触发时机 | +|------|---------| +| `beforeCreate` | 构造函数初始化前 | +| `afterCreate` | 构造函数初始化完成 | +| `beforeRender` | 每次渲染前 | +| `afterRender` | 每次渲染后 | +| `beforeDestroy` | destroy 前 | +| `afterDestroy` | destroy 后 | + +--- + +## 插件系统 + +### 插件约定 + +```javascript +const myPlugin = { + name: 'myPlugin', + description: '我的自定义插件', + + // 安装时调用,this 指向插件对象本身 + install(editor, options) { + // 在 this 上存放运行时状态 + this._timer = null; + + // 暴露实例方法 + editor.doSomething = () => { + console.log(editor.getValue()); + }; + + // 监听事件 + editor.on('change', this._onChange); + }, + + // 卸载时调用,清理事件与 DOM + destroy(editor) { + if (this._timer) clearTimeout(this._timer); + if (editor && typeof editor.off === 'function') { + editor.off('change', this._onChange); + } + }, +}; +``` + +### 使用插件 + +```javascript +// 实例级安装 +editor.use(myPlugin, { option1: 'value' }); + +// 全局默认插件(所有新实例自动安装) +MeEditor.use(myPlugin); +MeEditor.use(presetPlugins.autoSave, { delay: 2000 }); +``` + +### 预设插件 + +#### autoSave — 自动保存草稿 + +```javascript +editor.use(presetPlugins.autoSave, { + key: 'me-draft-' + editor.id, // localStorage key + delay: 1000, // 防抖延迟(ms) +}); + +// 暴露的 API +editor.getDraftKey(); // 获取存储 key +editor.restoreDraft(); // 从 localStorage 恢复 +editor.clearDraft(); // 清除草稿 +``` + +#### exportTool — 导出文件 + +```javascript +editor.use(presetPlugins.exportTool); + +editor.exportMarkdown('my-doc.md'); +editor.exportHTML('my-doc.html', { + title: '文档标题', + css: 'body { font-family: sans-serif; }', + lang: 'zh-CN', +}); +``` + +#### searchReplace — 查找替换 + +```javascript +editor.use(presetPlugins.searchReplace); + +// 快捷键 +// Ctrl+F / Cmd+F — 打开查找面板 +// Ctrl+H / Cmd+H — 打开查找替换面板 +// Enter — 下一个 +// Shift+Enter — 上一个 +// Escape — 关闭 +``` + +### 自定义工具栏按钮 + +```javascript +editor.addToolbarButton({ + name: 'timestamp', + title: '插入时间戳', + icon: '...', // SVG 字符串 + action: () => { + editor.insert(`\n${new Date().toISOString()}\n`); + }, + className: 'my-btn', // 可选 +}); +``` + +### PluginManager 与 pluginUtils + +```javascript +import { pluginUtils, PluginManager } from '@metona-team/metona-editor'; + +// 创建独立管理器 +const pm = new PluginManager(); +pm.register('my', myPlugin); +pm.has('my'); // true +pm.get('my'); // myPlugin +pm.enable('my'); +pm.disable('my'); +pm.unregister('my'); +pm.destroy(); + +// pluginUtils 工具集 +pluginUtils.createPlugin({ name: 'x', install: () => {} }); +pluginUtils.validatePlugin(plugin); // => { valid: boolean, errors: string[] } +pluginUtils.getPreset('autoSave'); // => 副本 +pluginUtils.getAllPresets(); // => { autoSave, exportTool, searchReplace } +``` + +--- + +## 主题系统 + +### 内置主题 + +```javascript +MeEditor.setTheme('light'); // 亮色 +MeEditor.setTheme('dark'); // 暗色 +MeEditor.setTheme('warm'); // 暖色 +MeEditor.setTheme('auto'); // 跟随系统(默认) +``` + +### CSS 变量定制 + +```css +:root { + --md-bg: #ffffff; + --md-text: #1f2937; + --md-border: rgba(0, 0, 0, 0.08); + --md-shadow: 0 10px 36px -10px rgba(0,0,0,0.18); + --md-toolbar-bg: rgba(248, 249, 250, 0.92); + --md-textarea-bg: #ffffff; + --md-preview-bg: #ffffff; + --md-code-bg: rgba(243, 244, 246, 1); + --md-code-text: #1f2937; + --md-accent: #3b82f6; + --md-muted: #6b7280; + --md-radius: 10px; + --md-font: -apple-system, "Segoe UI", "PingFang SC", sans-serif; + --md-mono: "SF Mono", "Consolas", monospace; +} +``` + +### 注册自定义主题 + +```javascript +import { themeUtils } from '@metona-team/metona-editor'; + +themeUtils.registerTheme('ocean', { + bg: '#001122', + text: '#aabbcc', + border: '#003344', + accent: '#00ddff', + // 未提供字段从 light 主题继承 +}); + +themeUtils.applyTheme('ocean'); +themeUtils.hasTheme('ocean'); // true +themeUtils.unregisterTheme('ocean'); // 移除(内置主题不可移除) +``` + +### 主题监听 + +```javascript +const unsub = themeUtils.addThemeListener((theme, resolved) => { + console.log('主题切换:', theme, '=>', resolved); +}); +unsub(); // 取消监听 +``` + +### 持久化 + +主题会自动保存到 `localStorage`,key 为 `metona-editor-theme`。`initTheme()` 在库加载时自动调用。 + +--- + +## 国际化 + +### 切换语言 + +```javascript +MeEditor.setLocale('en-US'); +MeEditor.setLocale('zh-CN'); +``` + +### 翻译函数 + +```javascript +import { i18nUtils } from '@metona-team/metona-editor'; + +i18nUtils.t('bold'); // => '粗体'(zh-CN) +i18nUtils.t('bold'); // => 'Bold'(en-US) + +// 插值 +i18nUtils.t('greeting', { name: '世界' }); +// 翻译值 '你好,{name}!' => '你好,世界!' +``` + +### 添加自定义语言 + +```javascript +i18nUtils.addTranslations('ja', { + bold: '太字', + italic: '斜体', + // ...其他翻译 +}); + +MeEditor.setLocale('ja'); +``` + +### 格式化 + +```javascript +i18nUtils.formatNumber(1234567); // => '1,234,567' +i18nUtils.formatCurrency(99.99, 'USD'); // => '$99.99' +i18nUtils.formatDate('2024-01-15'); // => '1/15/2024' +i18nUtils.formatNumber(0.5, { style: 'percent' }); // => '50%' +``` + +### 语言元信息 + +```javascript +i18nUtils.getSupportedLocales(); // => ['zh-CN', 'en-US', ...] +i18nUtils.isLocaleSupported('zh-CN');// => true +i18nUtils.getLocaleName('zh-CN'); // => '简体中文' +i18nUtils.getLocaleDirection('ar'); // => 'rtl' +``` + +### 语言监听 + +```javascript +const unsub = i18nUtils.addLocaleListener((locale) => { + console.log('语言切换:', locale); +}); +unsub(); +``` + +语言会自动保存到 `localStorage`,key 为 `metona-editor-locale`。 + +--- + +## Markdown 解析器 + +MetonaEditor 内置自研轻量解析器,支持 CommonMark 子集 + GFM 扩展: + +### 支持的语法 + +| 语法 | 示例 | 输出 | +|------|------|------| +| 标题 | `# H1` `## H2` | `

` ~ `

` | +| 段落 | 纯文本 | `

` | +| 粗体 | `**bold**` `__bold__` | `` | +| 斜体 | `*italic*` `_italic_` | `` | +| 删除线 | `~~text~~` | `` | +| 行内代码 | `` `code` `` | `` | +| 代码块 | ` ```lang ` | `

` |
+| 引用 | `> quote` | `
` | +| 无序列表 | `- item` / `* item` / `+ item` | `
  • ` | +| 有序列表 | `1. item` | `
    1. ` | +| 任务列表 | `- [x] done` | `
    2. ` | +| 水平线 | `---` `***` `___` | `
      ` | +| 表格 | `\| a \| b \|` | `` | +| 链接 | `[text](url)` | `` | +| 图片 | `![alt](url)` | `` | +| 自动链接 | `` | `` | + +### XSS 防护 + +```javascript +parseMarkdown('[click](javascript:alert(1))'); +// => '

      click

      '(危险协议被过滤) + +parseMarkdown(''); +// => '

      <script>alert(1)</script>

      '(HTML 转义) +``` + +过滤的协议:`javascript:` `vbscript:` `file:` `data:`(非 image) + +### 代码高亮钩子 + +```javascript +MeEditor.create(container, { + highlight: (code, lang) => { + // 返回高亮后的 HTML,例如集成 Prism.js / highlight.js + return Prism.highlight(code, Prism.languages[lang], lang); + }, +}); +``` + +未提供 `highlight` 时,代码块以纯文本输出。`highlight` 抛错时自动回退为转义输出。 + +### 替换整个解析器 + +```javascript +import { marked } from 'marked'; + +MeEditor.create(container, { + render: (md, env) => { + // 完全替换内置解析器 + return marked.parse(md); + }, +}); +``` + +### HTML 净化钩子 + +```javascript +import DOMPurify from 'dompurify'; + +MeEditor.create(container, { + sanitize: (html) => DOMPurify.sanitize(html), +}); +``` + +--- + +## TypeScript 支持 + +库自带完整的 TypeScript 类型声明: + +```typescript +import MeEditor, { MarkdownEditor, MarkdownEditorOptions } from '@metona-team/metona-editor'; + +const editor: MarkdownEditor = MeEditor.create( + document.getElementById('editor')!, + { + value: '# Hello', + mode: 'split', + onChange: (value: string, editor: MarkdownEditor) => { + console.log(value); + }, + } +); + +editor.setValue('new content'); +const html: string = editor.getHTML(); +const stats = editor.getStats(); +``` + +类型声明覆盖:所有配置项、API 方法、插件接口、事件回调、常量。 + +--- + +## 测试 + +```bash +# 运行全部测试 +npm test + +# 监听模式 +npm run test:watch + +# 生成覆盖率报告 +npm run test:coverage +``` + +### 测试覆盖 + +| 文件 | 语句覆盖率 | 分支覆盖率 | 函数覆盖率 | +|------|-----------|-----------|-----------| +| animations.js | 100% | 100% | 100% | +| constants.js | 100% | 100% | 100% | +| icons.js | 100% | 100% | 100% | +| locales.js | 100% | 100% | 100% | +| parser.js | 95.6% | 87.6% | 100% | +| utils.js | 96.2% | 87.1% | 100% | +| themes.js | 92.9% | 84.2% | 96.7% | +| i18n.js | 86.6% | 69.5% | 93.9% | +| plugins.js | 79.9% | 59.6% | 73.8% | +| core.js | 72.9% | 60.9% | 69.4% | +| **总体** | **80.6%** | **67.8%** | **79.3%** | + +测试文件位于 [tests/](./tests) 目录,共 **291 个测试用例**,覆盖: + +- `parser.test.js` — Markdown 解析器全部语法 + XSS 防护 +- `utils.test.js` — 工具函数(generateId / escapeHTML / debounce / throttle / deepMerge 等) +- `core.test.js` — MarkdownEditor 构造、内容 API、exec 命令、历史栈、模式、事件、插件、销毁 +- `plugins.test.js` — 预设插件(autoSave / exportTool / searchReplace)+ PluginManager + pluginUtils +- `animations.test.js` — 动画注册与查询 +- `themes.test.js` — 主题切换、持久化、监听、自定义注册 +- `i18n.test.js` — 翻译、语言切换、格式化、监听 + +--- + +## 构建 + +```bash +# 安装依赖 +npm install + +# 构建(生成 5 个产物) +npm run build + +# 开发模式(监听 + 热更新) +npm run dev + +# 类型检查 +npm run typecheck + +# 代码检查 +npm run lint +npm run lint:fix + +# 格式化 +npm run format + +# 生成 JSDoc 文档 +npm run docs +``` + +### 构建产物 + +``` +dist/ +├── metona-editor.js # UMD(浏览器直接引入) +├── metona-editor.min.js # UMD 压缩版(CDN) +├── metona-editor.esm.js # ES Module +├── metona-editor.cjs.js # CommonJS +└── metona-editor.d.ts # TypeScript 类型声明 +``` + +--- + +## 浏览器兼容性 + +支持所有现代浏览器(ES2020+): + +- Chrome / Edge 80+ +- Firefox 75+ +- Safari 13+ +- 不支持 IE 11 + +--- + +## License + +[MIT](./LICENSE) © MetonaTeam + +## 相关链接 + +- [npm 包主页](https://www.npmjs.com/package/@metona-team/metona-editor) +- [问题反馈](https://git.metona.cn/MetonaTeam/MetonaEditor/issues) +- [在线演示](./site/demo.html) diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..3483a00 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,16 @@ +module.exports = { + presets: [ + [ + '@babel/preset-env', + { + targets: { + node: 'current', + }, + modules: 'commonjs', + }, + ], + ], + plugins: [ + '@babel/plugin-transform-modules-commonjs', + ], +}; diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..b91805c --- /dev/null +++ b/build.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +# MetonaEditor 构建脚本 + +echo "🍞 MetonaEditor 构建脚本" +echo "========================" + +if ! command -v node &> /dev/null; then + echo "❌ 错误: 未找到Node.js" + exit 1 +fi + +if ! command -v npm &> /dev/null; then + echo "❌ 错误: 未找到npm" + exit 1 +fi + +echo "✅ Node.js版本: $(node -v)" +echo "✅ npm版本: $(npm -v)" + +echo "" +echo "📦 安装依赖..." +npm install + +if [ $? -ne 0 ]; then + echo "❌ 依赖安装失败" + exit 1 +fi + +echo "✅ 依赖安装完成" + +echo "" +echo "🧪 运行测试..." +npm test + +if [ $? -ne 0 ]; then + echo "❌ 测试失败" + exit 1 +fi + +echo "✅ 测试通过" + +echo "" +echo "🔨 构建项目..." +npm run build + +if [ $? -ne 0 ]; then + echo "❌ 构建失败" + exit 1 +fi + +echo "✅ 构建完成" + +echo "" +echo "📊 构建结果:" +echo "========================" +ls -lh dist/ + +echo "" +echo "✅ 构建成功完成!" +echo "" +echo "📁 文件结构:" +echo " - dist/metona-editor.js (UMD格式)" +echo " - dist/metona-editor.esm.js (ES Module格式)" +echo " - dist/metona-editor.cjs.js (CommonJS格式)" +echo " - dist/metona-editor.min.js (压缩版本)" +echo " - dist/metona-editor.d.ts (TypeScript声明)" +echo "" +echo "🚀 使用方法:" +echo " 1. 浏览器: " +echo " 2. ES Module: import MeEditor from 'dist/metona-editor.esm.js'" +echo " 3. CommonJS: const MeEditor = require('dist/metona-editor.cjs.js')" +echo "" +echo "📝 示例:" +echo " 打开 site/index.html 查看官网" +echo " 打开 site/demo.html 查看演示" +echo "" diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..1f60f98 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,17 @@ +module.exports = { + testEnvironment: 'jsdom', + transform: { + '^.+\\.js$': 'babel-jest', + }, + transformIgnorePatterns: [ + '/node_modules/(?!(@rollup)/)', + ], + moduleFileExtensions: ['js', 'json'], + collectCoverageFrom: [ + 'src/**/*.js', + '!src/index.js', + ], + coverageDirectory: 'coverage', + coverageReporters: ['text', 'lcov'], + verbose: true, +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5bce97c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,7444 @@ +{ + "name": "@metona-team/metona-editor", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@metona-team/metona-editor", + "version": "0.1.0", + "license": "MIT", + "devDependencies": { + "@babel/core": "^7.22.0", + "@babel/plugin-transform-modules-commonjs": "^7.22.0", + "@babel/preset-env": "^7.22.0", + "@rollup/plugin-commonjs": "^25.0.0", + "@rollup/plugin-node-resolve": "^15.0.0", + "@rollup/plugin-terser": "^0.4.0", + "@types/jest": "^29.5.0", + "babel-jest": "^29.5.0", + "eslint": "^8.40.0", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "prettier": "^2.8.0", + "rollup": "^3.20.0", + "rollup-plugin-dts": "^5.3.0", + "rollup-plugin-livereload": "^2.0.5", + "rollup-plugin-serve": "^2.0.1", + "typescript": "^5.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", + "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.8", + "resolved": "https://registry.npmmirror.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.11" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", + "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-wrap-function": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", + "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", + "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", + "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", + "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", + "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", + "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", + "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", + "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", + "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", + "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", + "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", + "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", + "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", + "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", + "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", + "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/template": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", + "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", + "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", + "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", + "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", + "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", + "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", + "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", + "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", + "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", + "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", + "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", + "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", + "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", + "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", + "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", + "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", + "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", + "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", + "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", + "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", + "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", + "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", + "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", + "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", + "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", + "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", + "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", + "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", + "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", + "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", + "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", + "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", + "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", + "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", + "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.29.7.tgz", + "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.29.7", + "@babel/plugin-syntax-import-attributes": "^7.29.7", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.29.7", + "@babel/plugin-transform-async-generator-functions": "^7.29.7", + "@babel/plugin-transform-async-to-generator": "^7.29.7", + "@babel/plugin-transform-block-scoped-functions": "^7.29.7", + "@babel/plugin-transform-block-scoping": "^7.29.7", + "@babel/plugin-transform-class-properties": "^7.29.7", + "@babel/plugin-transform-class-static-block": "^7.29.7", + "@babel/plugin-transform-classes": "^7.29.7", + "@babel/plugin-transform-computed-properties": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-dotall-regex": "^7.29.7", + "@babel/plugin-transform-duplicate-keys": "^7.29.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-dynamic-import": "^7.29.7", + "@babel/plugin-transform-explicit-resource-management": "^7.29.7", + "@babel/plugin-transform-exponentiation-operator": "^7.29.7", + "@babel/plugin-transform-export-namespace-from": "^7.29.7", + "@babel/plugin-transform-for-of": "^7.29.7", + "@babel/plugin-transform-function-name": "^7.29.7", + "@babel/plugin-transform-json-strings": "^7.29.7", + "@babel/plugin-transform-literals": "^7.29.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", + "@babel/plugin-transform-member-expression-literals": "^7.29.7", + "@babel/plugin-transform-modules-amd": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-modules-systemjs": "^7.29.7", + "@babel/plugin-transform-modules-umd": "^7.29.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-new-target": "^7.29.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", + "@babel/plugin-transform-numeric-separator": "^7.29.7", + "@babel/plugin-transform-object-rest-spread": "^7.29.7", + "@babel/plugin-transform-object-super": "^7.29.7", + "@babel/plugin-transform-optional-catch-binding": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/plugin-transform-private-methods": "^7.29.7", + "@babel/plugin-transform-private-property-in-object": "^7.29.7", + "@babel/plugin-transform-property-literals": "^7.29.7", + "@babel/plugin-transform-regenerator": "^7.29.7", + "@babel/plugin-transform-regexp-modifiers": "^7.29.7", + "@babel/plugin-transform-reserved-words": "^7.29.7", + "@babel/plugin-transform-shorthand-properties": "^7.29.7", + "@babel/plugin-transform-spread": "^7.29.7", + "@babel/plugin-transform-sticky-regex": "^7.29.7", + "@babel/plugin-transform-template-literals": "^7.29.7", + "@babel/plugin-transform-typeof-symbol": "^7.29.7", + "@babel/plugin-transform-unicode-escapes": "^7.29.7", + "@babel/plugin-transform-unicode-property-regex": "^7.29.7", + "@babel/plugin-transform-unicode-regex": "^7.29.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmmirror.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmmirror.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmmirror.com/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmmirror.com/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmmirror.com/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmmirror.com/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "25.0.8", + "resolved": "https://registry.npmmirror.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz", + "integrity": "sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.3.1", + "resolved": "https://registry.npmmirror.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", + "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmmirror.com/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.12", + "resolved": "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.12.tgz", + "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmmirror.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.1.tgz", + "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmmirror.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmmirror.com/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmmirror.com/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/node": { + "version": "26.1.1", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmmirror.com/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmmirror.com/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmmirror.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "dev": true, + "license": "ISC" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmmirror.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmmirror.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.2", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmmirror.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.1", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.1.tgz", + "integrity": "sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.16", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmmirror.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js-compat": { + "version": "3.49.0", + "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmmirror.com/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmmirror.com/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmmirror.com/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.395", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.395.tgz", + "integrity": "sha512-7zt9Aw+SrmxLWLN0zhaTWZQiCdryLVrYTq5R7iZakLvi2UQPYMMsROYV/2qVCzMeCiSXHwKOU+sZ4zOVVlrtKA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmmirror.com/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.4.3.tgz", + "integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmmirror.com/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmmirror.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.15.0", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmmirror.com/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/livereload": { + "version": "0.9.3", + "resolved": "https://registry.npmmirror.com/livereload/-/livereload-0.9.3.tgz", + "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.0", + "livereload-js": "^3.3.1", + "opts": ">= 1.2.0", + "ws": "^7.4.3" + }, + "bin": { + "livereload": "bin/livereload.js" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/livereload-js": { + "version": "3.4.1", + "resolved": "https://registry.npmmirror.com/livereload-js/-/livereload-js-3.4.1.tgz", + "integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/livereload/node_modules/ws": { + "version": "7.5.13", + "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmmirror.com/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.24", + "resolved": "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.24.tgz", + "integrity": "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/opts": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/opts/-/opts-2.0.2.tgz", + "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmmirror.com/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmmirror.com/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.2", + "resolved": "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.13.2.tgz", + "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.30.0", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-3.30.0.tgz", + "integrity": "sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==", + "dev": true, + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-dts": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/rollup-plugin-dts/-/rollup-plugin-dts-5.3.1.tgz", + "integrity": "sha512-gusMi+Z4gY/JaEQeXnB0RUdU82h1kF0WYzCWgVmV4p3hWXqelaKuCvcJawfeg+EKn2T1Ie+YWF2OiN1/L8bTVg==", + "dev": true, + "license": "LGPL-3.0", + "dependencies": { + "magic-string": "^0.30.2" + }, + "engines": { + "node": ">=v14.21.3" + }, + "funding": { + "url": "https://github.com/sponsors/Swatinem" + }, + "optionalDependencies": { + "@babel/code-frame": "^7.22.5" + }, + "peerDependencies": { + "rollup": "^3.0", + "typescript": "^4.1 || ^5.0" + } + }, + "node_modules/rollup-plugin-livereload": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/rollup-plugin-livereload/-/rollup-plugin-livereload-2.0.5.tgz", + "integrity": "sha512-vqQZ/UQowTW7VoiKEM5ouNW90wE5/GZLfdWuR0ELxyKOJUIaj+uismPZZaICU4DnWPVjnpCDDxEqwU7pcKY/PA==", + "dev": true, + "license": "MIT", + "dependencies": { + "livereload": "^0.9.1" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/rollup-plugin-serve": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/rollup-plugin-serve/-/rollup-plugin-serve-2.0.3.tgz", + "integrity": "sha512-gQKmfQng17+jOsX5tmDanvJkm0f9XLqWVvXsD7NGd1SlneT+U1j/HjslDUXQz6cqwLnVDRc6xF2lj6rre+eeeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime": "^3", + "opener": "1" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/smob": { + "version": "1.6.2", + "resolved": "https://registry.npmmirror.com/smob/-/smob-1.6.2.tgz", + "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser": { + "version": "5.49.0", + "resolved": "https://registry.npmmirror.com/terser/-/terser-5.49.0.tgz", + "integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmmirror.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.21.1", + "resolved": "https://registry.npmmirror.com/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..9f6a08b --- /dev/null +++ b/package.json @@ -0,0 +1,85 @@ +{ + "name": "@metona-team/metona-editor", + "version": "0.1.0", + "description": "轻量、零依赖、精致美观的 Markdown Editor 库。单文件,开箱即用。", + "main": "dist/metona-editor.js", + "module": "src/index.js", + "unpkg": "dist/metona-editor.min.js", + "jsdelivr": "dist/metona-editor.min.js", + "types": "types/index.d.ts", + "exports": { + ".": { + "import": "./src/index.js", + "require": "./dist/metona-editor.js", + "types": "./types/index.d.ts" + } + }, + "files": [ + "dist/", + "src/", + "types/", + "README.md", + "LICENSE" + ], + "scripts": { + "build": "rollup -c", + "dev": "rollup -c -w", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage", + "lint": "eslint src/", + "lint:fix": "eslint src/ --fix", + "format": "prettier --write src/", + "typecheck": "tsc --noEmit", + "prepublishOnly": "npm run build", + "docs": "jsdoc src/ -d docs", + "example": "serve examples/" + }, + "repository": { + "type": "git", + "url": "git+https://git.metona.cn/MetonaTeam/MetonaEditor.git" + }, + "keywords": [ + "markdown", + "editor", + "wysiwyg", + "ui", + "component", + "lightweight", + "zero-dependency" + ], + "author": "thzxx", + "license": "MIT", + "bugs": { + "url": "https://git.metona.cn/MetonaTeam/MetonaEditor/issues" + }, + "homepage": "https://git.metona.cn/MetonaTeam/MetonaEditor#readme", + "devDependencies": { + "@babel/core": "^7.22.0", + "@babel/preset-env": "^7.22.0", + "@babel/plugin-transform-modules-commonjs": "^7.22.0", + "@rollup/plugin-commonjs": "^25.0.0", + "@rollup/plugin-node-resolve": "^15.0.0", + "@rollup/plugin-terser": "^0.4.0", + "@types/jest": "^29.5.0", + "babel-jest": "^29.5.0", + "eslint": "^8.40.0", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "prettier": "^2.8.0", + "rollup": "^3.20.0", + "rollup-plugin-dts": "^5.3.0", + "rollup-plugin-livereload": "^2.0.5", + "rollup-plugin-serve": "^2.0.1", + "typescript": "^5.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not dead", + "not ie 11" + ] +} diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..36fc792 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,122 @@ +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import terser from '@rollup/plugin-terser'; +import dts from 'rollup-plugin-dts'; +import serve from 'rollup-plugin-serve'; +import livereload from 'rollup-plugin-livereload'; + +const isDev = process.env.ROLLUP_WATCH; +const isProd = process.env.NODE_ENV === 'production'; + +const baseConfig = { + input: 'src/index.js', + plugins: [ + resolve(), + commonjs(), + ], +}; + +const devPlugins = isDev ? [ + serve({ + open: true, + contentBase: ['site', 'dist'], + port: 3001, + }), + livereload({ + watch: ['src', 'site'], + }), +] : []; + +const prodPlugins = isProd ? [ + terser({ + compress: { + drop_console: true, + drop_debugger: true, + pure_funcs: ['console.log', 'console.warn'], + }, + format: { + comments: false, + }, + }), +] : []; + +export default [ + { + ...baseConfig, + output: { + file: 'dist/metona-editor.js', + format: 'umd', + name: 'MeEditor', + exports: 'named', + sourcemap: !isProd, + globals: {}, + }, + plugins: [ + ...baseConfig.plugins, + ...devPlugins, + ...prodPlugins, + ], + }, + { + ...baseConfig, + output: { + file: 'dist/metona-editor.esm.js', + format: 'es', + exports: 'named', + sourcemap: !isProd, + }, + plugins: [ + ...baseConfig.plugins, + ...prodPlugins, + ], + }, + { + ...baseConfig, + output: { + file: 'dist/metona-editor.cjs.js', + format: 'cjs', + exports: 'named', + sourcemap: !isProd, + }, + plugins: [ + ...baseConfig.plugins, + ...prodPlugins, + ], + }, + { + ...baseConfig, + output: { + file: 'dist/metona-editor.min.js', + format: 'umd', + name: 'MeEditor', + exports: 'named', + sourcemap: false, + globals: {}, + }, + plugins: [ + ...baseConfig.plugins, + terser({ + compress: { + drop_console: true, + drop_debugger: true, + pure_funcs: ['console.log', 'console.warn'], + passes: 2, + }, + format: { + comments: false, + }, + mangle: { + toplevel: true, + }, + }), + ], + }, + { + input: 'types/index.d.ts', + output: { + file: 'dist/metona-editor.d.ts', + format: 'es', + }, + plugins: [dts()], + }, +]; diff --git a/serve.sh b/serve.sh new file mode 100644 index 0000000..09c2bca --- /dev/null +++ b/serve.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# MetonaEditor 本地开发服务器 + +echo "🍞 MetonaEditor 开发服务器" +echo "=========================" + +if command -v python3 &> /dev/null; then + PYTHON=python3 +elif command -v python &> /dev/null; then + PYTHON=python +else + echo "❌ 错误: 未找到Python" + exit 1 +fi + +PORT=${1:-3001} + +echo "✅ Python: $($PYTHON --version 2>&1)" +echo "🌐 启动服务器..." +echo "" +echo "📁 访问地址:" +echo " http://localhost:$PORT/site/index.html" +echo " http://localhost:$PORT/site/demo.html" +echo " http://localhost:$PORT/site/docs.html" +echo "" +echo "按 Ctrl+C 停止服务器" +echo "" + +cd "$(dirname "$0")" +$PYTHON -m http.server $PORT diff --git a/site/demo.html b/site/demo.html new file mode 100644 index 0000000..3e2a851 --- /dev/null +++ b/site/demo.html @@ -0,0 +1,400 @@ + + + + + +MetonaEditor · 功能演示 + + + +
      +
      +

      + MetonaEditor 功能演示 + ← 返回首页 +

      +
      + 主题 +
      + + + +
      + 语言 +
      + + +
      +
      +
      + +
      +
      + + 状态:运行中 +
      +
      模式:split
      +
      主题:light
      +
      语言:zh-CN
      +
      字数:0
      +
      全屏:
      +
      + +
      + +
      +
      +
      + API 控制台 + 0 次调用 +
      +
      +
      + + + + + + + + + + + + + + + + + + +
      +
      + +
      +
      + 事件流(实时) + 0 个事件 +
      +
      +
      +
      + +
      + 快捷键提示: + 编辑区内按 Ctrl+B 加粗、Ctrl+I 斜体、Ctrl+K 插入链接、 + Ctrl+Z 撤销、Ctrl+S 触发保存、Ctrl+F 查找、Ctrl+H 替换、 + Tab 缩进、Esc 关闭面板。 +
      +
      + + + + + diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..8843b88 --- /dev/null +++ b/site/index.html @@ -0,0 +1,728 @@ + + + + + +MetonaEditor — 现代化零依赖 Markdown 编辑器 + + + + + + +
      +
      + +

      现代化零依赖的
      Markdown 编辑器

      +

      单文件开箱即用,内置轻量解析器、分屏预览、插件系统与中文优先的国际化。为中文场景与现代 Web 应用而生。

      +
      + 零依赖 + UMD / ESM / CJS + TypeScript + 主题可定制 + 中文优先 + MIT +
      + +
      +
      +
      0
      +
      运行时依赖
      +
      +
      +
      390+
      +
      单元测试
      +
      +
      +
      80%+
      +
      测试覆盖率
      +
      +
      +
      15KB
      +
      gzip 体积
      +
      +
      +
      + +
      +
      + Live Demo +

      立即体验

      +

      下面是一个真实可用的编辑器实例,试着在左侧编辑,右侧会实时预览。

      +
      +
      +
      + +
      +
      + Features +

      为什么选择 MetonaEditor

      +

      为中文场景与现代 Web 应用而生,每一项特性都经过精心打磨。

      +
      +
      +
      + +
      +

      真正的零依赖

      +

      整个库不依赖任何第三方运行时,打包后单文件可直接 <script> 引入,也支持 npm 安装。

      +
      +
      +
      + +
      +

      分屏实时预览

      +

      编辑 / 分屏 / 预览三模式自由切换,分屏模式下可拖拽分隔条调整比例,滚动自动同步。

      +
      +
      +
      + +
      +

      内置轻量解析器

      +

      自研 CommonMark 子集 + GFM 扩展解析器,覆盖标题、表格、任务列表、代码块等常用语法,也可整体替换。

      +
      +
      +
      + +
      +

      易扩展的插件系统

      +

      内置自动保存、导出、查找替换插件;自定义插件只需实现 install / destroy 两个生命周期钩子。

      +
      +
      +
      + +
      +

      主题与国际化

      +

      基于 CSS 变量的主题系统,内置亮 / 暗 / 暖色三套主题;中英双语开箱即用,支持 RTL 布局。

      +
      +
      +
      + +
      +

      XSS 安全防护

      +

      所有文本经 HTML 转义,URL 过滤 javascript / vbscript / file 等危险协议,并提供 sanitize 钩子。

      +
      +
      +
      +
      + +
      +
      + Quick Start +

      三行代码即可使用

      +

      CDN 引入或 npm 安装,都只需要极少的代码。

      +
      +<!-- 浏览器直接引入 --> +<script src="dist/metona-editor.js"></script> +<div id="editor"></div> +<script> + const editor = MeEditor.create('#editor', { + mode: 'split', + value: '# 你好,世界', + plugins: ['autoSave', 'exportTool'] + }); +</script> + +// 或 npm 安装 +import MeEditor from '@metona-team/metona-editor'; + +const editor = MeEditor.create(container, { + mode: 'split', + theme: 'dark', + onChange: (value) => console.log(value) +}); + +// 链式 API +editor.exec('bold').exec('h1').focus(); +
      +
      +
      + +
      +
      + API Reference +

      API 速览

      +

      完整 API 文档请参考 README.md,以下是最常用的方法。

      +
      +
      +

      内容操作

      +
        +
      • getValue() string
      • +
      • setValue(md) this
      • +
      • getHTML() string
      • +
      • insert(text) this
      • +
      • wrap(before, after) this
      • +
      • focus() / blur() this
      • +
      +
      +
      +

      命令执行

      +
        +
      • exec(action) this
      • +
      • undo() / redo() this
      • +
      • canUndo() boolean
      • +
      • canRedo() boolean
      • +
      +
      +
      +

      模式与全屏

      +
        +
      • setMode(mode) this
      • +
      • getMode() string
      • +
      • toggleFullscreen() this
      • +
      • isFullscreen() boolean
      • +
      +
      +
      +

      事件与插件

      +
        +
      • on(event, fn) unsub
      • +
      • off(event, fn) void
      • +
      • use(plugin) this
      • +
      • getPlugins() Plugin[]
      • +
      • addToolbarButton(cfg) this
      • +
      +
      +
      +

      统计与状态

      +
        +
      • getStats() Stats
      • +
      • getStatus() Status
      • +
      • enable() / disable() this
      • +
      • destroy() void
      • +
      +
      +
      +

      静态 API

      +
        +
      • MeEditor.create(el, opts) Editor
      • +
      • MeEditor.use(plugin) void
      • +
      • MeEditor.setTheme(name) void
      • +
      • MeEditor.setLocale(name) void
      • +
      • MeEditor.on(hook, fn) void
      • +
      +
      +
      +
      +
      + +
      +
      + Keyboard Shortcuts +

      快捷键

      +

      Mac 用户请将 Ctrl 替换为 Cmd。

      +
      +
      + 加粗 +
      CtrlB
      +
      +
      + 斜体 +
      CtrlI
      +
      +
      + 行内代码 +
      CtrlE
      +
      +
      + 下划线 +
      CtrlU
      +
      +
      + 插入链接 +
      CtrlK
      +
      +
      + 标题 1/2/3 +
      Ctrl1/2/3
      +
      +
      + 引用 +
      CtrlQ
      +
      +
      + 撤销 +
      CtrlZ
      +
      +
      + 重做 +
      CtrlShift+Z
      +
      +
      + 保存 +
      CtrlS
      +
      +
      + 查找 +
      CtrlF
      +
      +
      + 查找替换 +
      CtrlH
      +
      +
      + 缩进 / 反缩进 +
      TabShift+Tab
      +
      +
      + 下一个匹配 +
      Enter
      +
      +
      + 上一个匹配 +
      ShiftEnter
      +
      +
      + 关闭面板 +
      Esc
      +
      +
      +
      +
      + + + + + + + + diff --git a/src/animations.js b/src/animations.js new file mode 100644 index 0000000..d968888 --- /dev/null +++ b/src/animations.js @@ -0,0 +1,70 @@ +/** + * MetonaEditor Animations - 动画管理 + * @module animations + * @version 0.1.0 + * @description 动画注册与管理 + */ + +import { ANIMATIONS } from './constants.js'; + +const animationMap = new Map(); + +// 注册默认动画 +Object.entries(ANIMATIONS).forEach(([name, config]) => { + animationMap.set(name, config); +}); + +/** + * 动画工具函数 + */ +export const animationUtils = { + register(name, config) { + animationMap.set(name, { + name, + enter: config.enter || {}, + leave: config.leave || {}, + duration: config.duration || 300, + easing: config.easing || 'cubic-bezier(0.4, 0, 0.2, 1)', + }); + }, + + unregister(name) { + animationMap.delete(name); + }, + + get(name) { + return animationMap.get(name) || ANIMATIONS[name] || null; + }, + + getAnimationNames() { + return Array.from(animationMap.keys()); + }, + + getActiveCount() { + return 0; + }, + + cancelAll() { + // CSS动画由浏览器原生管理 + }, + + reset() { + animationMap.clear(); + Object.entries(ANIMATIONS).forEach(([name, config]) => { + animationMap.set(name, config); + }); + }, + + destroy() { + animationMap.clear(); + }, +}; + +export const animationPresets = {}; + +export const createAnimation = (config = {}) => ({ + enter: config.enter || {}, + leave: config.leave || {}, + duration: config.duration || 300, + easing: config.easing || 'cubic-bezier(0.4, 0, 0.2, 1)', +}); diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..283254d --- /dev/null +++ b/src/constants.js @@ -0,0 +1,194 @@ +/** + * MetonaEditor Constants - 常量定义 + * @module constants + * @version 0.1.0 + * @description 默认配置、主题、动画、工具栏等常量 + */ + +import { ICONS } from './icons.js'; +import { LOCALES } from './locales.js'; + +/** + * 默认工具栏按钮序列 + * '|' 表示分组分隔符 + */ +export const DEFAULT_TOOLBAR = [ + 'bold', 'italic', 'strikethrough', 'code', '|', + 'h1', 'h2', 'h3', '|', + 'quote', 'ul', 'ol', '|', + 'link', 'image', 'table', 'hr', '|', + 'undo', 'redo', '|', + 'edit', 'split', 'preview', 'fullscreen', +]; + +/** + * 默认配置 + */ +export const DEFAULTS = Object.freeze({ + // 内容 + value: '', + placeholder: '', + // 视图模式:edit / split / preview + mode: 'split', + // 高度:数字为 px,字符串原样使用 + height: 400, + // 工具栏:数组或 false(隐藏) + toolbar: DEFAULT_TOOLBAR, + // 字数统计状态栏 + wordCount: true, + // 自动聚焦 + autofocus: false, + // 拼写检查 + spellcheck: false, + // 历史栈上限 + historyLimit: 100, + // 同步滚动(分屏模式) + syncScroll: true, + // 制表符插入的空格数(0 表示插入 \t) + tabSize: 2, + // 主题:light / dark / auto / warm / 自定义 + theme: 'auto', + // 国际化 + locale: 'zh-CN', + // 自定义 Markdown 渲染函数 (md, env) => html,覆盖内置解析器 + render: null, + // 自定义代码高亮函数 (code, lang) => html + highlight: null, + // 自定义渲染前的 HTML 净化函数(接收渲染后的 HTML,返回安全 HTML) + sanitize: null, + // 外观 + className: '', + style: {}, + // 插件 + plugins: [], + // 生命周期回调 + onChange: null, + onInput: null, + onFocus: null, + onBlur: null, + onSave: null, + onModeChange: null, + onFullscreen: null, + onCreate: null, + onDestroy: null, +}); + +// Icons - 从 icons.js 导入并重新导出(保持单一来源) +export { ICONS }; + +/** + * 类型颜色配置(保留用于状态提示与按钮强调) + */ +export const TYPE_COLORS = { + success: { fg: '#10b981', bg: 'rgba(16, 185, 129, 0.1)' }, + error: { fg: '#ef4444', bg: 'rgba(239, 68, 68, 0.1)' }, + warning: { fg: '#f59e0b', bg: 'rgba(245, 158, 11, 0.1)' }, + info: { fg: '#3b82f6', bg: 'rgba(59, 130, 246, 0.1)' }, + loading: { fg: '#6366f1', bg: 'rgba(99, 102, 241, 0.1)' }, + default: { fg: '#6b7280', bg: 'rgba(107, 114, 128, 0.1)' }, +}; + +/** + * 主题配置 + */ +export const THEMES = { + light: { + bg: 'rgba(255, 255, 255, 0.96)', + text: '#1f2937', + border: 'rgba(0, 0, 0, 0.08)', + shadow: '0 10px 36px -10px rgba(0, 0, 0, 0.18), 0 4px 14px -4px rgba(0, 0, 0, 0.08)', + hoverShadow: '0 14px 48px -10px rgba(0, 0, 0, 0.22), 0 6px 18px -4px rgba(0, 0, 0, 0.10)', + // 编辑器专属 + toolbarBg: 'rgba(248, 249, 250, 0.92)', + textareaBg: '#ffffff', + previewBg: '#ffffff', + codeBg: 'rgba(243, 244, 246, 1)', + codeText: '#1f2937', + accent: '#3b82f6', + muted: '#6b7280', + }, + dark: { + bg: 'rgba(28, 32, 40, 0.94)', + text: '#e6e8eb', + border: 'rgba(255, 255, 255, 0.1)', + shadow: '0 10px 36px -10px rgba(0, 0, 0, 0.6), 0 4px 14px -4px rgba(0, 0, 0, 0.4)', + hoverShadow: '0 14px 48px -8px rgba(0, 0, 0, 0.6), 0 6px 18px -4px rgba(0, 0, 0, 0.4)', + toolbarBg: 'rgba(22, 26, 33, 0.92)', + textareaBg: '#1c2028', + previewBg: '#1c2028', + codeBg: 'rgba(15, 18, 24, 1)', + codeText: '#e6e8eb', + accent: '#60a5fa', + muted: '#9ca3af', + }, + auto: 'auto', + warm: { + bg: 'rgba(255, 251, 235, 0.96)', + text: '#78350f', + border: 'rgba(245, 158, 11, 0.2)', + shadow: '0 10px 36px -10px rgba(245, 158, 11, 0.18), 0 4px 14px -4px rgba(245, 158, 11, 0.08)', + hoverShadow: '0 14px 48px -10px rgba(245, 158, 11, 0.22), 0 6px 18px -4px rgba(245, 158, 11, 0.10)', + toolbarBg: 'rgba(254, 243, 199, 0.92)', + textareaBg: '#fffbeb', + previewBg: '#fffbeb', + codeBg: 'rgba(254, 215, 170, 0.6)', + codeText: '#78350f', + accent: '#d97706', + muted: '#a16207', + }, +}; + +/** + * 位置配置(保留,预览面板提示等场景可用) + */ +export const POSITIONS = [ + 'top-left', 'top-center', 'top-right', + 'bottom-left', 'bottom-center', 'bottom-right', +]; + +/** + * 动画配置(保留,DOM 过渡与未来扩展使用) + */ +export const ANIMATIONS = { + slide: { enter: { transform: 'translateX(80px)', opacity: 0 }, leave: { transform: 'translateX(120%)', opacity: 0 }, duration: 400, easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)' }, + fade: { enter: { opacity: 0, filter: 'blur(3px)' }, leave: { opacity: 0, filter: 'blur(3px)' }, duration: 500, easing: 'ease' }, + scale: { enter: { transform: 'scale(0.55)', opacity: 0 }, leave: { transform: 'scale(0.55)', opacity: 0 }, duration: 450, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' }, + bounce: { enter: { transform: 'translateY(-80px)', opacity: 0 }, leave: { transform: 'translateY(20px)', opacity: 0 }, duration: 650, easing: 'ease' }, + flip: { enter: { transform: 'perspective(500px) rotateX(-90deg)', opacity: 0 }, leave: { transform: 'perspective(500px) rotateX(90deg)', opacity: 0 }, duration: 500, easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)' }, + rotate: { enter: { transform: 'rotate(-25deg) scale(0.6)', opacity: 0 }, leave: { transform: 'rotate(25deg) scale(0.6)', opacity: 0 }, duration: 500, easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)' }, + zoom: { enter: { transform: 'scale(0.1)', opacity: 0 }, leave: { transform: 'scale(0.1)', opacity: 0 }, duration: 500, easing: 'cubic-bezier(0.34, 1.56, 0.64, 1)' }, +}; + +/** + * 主题类型 + */ +export const THEME_TYPES = ['light', 'dark', 'auto']; + +/** + * 动画类型 + */ +export const ANIMATION_TYPES = ['slide', 'fade', 'scale', 'bounce', 'flip', 'rotate', 'zoom']; + +/** + * 编辑器视图模式 + */ +export const EDIT_MODES = ['edit', 'split', 'preview']; + +/** + * 内置工具栏动作清单 + */ +export const TOOLBAR_ACTIONS = [ + 'bold', 'italic', 'strikethrough', 'underline', 'code', + 'h1', 'h2', 'h3', 'quote', 'ul', 'ol', 'indent', 'outdent', 'hr', + 'link', 'image', 'table', + 'undo', 'redo', + 'edit', 'split', 'preview', 'fullscreen', +]; + +// Locales - 从 locales.js 导入并重新导出 +export { LOCALES }; + +/** + * 进度条方向(保留兼容) + */ +export const PROGRESS_DIRECTIONS = ['horizontal', 'vertical']; diff --git a/src/core.js b/src/core.js new file mode 100644 index 0000000..0253b62 --- /dev/null +++ b/src/core.js @@ -0,0 +1,945 @@ +/** + * MetonaEditor Core - 编辑器核心 + * @module core + * @version 0.1.0 + * @description MarkdownEditor 类实现:DOM 构建、事件、渲染、历史栈、选区操作、模式切换 + */ + +import { generateId, escapeHTML, isBrowser } from './utils.js'; +import { DEFAULTS, ICONS, THEMES } from './constants.js'; +import { injectStyles } from './styles.js'; +import { t as i18nT, getCurrentLocale, getLocaleDirection } from './i18n.js'; +import { parseMarkdown } from './parser.js'; +import { presetPlugins } from './plugins.js'; +import { getTheme } from './themes.js'; + +const MODES = ['edit', 'split', 'preview']; + +/** + * 解析主题名称为实际主题(auto -> light/dark) + */ +const resolveThemeName = (theme) => { + if (theme && theme !== 'auto') return theme; + return getTheme('auto'); +}; + +/** + * MarkdownEditor 编辑器类 + */ +class MarkdownEditor { + /** + * 静态事件钩子(全局,所有实例共享,供插件使用) + */ + static _hooks = new Map(); + + static on(name, fn) { + if (!this._hooks.has(name)) this._hooks.set(name, []); + this._hooks.get(name).push(fn); + return () => this.off(name, fn); + } + + static off(name, fn) { + const list = this._hooks.get(name); + if (list) this._hooks.set(name, list.filter((f) => f !== fn)); + } + + static trigger(name, instance) { + const list = this._hooks.get(name); + if (list) list.forEach((fn) => { + try { fn(instance); } catch (e) { console.error(`MeEditor hook "${name}" error:`, e); } + }); + } + + /** + * @param {string|HTMLElement} container - 容器选择器或元素 + * @param {Object} options - 配置项(见 DEFAULTS) + */ + constructor(container, options = {}) { + if (!isBrowser()) { + this._destroyed = true; + this._value = options.value || ''; + return this; + } + + this.id = options.id || generateId(); + this.container = typeof container === 'string' + ? document.querySelector(container) + : container; + + if (!this.container) { + console.error('MeEditor: container not found:', container); + this._destroyed = true; + return this; + } + + this.config = { ...DEFAULTS, ...options }; + if (options.style && typeof options.style === 'object') { + this.config.style = { ...options.style }; + } + + this._value = this.config.value || ''; + this._mode = MODES.includes(this.config.mode) ? this.config.mode : 'split'; + this._history = []; + this._historyIndex = -1; + this._cleanups = []; + this._plugins = []; + this._listeners = {}; + this._renderRaf = null; + this._historyTimer = null; + this._fullscreen = false; + this._destroyed = false; + + // 渲染函数:自定义覆盖内置解析器 + this._renderFn = (typeof this.config.render === 'function') ? this.config.render : parseMarkdown; + this._highlightFn = (typeof this.config.highlight === 'function') ? this.config.highlight : null; + + MarkdownEditor.trigger('beforeCreate', this); + + injectStyles(); + this._buildDOM(); + this._buildToolbar(); + this._bindEvents(); + + // 初始内容 + this.textarea.value = this._value; + this._pushHistory(); + this._render(); + this._updateWordCount(); + + // 安装配置中的插件 + if (Array.isArray(this.config.plugins)) { + this.config.plugins.forEach((p) => this.use(p)); + } + + if (this.config.autofocus) this.focus(); + + if (typeof this.config.onCreate === 'function') { + try { this.config.onCreate(this); } catch (e) { console.error('onCreate error:', e); } + } + + MarkdownEditor.trigger('afterCreate', this); + } + + // ============ DOM 构建 ============ + + _buildDOM() { + const wrapper = document.createElement('div'); + wrapper.className = `me-wrapper me-theme-${resolveThemeName(this.config.theme)}`; + wrapper.dataset.id = this.id; + wrapper.setAttribute('role', 'application'); + wrapper.setAttribute('aria-label', i18nT('edit')); + + if (this.config.className) { + this.config.className.split(/\s+/).filter(Boolean).forEach((c) => wrapper.classList.add(c)); + } + + // 高度 + const h = this.config.height; + wrapper.style.height = typeof h === 'number' ? `${h}px` : (h || '400px'); + + // 自定义样式 + if (this.config.style && typeof this.config.style === 'object') { + Object.entries(this.config.style).forEach(([k, v]) => { wrapper.style[k] = v; }); + } + + const toolbar = document.createElement('div'); + toolbar.className = 'me-toolbar'; + toolbar.setAttribute('role', 'toolbar'); + + const body = document.createElement('div'); + body.className = `me-body me-mode-${this._mode}`; + + const editorPane = document.createElement('div'); + editorPane.className = 'me-editor-pane'; + + const textarea = document.createElement('textarea'); + textarea.className = 'me-textarea'; + textarea.spellcheck = !!this.config.spellcheck; + textarea.placeholder = this.config.placeholder || i18nT('placeholder'); + textarea.setAttribute('aria-label', i18nT('edit')); + editorPane.appendChild(textarea); + + const divider = document.createElement('div'); + divider.className = 'me-divider'; + divider.setAttribute('role', 'separator'); + + const previewPane = document.createElement('div'); + previewPane.className = 'me-preview-pane'; + previewPane.setAttribute('role', 'region'); + previewPane.setAttribute('aria-label', i18nT('preview')); + + const preview = document.createElement('div'); + preview.className = 'me-preview'; + previewPane.appendChild(preview); + + body.appendChild(editorPane); + body.appendChild(divider); + body.appendChild(previewPane); + + wrapper.appendChild(toolbar); + wrapper.appendChild(body); + + // 状态栏 + let statusbar = null; + if (this.config.wordCount) { + statusbar = document.createElement('div'); + statusbar.className = 'me-statusbar'; + wrapper.appendChild(statusbar); + } + + this.container.appendChild(wrapper); + + this.el = wrapper; + this.toolbarEl = toolbar; + this.bodyEl = body; + this.editorPane = editorPane; + this.previewPane = previewPane; + this.dividerEl = divider; + this.textarea = textarea; + this.previewEl = preview; + this.statusEl = statusbar; + } + + _buildToolbar() { + const tools = this.config.toolbar; + if (!tools || !Array.isArray(tools) || tools.length === 0) return; + + const modeActions = []; + tools.forEach((item) => { + if (item === '|') { + const sep = document.createElement('span'); + sep.className = 'me-toolbar-sep'; + this.toolbarEl.appendChild(sep); + return; + } + // 模式与全屏按钮归入右侧组 + if (item === 'edit' || item === 'split' || item === 'preview' || item === 'fullscreen') { + modeActions.push(item); + return; + } + this.toolbarEl.appendChild(this._createBtn(item)); + }); + + if (modeActions.length) { + const group = document.createElement('div'); + group.className = 'me-toolbar-group'; + modeActions.forEach((item) => { + const btn = this._createBtn(item); + if (item === 'edit' || item === 'split' || item === 'preview') { + btn.dataset.mode = item; + if (item === this._mode) btn.classList.add('me-active'); + } + group.appendChild(btn); + }); + this.toolbarEl.appendChild(group); + } + } + + _createBtn(item) { + const btn = document.createElement('button'); + btn.type = 'button'; + btn.className = `me-btn me-btn-${item}`; + btn.dataset.action = item; + const label = i18nT(item) || item; + btn.title = label; + btn.setAttribute('aria-label', label); + btn.innerHTML = ICONS[item] || `${escapeHTML(item)}`; + return btn; + } + + // ============ 事件绑定 ============ + + _bindEvents() { + const ta = this.textarea; + + const onInput = () => { + this._value = ta.value; + this._scheduleRender(); + this._scheduleHistory(); + this._updateWordCount(); + this._emit('input', this._value); + this._emit('change', this._value); + if (typeof this.config.onInput === 'function') { + try { this.config.onInput(this._value, this); } catch (e) { console.error(e); } + } + if (typeof this.config.onChange === 'function') { + try { this.config.onChange(this._value, this); } catch (e) { console.error(e); } + } + }; + ta.addEventListener('input', onInput); + + const onKeydown = (e) => this._handleKeydown(e); + ta.addEventListener('keydown', onKeydown); + + const onScroll = () => { + if (!this.config.syncScroll || this._mode !== 'split') return; + const max = ta.scrollHeight - ta.clientHeight; + const ratio = max > 0 ? ta.scrollTop / max : 0; + const pmax = this.previewPane.scrollHeight - this.previewPane.clientHeight; + this.previewPane.scrollTop = ratio * pmax; + }; + ta.addEventListener('scroll', onScroll); + + const onFocus = () => { + this._emit('focus', this); + if (typeof this.config.onFocus === 'function') { + try { this.config.onFocus(this); } catch (e) { console.error(e); } + } + }; + const onBlur = () => { + this._emit('blur', this); + if (typeof this.config.onBlur === 'function') { + try { this.config.onBlur(this); } catch (e) { console.error(e); } + } + }; + ta.addEventListener('focus', onFocus); + ta.addEventListener('blur', onBlur); + + // 工具栏点击 + const onToolbarClick = (e) => { + const btn = e.target.closest('.me-btn'); + if (!btn) return; + const mode = btn.dataset.mode; + if (mode) { + this.setMode(mode); + return; + } + const action = btn.dataset.action; + if (action) this.exec(action); + }; + this.toolbarEl.addEventListener('click', onToolbarClick); + + // 分隔条拖拽(分屏模式) + const onDividerDown = (e) => this._bindDividerDrag(e); + this.dividerEl.addEventListener('pointerdown', onDividerDown); + + this._cleanups.push(() => { + ta.removeEventListener('input', onInput); + ta.removeEventListener('keydown', onKeydown); + ta.removeEventListener('scroll', onScroll); + ta.removeEventListener('focus', onFocus); + ta.removeEventListener('blur', onBlur); + this.toolbarEl.removeEventListener('click', onToolbarClick); + this.dividerEl.removeEventListener('pointerdown', onDividerDown); + }); + } + + _handleKeydown(e) { + // Tab 缩进 + if (e.key === 'Tab') { + e.preventDefault(); + this._handleTab(e.shiftKey); + return; + } + + const mod = e.ctrlKey || e.metaKey; + if (!mod) return; + + const key = e.key.toLowerCase(); + const shortcuts = { + b: 'bold', i: 'italic', k: 'link', u: 'underline', + e: 'code', '1': 'h1', '2': 'h2', '3': 'h3', + q: 'quote', + }; + + if (key === 'z' && !e.shiftKey) { + e.preventDefault(); + this.undo(); + } else if ((key === 'z' && e.shiftKey) || key === 'y') { + e.preventDefault(); + this.redo(); + } else if (key === 's') { + e.preventDefault(); + this._emit('save', this._value); + if (typeof this.config.onSave === 'function') { + try { this.config.onSave(this._value, this); } catch (err) { console.error(err); } + } + } else if (shortcuts[key]) { + e.preventDefault(); + this.exec(shortcuts[key]); + } + } + + _handleTab(shift) { + const ta = this.textarea; + const start = ta.selectionStart; + const end = ta.selectionEnd; + const tabSize = this.config.tabSize || 0; + const tabStr = tabSize > 0 ? ' '.repeat(tabSize) : '\t'; + + if (start === end) { + // 无选区:插入缩进 + ta.value = ta.value.slice(0, start) + tabStr + ta.value.slice(end); + ta.selectionStart = ta.selectionEnd = start + tabStr.length; + } else { + // 有选区:整块缩进/反缩进 + const lineStart = ta.value.lastIndexOf('\n', start - 1) + 1; + const block = ta.value.slice(lineStart, end); + const lines = block.split('\n'); + let newBlock; + if (shift) { + newBlock = lines.map((l) => l.replace(/^(\t| {1,4})/, '')).join('\n'); + } else { + newBlock = lines.map((l) => tabStr + l).join('\n'); + } + ta.value = ta.value.slice(0, lineStart) + newBlock + ta.value.slice(end); + ta.selectionStart = lineStart; + ta.selectionEnd = lineStart + newBlock.length; + } + + this._value = ta.value; + this._pushHistory(); + this._render(); + this._emit('change', this._value); + } + + _bindDividerDrag(e) { + if (this._mode !== 'split') return; + e.preventDefault(); + const startX = e.clientX; + const editorWidth = this.editorPane.getBoundingClientRect().width; + const totalWidth = this.bodyEl.getBoundingClientRect().width; + const divider = this.dividerEl; + + const onMove = (ev) => { + const dx = ev.clientX - startX; + let newEditorWidth = editorWidth + dx; + const min = 80; + const max = totalWidth - 80 - divider.offsetWidth; + newEditorWidth = Math.max(min, Math.min(max, newEditorWidth)); + const pct = (newEditorWidth / totalWidth) * 100; + this.editorPane.style.flex = `0 0 ${pct}%`; + this.previewPane.style.flex = `1 1 ${100 - pct}%`; + }; + const onUp = () => { + window.removeEventListener('pointermove', onMove); + window.removeEventListener('pointerup', onUp); + }; + window.addEventListener('pointermove', onMove); + window.addEventListener('pointerup', onUp); + } + + // ============ 渲染 ============ + + _scheduleRender() { + if (this._renderRaf) return; + this._renderRaf = requestAnimationFrame(() => { + this._renderRaf = null; + this._render(); + }); + } + + _render() { + if (this._mode === 'edit') return; + MarkdownEditor.trigger('beforeRender', this); + + const env = { + highlight: this._highlightFn, + locale: getCurrentLocale(), + }; + + let html; + try { + html = this._renderFn(this._value, env); + } catch (err) { + console.error('MeEditor render error:', err); + html = `

      ${i18nT('renderError')}: ${escapeHTML(err.message)}

      `; + } + + if (typeof this.config.sanitize === 'function') { + try { html = this.config.sanitize(html); } catch (err) { console.error('sanitize error:', err); } + } + + this.previewEl.innerHTML = html; + MarkdownEditor.trigger('afterRender', this); + } + + // ============ 历史栈 ============ + + _scheduleHistory() { + if (this._historyTimer) clearTimeout(this._historyTimer); + this._historyTimer = setTimeout(() => this._pushHistory(), 400); + } + + _pushHistory() { + const cur = this._history[this._historyIndex]; + if (cur === this._value) return; + this._history = this._history.slice(0, this._historyIndex + 1); + this._history.push(this._value); + const limit = this.config.historyLimit > 0 ? this.config.historyLimit : 100; + while (this._history.length > limit) this._history.shift(); + this._historyIndex = this._history.length - 1; + } + + undo() { + if (this._historyIndex <= 0) return this; + this._historyIndex--; + this._applyHistory(); + return this; + } + + redo() { + if (this._historyIndex >= this._history.length - 1) return this; + this._historyIndex++; + this._applyHistory(); + return this; + } + + _applyHistory() { + this._value = this._history[this._historyIndex]; + this.textarea.value = this._value; + this._render(); + this._updateWordCount(); + this._emit('change', this._value); + if (typeof this.config.onChange === 'function') { + try { this.config.onChange(this._value, this); } catch (e) { console.error(e); } + } + } + + canUndo() { return this._historyIndex > 0; } + canRedo() { return this._historyIndex < this._history.length - 1; } + + // ============ 命令执行 ============ + + exec(action, ...args) { + const actions = { + bold: () => this._wrapSelection('**', '**'), + italic: () => this._wrapSelection('*', '*'), + strikethrough: () => this._wrapSelection('~~', '~~'), + underline: () => this._wrapSelection('', ''), + code: () => this._wrapSelection('`', '`'), + h1: () => this._toggleLinePrefix('# '), + h2: () => this._toggleLinePrefix('## '), + h3: () => this._toggleLinePrefix('### '), + quote: () => this._toggleLinePrefix('> '), + ul: () => this._toggleLinePrefix('- '), + ol: () => this._toggleLinePrefix('1. '), + indent: () => this._handleTab(false), + outdent: () => this._handleTab(true), + hr: () => this._insertBlock('\n---\n'), + link: () => this._insertLink(), + image: () => this._insertImage(), + table: () => this._insertTable(), + undo: () => this.undo(), + redo: () => this.redo(), + edit: () => this.setMode('edit'), + split: () => this.setMode('split'), + preview: () => this.setMode('preview'), + fullscreen: () => this.toggleFullscreen(), + }; + const fn = actions[action]; + if (fn) fn.apply(this, args); + return this; + } + + _wrapSelection(before, after) { + const ta = this.textarea; + const start = ta.selectionStart; + const end = ta.selectionEnd; + const selected = ta.value.slice(start, end); + const text = selected || (i18nT('placeholder') || 'text'); + const inserted = before + text + after; + + ta.value = ta.value.slice(0, start) + inserted + ta.value.slice(end); + ta.focus(); + + if (selected) { + ta.selectionStart = start + before.length; + ta.selectionEnd = start + before.length + text.length; + } else { + ta.selectionStart = ta.selectionEnd = start + before.length; + } + + this._value = ta.value; + this._pushHistory(); + this._render(); + this._updateWordCount(); + this._emit('change', this._value); + } + + _toggleLinePrefix(prefix) { + const ta = this.textarea; + const start = ta.selectionStart; + const lineStart = ta.value.lastIndexOf('\n', start - 1) + 1; + const lineEndPos = ta.value.indexOf('\n', start); + const lineEnd = lineEndPos === -1 ? ta.value.length : lineEndPos; + const line = ta.value.slice(lineStart, lineEnd); + + const existingMatch = line.match(/^(#{1,6}\s*|>\s*|[-*+]\s*|\d+\.\s*)/); + let newLine; + if (existingMatch && existingMatch[0] === prefix) { + newLine = line.slice(prefix.length); + } else if (existingMatch) { + newLine = prefix + line.slice(existingMatch[0].length); + } else { + newLine = prefix + line; + } + + ta.value = ta.value.slice(0, lineStart) + newLine + ta.value.slice(lineEnd); + ta.focus(); + ta.selectionStart = ta.selectionEnd = lineStart + newLine.length; + + this._value = ta.value; + this._pushHistory(); + this._render(); + this._updateWordCount(); + this._emit('change', this._value); + } + + _insertBlock(text) { + const ta = this.textarea; + const start = ta.selectionStart; + const before = ta.value.slice(0, start); + const needNL = before && !before.endsWith('\n'); + const insert = (needNL ? '\n' : '') + text; + + ta.value = ta.value.slice(0, start) + insert + ta.value.slice(ta.selectionEnd); + ta.focus(); + const pos = start + insert.length; + ta.selectionStart = ta.selectionEnd = pos; + + this._value = ta.value; + this._pushHistory(); + this._render(); + this._updateWordCount(); + this._emit('change', this._value); + } + + _insertLink() { + const ta = this.textarea; + const start = ta.selectionStart; + const end = ta.selectionEnd; + const sel = ta.value.slice(start, end) || i18nT('link') || 'link'; + const url = 'https://'; + const insert = `[${sel}](${url})`; + ta.value = ta.value.slice(0, start) + insert + ta.value.slice(end); + ta.focus(); + // 选中 url 便于替换 + const urlStart = start + sel.length + 3; + ta.selectionStart = urlStart; + ta.selectionEnd = urlStart + url.length; + this._value = ta.value; + this._pushHistory(); + this._render(); + this._emit('change', this._value); + } + + _insertImage() { + const ta = this.textarea; + const start = ta.selectionStart; + const end = ta.selectionEnd; + const sel = ta.value.slice(start, end) || i18nT('image') || 'image'; + const url = 'https://'; + const insert = `![${sel}](${url})`; + ta.value = ta.value.slice(0, start) + insert + ta.value.slice(end); + ta.focus(); + const urlStart = start + sel.length + 4; + ta.selectionStart = urlStart; + ta.selectionEnd = urlStart + url.length; + this._value = ta.value; + this._pushHistory(); + this._render(); + this._emit('change', this._value); + } + + _insertTable(rows = 3, cols = 3) { + const header = Array.from({ length: cols }, (_, i) => `${i18nT('tableCols') || '列'}${i + 1}`).join(' | '); + const sep = Array.from({ length: cols }, () => '---').join(' | '); + let md = `| ${header} |\n| ${sep} |\n`; + for (let r = 1; r < rows; r++) { + const row = Array.from({ length: cols }, () => ' ').join(' | '); + md += `| ${row} |\n`; + } + this._insertBlock('\n' + md); + } + + // ============ 模式与全屏 ============ + + setMode(mode) { + if (!MODES.includes(mode) || mode === this._mode) return this; + this._mode = mode; + this.bodyEl.className = `me-body me-mode-${mode}`; + this._updateModeButtons(); + if (mode !== 'edit') this._render(); + this._emit('modeChange', mode); + if (typeof this.config.onModeChange === 'function') { + try { this.config.onModeChange(mode, this); } catch (e) { console.error(e); } + } + return this; + } + + getMode() { return this._mode; } + + _updateModeButtons() { + this.toolbarEl.querySelectorAll('.me-btn[data-mode]').forEach((b) => { + b.classList.toggle('me-active', b.dataset.mode === this._mode); + }); + } + + toggleFullscreen() { + this._fullscreen = !this._fullscreen; + this.el.classList.toggle('me-fullscreen', this._fullscreen); + // 更新全屏按钮图标/标题 + const fsBtn = this.toolbarEl.querySelector('.me-btn-fullscreen'); + if (fsBtn) { + fsBtn.title = this._fullscreen ? (i18nT('fullscreenExit') || '退出全屏') : (i18nT('fullscreen') || '全屏'); + } + this._emit('fullscreen', this._fullscreen); + if (typeof this.config.onFullscreen === 'function') { + try { this.config.onFullscreen(this._fullscreen, this); } catch (e) { console.error(e); } + } + return this; + } + + isFullscreen() { return this._fullscreen; } + + exitFullscreen() { + if (this._fullscreen) this.toggleFullscreen(); + return this; + } + + // ============ 字数统计 ============ + + _updateWordCount() { + if (!this.config.wordCount || !this.statusEl) return; + const text = this._value || ''; + const chars = text.length; + // 中文字符数 + 英文单词数 + const cnChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length; + const enWords = (text.replace(/[\u4e00-\u9fa5]/g, ' ').match(/[a-zA-Z0-9]+/g) || []).length; + const words = cnChars + enWords; + const lines = text ? text.split('\n').length : 0; + const readingMin = Math.max(1, Math.ceil(words / 300)); + + const parts = [ + `${i18nT('characters')}: ${chars}`, + `${i18nT('words')}: ${words}`, + `${i18nT('lines')}: ${lines}`, + `${i18nT('readingTime')}: ${readingMin} ${i18nT('minutes')}`, + ]; + this.statusEl.textContent = parts.join(' · '); + } + + getStats() { + const text = this._value || ''; + const cnChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length; + const enWords = (text.replace(/[\u4e00-\u9fa5]/g, ' ').match(/[a-zA-Z0-9]+/g) || []).length; + return { + characters: text.length, + words: cnChars + enWords, + chineseChars: cnChars, + englishWords: enWords, + lines: text ? text.split('\n').length : 0, + readingTime: Math.max(1, Math.ceil((cnChars + enWords) / 300)), + }; + } + + // ============ 公共 API ============ + + getValue() { return this._value; } + + setValue(md, opts = {}) { + this._value = md || ''; + this.textarea.value = this._value; + if (!opts.silent) this._pushHistory(); + this._render(); + this._updateWordCount(); + if (!opts.silent) { + this._emit('change', this._value); + if (typeof this.config.onChange === 'function') { + try { this.config.onChange(this._value, this); } catch (e) { console.error(e); } + } + } + return this; + } + + getHTML() { + const env = { highlight: this._highlightFn, locale: getCurrentLocale() }; + let html = this._renderFn(this._value, env); + if (typeof this.config.sanitize === 'function') { + try { html = this.config.sanitize(html); } catch (e) { console.error(e); } + } + return html; + } + + insert(text, opts = {}) { + const ta = this.textarea; + const start = ta.selectionStart; + const end = ta.selectionEnd; + const replace = opts.replace || false; + ta.value = ta.value.slice(0, replace ? start : start) + text + ta.value.slice(replace ? end : end); + ta.focus(); + const pos = start + text.length; + ta.selectionStart = ta.selectionEnd = pos; + this._value = ta.value; + this._pushHistory(); + this._render(); + this._updateWordCount(); + this._emit('change', this._value); + return this; + } + + /** + * 在选区两侧包裹文本 + */ + wrap(before, after) { + this._wrapSelection(before, after || before); + return this; + } + + focus() { this.textarea.focus(); return this; } + blur() { this.textarea.blur(); return this; } + + enable() { + this.textarea.disabled = false; + this.el.classList.remove('me-disabled'); + return this; + } + + disable() { + this.textarea.disabled = true; + this.el.classList.add('me-disabled'); + return this; + } + + isDisabled() { return this.textarea.disabled; } + + /** + * 注册实例事件监听 + * 支持事件:change/input/focus/blur/save/modeChange/fullscreen/beforeCreate/afterCreate/beforeRender/afterRender/destroy + */ + on(name, fn) { + if (!this._listeners[name]) this._listeners[name] = []; + this._listeners[name].push(fn); + return () => this.off(name, fn); + } + + off(name, fn) { + const list = this._listeners[name]; + if (list) this._listeners[name] = list.filter((f) => f !== fn); + return this; + } + + _emit(name, ...args) { + const list = this._listeners[name]; + if (list) list.forEach((fn) => { + try { fn(...args, this); } catch (e) { console.error(`MeEditor event "${name}" error:`, e); } + }); + } + + /** + * 安装插件 + * @param {string|Object} plugin - 预设插件名或插件对象 + * @param {Object} options - 插件配置 + */ + use(plugin, options = {}) { + let p = plugin; + if (typeof plugin === 'string') { + p = presetPlugins[plugin]; + if (!p) { + console.warn(`MeEditor: preset plugin "${plugin}" not found`); + return this; + } + } + if (!p || typeof p !== 'object') { + console.warn('MeEditor: invalid plugin'); + return this; + } + const merged = { ...p, ...options }; + if (typeof merged.install === 'function') { + try { + merged.install(this); + } catch (e) { + console.error(`MeEditor: plugin "${merged.name || 'custom'}" install error:`, e); + } + } + this._plugins.push(merged); + return this; + } + + getPlugins() { return [...this._plugins]; } + + /** + * 向工具栏追加自定义按钮 + */ + addToolbarButton(config) { + if (!config || !config.action) return this; + const btn = document.createElement('button'); + btn.type = 'button'; + btn.className = `me-btn me-btn-custom-${config.action}`; + btn.dataset.action = config.action; + btn.title = config.title || config.action; + btn.setAttribute('aria-label', config.title || config.action); + btn.innerHTML = config.icon || `${escapeHTML(config.text || config.action)}`; + if (typeof config.onClick === 'function') { + btn.addEventListener('click', () => config.onClick(this)); + } else if (config.action) { + // 注册到 exec 动作 + this._customActions = this._customActions || {}; + this._customActions[config.action] = config.handler; + } + this.toolbarEl.insertBefore(btn, this.toolbarEl.querySelector('.me-toolbar-group') || null); + return this; + } + + /** + * 销毁编辑器 + */ + destroy() { + if (this._destroyed) return; + MarkdownEditor.trigger('beforeDestroy', this); + + if (this._renderRaf) cancelAnimationFrame(this._renderRaf); + if (this._historyTimer) clearTimeout(this._historyTimer); + + this._cleanups.forEach((fn) => { try { fn(); } catch (_) {} }); + this._cleanups = []; + + // 销毁插件 + this._plugins.forEach((p) => { + if (typeof p.destroy === 'function') { + try { p.destroy(this); } catch (_) {} + } + }); + this._plugins = []; + + if (this.el && this.el.parentNode) { + this.el.parentNode.removeChild(this.el); + } + this.el = null; + this.textarea = null; + this.previewEl = null; + + this._destroyed = true; + + if (typeof this.config.onDestroy === 'function') { + try { this.config.onDestroy(this); } catch (e) { console.error(e); } + } + + // 先触发 destroy 事件,再清空监听器(否则 destroy 监听器永远收不到事件) + this._emit('destroy'); + this._listeners = {}; + MarkdownEditor.trigger('afterDestroy', this); + } + + isDestroyed() { return this._destroyed; } + + /** + * 获取编辑器状态 + */ + getStatus() { + return { + id: this.id, + mode: this._mode, + theme: this.config.theme, + locale: getCurrentLocale(), + fullscreen: this._fullscreen, + disabled: this.textarea ? this.textarea.disabled : false, + destroyed: this._destroyed, + plugins: this._plugins.map((p) => p.name || 'custom'), + }; + } +} + +export { MarkdownEditor as Editor, MarkdownEditor }; +export default MarkdownEditor; diff --git a/src/i18n.js b/src/i18n.js new file mode 100644 index 0000000..eef9dfc --- /dev/null +++ b/src/i18n.js @@ -0,0 +1,391 @@ +/** + * MetonaEditor i18n - 国际化管理 + * @module i18n + * @version 0.1.0 + * @description 多语言支持、语言切换和翻译管理 + */ + +import { LOCALES } from './constants.js'; + +let currentLocale = 'zh-CN'; +let localeListeners = new Set(); +let fallbackLocale = 'zh-CN'; + +/** + * 获取当前语言 + */ +export const getCurrentLocale = () => currentLocale; + +/** + * 设置当前语言 + */ +export const setCurrentLocale = (locale) => { + if (!LOCALES[locale]) { + console.warn(`Locale "${locale}" not found, falling back to "${fallbackLocale}"`); + locale = fallbackLocale; + } + + currentLocale = locale; + notifyLocaleListeners(locale); + saveLocale(locale); +}; + +/** + * 翻译函数 + */ +export const t = (key, params = {}) => { + const currentTranslation = getTranslation(currentLocale, key); + if (currentTranslation !== undefined) { + return interpolate(currentTranslation, params); + } + + if (currentLocale !== fallbackLocale) { + const fallbackTranslation = getTranslation(fallbackLocale, key); + if (fallbackTranslation !== undefined) { + return interpolate(fallbackTranslation, params); + } + } + + console.warn(`Translation missing for key "${key}" in locale "${currentLocale}"`); + return key; +}; + +/** + * 获取翻译 + */ +const getTranslation = (locale, key) => { + const localeData = LOCALES[locale]; + if (!localeData) return undefined; + + const keys = key.split('.'); + let result = localeData; + + for (const k of keys) { + if (result && typeof result === 'object' && k in result) { + result = result[k]; + } else { + return undefined; + } + } + + return typeof result === 'string' ? result : undefined; +}; + +/** + * 插值函数 + */ +const interpolate = (str, params) => { + return str.replace(/\{(\w+)\}/g, (match, key) => { + return params[key] !== undefined ? params[key] : match; + }); +}; + +/** + * 检查翻译是否存在 + */ +export const hasTranslation = (key) => { + return getTranslation(currentLocale, key) !== undefined || + getTranslation(fallbackLocale, key) !== undefined; +}; + +/** + * 获取所有翻译 + */ +export const getTranslations = (locale) => { + return LOCALES[locale] || {}; +}; + +/** + * 添加翻译 + */ +export const addTranslations = (locale, translations) => { + if (!LOCALES[locale]) { + LOCALES[locale] = {}; + } + + deepMerge(LOCALES[locale], translations); +}; + +/** + * 深度合并对象 + */ +const deepMerge = (target, source) => { + for (const key in source) { + if (source[key] instanceof Object && key in target && target[key] instanceof Object) { + deepMerge(target[key], source[key]); + } else { + target[key] = source[key]; + } + } + return target; +}; + +/** + * 获取支持的语言列表 + */ +export const getSupportedLocales = () => { + return Object.keys(LOCALES); +}; + +/** + * 检查语言是否支持 + */ +export const isLocaleSupported = (locale) => { + return locale in LOCALES; +}; + +/** + * 获取语言名称 + */ +export const getLocaleName = (locale) => { + const names = { + 'zh-CN': '简体中文', + 'zh-TW': '繁體中文', + 'en-US': 'English (US)', + 'en-GB': 'English (UK)', + 'ja': '日本語', + 'ko': '한국어', + 'fr': 'Français', + 'de': 'Deutsch', + 'es': 'Español', + 'pt': 'Português', + 'ru': 'Русский', + 'ar': 'العربية', + 'hi': 'हिन्दी', + 'th': 'ไทย', + 'vi': 'Tiếng Việt', + 'id': 'Bahasa Indonesia', + 'ms': 'Bahasa Melayu', + 'tr': 'Türkçe', + 'it': 'Italiano', + 'nl': 'Nederlands', + 'pl': 'Polski', + }; + + return names[locale] || locale; +}; + +/** + * 获取语言方向 + */ +export const getLocaleDirection = (locale) => { + const rtlLocales = ['ar', 'he', 'fa', 'ur', 'yi', 'ps', 'sd', 'ug']; + return rtlLocales.includes(locale) ? 'rtl' : 'ltr'; +}; + +/** + * 格式化数字 + */ +export const formatNumber = (number, options = {}) => { + try { + return new Intl.NumberFormat(currentLocale, options).format(number); + } catch (e) { + return number.toString(); + } +}; + +/** + * 格式化货币 + */ +export const formatCurrency = (amount, currency = 'USD', options = {}) => { + try { + return new Intl.NumberFormat(currentLocale, { + style: 'currency', + currency, + ...options, + }).format(amount); + } catch (e) { + return amount.toString(); + } +}; + +/** + * 格式化日期 + */ +export const formatDate = (date, options = {}) => { + try { + const dateObj = date instanceof Date ? date : new Date(date); + return new Intl.DateTimeFormat(currentLocale, options).format(dateObj); + } catch (e) { + return date.toString(); + } +}; + +/** + * 添加语言监听器 + */ +export const addLocaleListener = (listener) => { + localeListeners.add(listener); + + return () => { + localeListeners.delete(listener); + }; +}; + +/** + * 移除语言监听器 + */ +export const removeLocaleListener = (listener) => { + localeListeners.delete(listener); +}; + +/** + * 通知语言监听器 + */ +const notifyLocaleListeners = (locale) => { + localeListeners.forEach((listener) => { + try { + listener(locale); + } catch (e) { + console.error('Locale listener error:', e); + } + }); +}; + +/** + * 清除所有语言监听器 + */ +export const clearLocaleListeners = () => { + localeListeners.clear(); +}; + +/** + * 保存语言到本地存储 + */ +export const saveLocale = (locale) => { + if (typeof localStorage !== 'undefined') { + try { + localStorage.setItem('metona-editor-locale', locale); + } catch (e) { + console.warn('Failed to save locale:', e); + } + } +}; + +/** + * 从本地存储加载语言 + */ +export const loadLocale = () => { + if (typeof localStorage !== 'undefined') { + try { + return localStorage.getItem('metona-editor-locale') || getDefaultLocale(); + } catch (e) { + console.warn('Failed to load locale:', e); + } + } + return getDefaultLocale(); +}; + +/** + * 获取默认语言 + */ +export const getDefaultLocale = () => { + if (typeof navigator !== 'undefined') { + const browserLocale = navigator.language || navigator.userLanguage; + if (browserLocale && isLocaleSupported(browserLocale)) { + return browserLocale; + } + + const shortLocale = browserLocale?.split('-')[0]; + if (shortLocale && isLocaleSupported(shortLocale)) { + return shortLocale; + } + } + + return fallbackLocale; +}; + +/** + * 初始化国际化系统 + */ +export const initI18n = () => { + const savedLocale = loadLocale(); + setCurrentLocale(savedLocale); +}; + +/** + * 切换语言 + */ +export const switchLocale = (locale) => { + setCurrentLocale(locale); +}; + +/** + * 国际化工具 — 代理层 + */ +export const i18nUtils = { + t, + getCurrentLocale, + setCurrentLocale, + switchLocale, + getFallbackLocale: () => fallbackLocale, + setFallbackLocale: (locale) => { fallbackLocale = locale; }, + hasTranslation, + getTranslations, + addTranslations, + getSupportedLocales, + isLocaleSupported, + getLocaleName, + getLocaleDirection, + formatNumber, + formatCurrency, + formatDate, + addLocaleListener, + removeLocaleListener, + clearLocaleListeners, + initI18n, + saveLocale, + loadLocale, + getDefaultLocale, +}; + +/** + * 预设语言包 + */ +export const presetLocales = { + 'zh-CN': { + name: '简体中文', + nativeName: '简体中文', + direction: 'ltr', + translations: LOCALES['zh-CN'], + }, + 'en-US': { + name: 'English (US)', + nativeName: 'English (US)', + direction: 'ltr', + translations: LOCALES['en-US'], + }, +}; + +/** + * 创建国际化管理器 + */ +export const createI18nManager = () => { + return { + t, + getCurrentLocale, + setCurrentLocale, + switchLocale, + getFallbackLocale: () => fallbackLocale, + setFallbackLocale: (locale) => { fallbackLocale = locale; }, + hasTranslation, + getTranslations, + addTranslations, + getSupportedLocales, + isLocaleSupported, + getLocaleName, + getLocaleDirection, + formatNumber, + formatCurrency, + formatDate, + addLocaleListener, + removeLocaleListener, + clearLocaleListeners, + initI18n, + saveLocale, + loadLocale, + getDefaultLocale, + }; +}; + +export { i18nUtils as default }; diff --git a/src/icons.js b/src/icons.js new file mode 100644 index 0000000..abb9b4d --- /dev/null +++ b/src/icons.js @@ -0,0 +1,56 @@ +/** + * MetonaEditor Icons — 工具栏图标SVG定义 + * @module icons + * @version 0.1.0 + * @description 工具栏格式化按钮 SVG 图标 + */ + +export const ICONS = { + bold: ``, + + italic: ``, + + underline: ``, + + strikethrough: ``, + + h1: ``, + + h2: ``, + + h3: ``, + + quote: ``, + + code: ``, + + link: ``, + + image: ``, + + table: ``, + + ul: ``, + + ol: ``, + + indent: ``, + + outdent: ``, + + hr: ``, + + undo: ``, + + redo: ``, + + preview: ``, + + split: ``, + + edit: ``, + + fullscreen: ``, + + theme: ``, +}; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..e120dc6 --- /dev/null +++ b/src/index.js @@ -0,0 +1,248 @@ +/** + * MetonaEditor - 轻量级 Markdown Editor 库 + * @module metona-editor + * @version 0.1.0 + * @author thzxx + * @description 现代化、零依赖、易扩展、易维护、易使用的 Markdown 编辑器库。单文件,开箱即用。 + * @license MIT + * + * 用法: + * // 浏览器 + * + * const editor = MeEditor.create('#editor', { mode: 'split' }); + * + * // ES Module + * import MeEditor from 'metona-editor'; + * const editor = MeEditor.create(container, options); + * + * // 直接使用类 + * import { MarkdownEditor } from 'metona-editor'; + * const editor = new MarkdownEditor(container, options); + */ + +import { MarkdownEditor } from './core.js'; +import { parseMarkdown, safeUrl, slugify } from './parser.js'; +import { themeUtils } from './themes.js'; +import { i18nUtils } from './i18n.js'; +import { pluginUtils, presetPlugins } from './plugins.js'; +import { animationUtils } from './animations.js'; +import { DEFAULTS, ICONS, THEMES, EDIT_MODES, DEFAULT_TOOLBAR, TOOLBAR_ACTIONS } from './constants.js'; + +// 版本信息 +const VERSION = '0.1.0'; + +/** + * 全局默认插件队列 + * 通过 beforeCreate 钩子自动应用到所有后续创建的编辑器实例 + */ +const globalPlugins = []; + +// 注册全局钩子:每个新实例创建时自动安装全局默认插件 +// 注意:core.js 构造函数中 beforeCreate 在 config.plugins 安装之前触发, +// 且 this._plugins 已初始化为 [],因此此处调用 editor.use() 安全。 +MarkdownEditor.on('beforeCreate', (editor) => { + if (!editor || editor._destroyed) return; + globalPlugins.forEach((p) => { + try { editor.use(p); } catch (e) { console.error('MeEditor global plugin install error:', e); } + }); +}); + +/** + * 创建编辑器实例(工厂函数,推荐用法) + * @param {string|HTMLElement} container - 容器选择器或 DOM 元素 + * @param {Object} [options] - 配置项(见 DEFAULTS) + * @returns {MarkdownEditor} 编辑器实例 + */ +function create(container, options = {}) { + return new MarkdownEditor(container, options); +} + +/** + * 注册全局默认插件(作用于所有后续创建的编辑器实例) + * @param {string|Object} plugin - 预设插件名 或 插件对象 + * @param {Object} [options] - 插件配置(覆盖预设默认值) + * @returns {typeof api} 返回 api 自身以支持链式调用 + */ +function use(plugin, options = {}) { + let p = plugin; + if (typeof plugin === 'string') { + p = presetPlugins[plugin]; + if (!p) { + console.warn(`MeEditor: preset plugin "${plugin}" not found`); + return api; + } + } + if (!p || typeof p !== 'object') { + console.warn('MeEditor: invalid plugin'); + return api; + } + const merged = (options && typeof options === 'object' && Object.keys(options).length > 0) + ? { ...p, ...options } + : p; + globalPlugins.push(merged); + return api; +} + +/** + * 注册全局事件钩子(转发到 MarkdownEditor 静态钩子,所有实例共享) + * 可用钩子:beforeCreate / afterCreate / beforeRender / afterRender / beforeDestroy / afterDestroy + * @param {string} name - 钩子名 + * @param {Function} fn - 回调 (editor) => void + * @returns {Function} 取消注册函数 + */ +function on(name, fn) { + return MarkdownEditor.on(name, fn); +} + +/** + * 注销全局事件钩子 + */ +function off(name, fn) { + MarkdownEditor.off(name, fn); + return api; +} + +/** + * 切换全局主题(转发到 themeUtils) + */ +function setTheme(theme, options) { + if (themeUtils && typeof themeUtils.switchTheme === 'function') { + themeUtils.switchTheme(theme, options); + } + return api; +} + +/** + * 切换全局语言(转发到 i18nUtils) + */ +function setLocale(locale) { + if (i18nUtils && typeof i18nUtils.setCurrentLocale === 'function') { + i18nUtils.setCurrentLocale(locale); + } + return api; +} + +/** + * 销毁所有全局资源(主题监听、i18n 监听、动画、全局插件队列) + * 注意:此方法不销毁已创建的编辑器实例,请单独调用每个实例的 destroy() + */ +function destroy() { + if (themeUtils) { + if (typeof themeUtils.clearThemeListeners === 'function') { + try { themeUtils.clearThemeListeners(); } catch (_) {} + } + if (typeof themeUtils.unwatchSystemTheme === 'function') { + try { themeUtils.unwatchSystemTheme(); } catch (_) {} + } + } + if (i18nUtils && typeof i18nUtils.clearLocaleListeners === 'function') { + try { i18nUtils.clearLocaleListeners(); } catch (_) {} + } + if (animationUtils && typeof animationUtils.cancelAll === 'function') { + try { animationUtils.cancelAll(); } catch (_) {} + } + globalPlugins.length = 0; +} + +/** + * 获取库运行状态 + */ +function getStatus() { + return { + version: VERSION, + theme: (themeUtils && typeof themeUtils.getCurrentTheme === 'function') ? themeUtils.getCurrentTheme() : 'auto', + locale: (i18nUtils && typeof i18nUtils.getCurrentLocale === 'function') ? i18nUtils.getCurrentLocale() : 'zh-CN', + globalPlugins: globalPlugins.map((p) => p.name || 'custom'), + presetPlugins: Object.keys(presetPlugins || {}), + }; +} + +// 初始化主题与国际化(浏览器环境) +if (typeof themeUtils === 'object' && typeof themeUtils.initTheme === 'function') { + try { themeUtils.initTheme(); } catch (_) {} +} +if (typeof i18nUtils === 'object' && typeof i18nUtils.initI18n === 'function') { + try { i18nUtils.initI18n(); } catch (_) {} +} + +/** + * 顶层 API 对象(默认导出) + */ +const api = { + // 版本 + VERSION, + version: VERSION, + + // 核心类 + MarkdownEditor, + Editor: MarkdownEditor, + + // 工厂函数(推荐入口) + create, + + // 顶层 API + use, + on, + off, + setTheme, + setLocale, + destroy, + getStatus, + + // 内置解析器(可单独使用或替换) + parseMarkdown, + safeUrl, + slugify, + + // 工具集 + themes: themeUtils, + i18n: i18nUtils, + animations: animationUtils, + plugins: pluginUtils, + presetPlugins, + + // 常量 + DEFAULTS, + ICONS, + THEMES, + EDIT_MODES, + DEFAULT_TOOLBAR, + TOOLBAR_ACTIONS, +}; + +// 浏览器环境全局注册(UMD/CDN 场景:window.MeEditor) +if (typeof window !== 'undefined') { + window.MeEditor = api; +} + +export default api; +export { + api, + api as meEditor, + api as MeEditor, + MarkdownEditor, + MarkdownEditor as Editor, + create, + use, + on, + off, + setTheme, + setLocale, + destroy, + getStatus, + parseMarkdown, + safeUrl, + slugify, + themeUtils, + i18nUtils, + pluginUtils, + presetPlugins, + animationUtils, + DEFAULTS, + ICONS, + THEMES, + EDIT_MODES, + DEFAULT_TOOLBAR, + TOOLBAR_ACTIONS, + VERSION, +}; diff --git a/src/locales.js b/src/locales.js new file mode 100644 index 0000000..5bf6b39 --- /dev/null +++ b/src/locales.js @@ -0,0 +1,168 @@ +/** + * MetonaEditor Locales — 国际化翻译数据 + * @module locales + * @version 0.1.0 + * @description 内置 zh-CN / en-US 完整翻译 + */ + +export const LOCALES = { + 'zh-CN': { + bold: '粗体', + italic: '斜体', + underline: '下划线', + strikethrough: '删除线', + h1: '标题1', + h2: '标题2', + h3: '标题3', + quote: '引用', + code: '代码', + link: '链接', + image: '图片', + table: '表格', + ul: '无序列表', + ol: '有序列表', + indent: '增加缩进', + outdent: '减少缩进', + hr: '分隔线', + undo: '撤销', + redo: '重做', + edit: '编辑', + split: '分屏', + preview: '预览', + fullscreen: '全屏', + fullscreenExit: '退出全屏', + theme: '主题', + light: '亮色', + dark: '暗色', + auto: '自动', + warm: '暖色', + wordCount: '字数统计', + characters: '字符', + words: '词数', + lines: '行数', + readingTime: '阅读', + minutes: '分钟', + placeholder: '开始输入 Markdown...', + empty: '暂无内容', + copied: '已复制', + copyContent: '复制内容', + copyHTML: '复制 HTML', + copySuccess: '复制成功', + copyFailed: '复制失败', + clearContent: '清空内容', + clearConfirm: '确定要清空所有内容吗?', + linkPlaceholder: '请输入链接地址', + imagePlaceholder: '请输入图片地址', + altPlaceholder: '请输入替代文本', + tableRows: '行数', + tableCols: '列数', + confirm: '确认', + cancel: '取消', + exportMarkdown: '导出 Markdown', + exportHTML: '导出 HTML', + search: '搜索', + replace: '替换', + replaceAll: '全部替换', + searchPlaceholder: '查找内容', + replacePlaceholder: '替换为', + findNext: '查找下一个', + findPrev: '查找上一个', + matchCase: '区分大小写', + wholeWord: '全字匹配', + close: '关闭', + open: '打开', + save: '保存', + saved: '已保存', + saving: '保存中...', + delete: '删除', + confirmDelete: '确定要删除吗?', + unsavedChanges: '有未保存的更改', + error: '错误', + success: '成功', + warning: '警告', + info: '信息', + loading: '加载中...', + retry: '重试', + renderError: '渲染失败', + }, + + 'en-US': { + bold: 'Bold', + italic: 'Italic', + underline: 'Underline', + strikethrough: 'Strikethrough', + h1: 'Heading 1', + h2: 'Heading 2', + h3: 'Heading 3', + quote: 'Quote', + code: 'Code', + link: 'Link', + image: 'Image', + table: 'Table', + ul: 'Bullet List', + ol: 'Numbered List', + indent: 'Indent', + outdent: 'Outdent', + hr: 'Horizontal Rule', + undo: 'Undo', + redo: 'Redo', + edit: 'Edit', + split: 'Split', + preview: 'Preview', + fullscreen: 'Fullscreen', + fullscreenExit: 'Exit Fullscreen', + theme: 'Theme', + light: 'Light', + dark: 'Dark', + auto: 'Auto', + warm: 'Warm', + wordCount: 'Word Count', + characters: 'Characters', + words: 'Words', + lines: 'Lines', + readingTime: 'Reading', + minutes: 'min', + placeholder: 'Start typing Markdown...', + empty: 'No content', + copied: 'Copied', + copyContent: 'Copy Content', + copyHTML: 'Copy HTML', + copySuccess: 'Copied successfully', + copyFailed: 'Copy failed', + clearContent: 'Clear Content', + clearConfirm: 'Clear all content?', + linkPlaceholder: 'Enter link URL', + imagePlaceholder: 'Enter image URL', + altPlaceholder: 'Enter alt text', + tableRows: 'Rows', + tableCols: 'Columns', + confirm: 'Confirm', + cancel: 'Cancel', + exportMarkdown: 'Export Markdown', + exportHTML: 'Export HTML', + search: 'Search', + replace: 'Replace', + replaceAll: 'Replace All', + searchPlaceholder: 'Find', + replacePlaceholder: 'Replace with', + findNext: 'Find Next', + findPrev: 'Find Previous', + matchCase: 'Match Case', + wholeWord: 'Whole Word', + close: 'Close', + open: 'Open', + save: 'Save', + saved: 'Saved', + saving: 'Saving...', + delete: 'Delete', + confirmDelete: 'Are you sure you want to delete?', + unsavedChanges: 'You have unsaved changes', + error: 'Error', + success: 'Success', + warning: 'Warning', + info: 'Info', + loading: 'Loading...', + retry: 'Retry', + renderError: 'Render failed', + }, +}; diff --git a/src/parser.js b/src/parser.js new file mode 100644 index 0000000..5056c61 --- /dev/null +++ b/src/parser.js @@ -0,0 +1,322 @@ +/** + * MetonaEditor Parser - 轻量 Markdown 解析器 + * @module parser + * @version 0.1.0 + * @description 自研零依赖 Markdown 解析器,覆盖 CommonMark 子集 + GFM 扩展 + * + * 支持语法: + * - 标题 # ~ ###### + * - 段落 / 换行 + * - 粗体 **text** / __text__ + * - 斜体 *text* / _text_ + * - 删除线 ~~text~~ + * - 行内代码 `code` + * - 代码块 ```lang ... ``` 与 ~~~ ... ~~~ + * - 引用块 > + * - 无序列表 - / * / + + * - 有序列表 1. + * - 任务列表 - [ ] / - [x] + * - 水平线 --- / *** / ___ + * - 表格 | a | b | + * - 链接 [text](url) / 自动链接 + * - 图片 ![alt](url) + * - 转义字符 \X + * + * 安全:所有文本经 HTML 转义;URL 过滤 javascript:/vbscript: 等危险协议 + * 扩展:env.highlight(code, lang) => html 钩子用于代码高亮 + */ + +import { escapeHTML } from './utils.js'; + +/** + * 危险协议过滤 + */ +const safeUrl = (url) => { + if (!url) return ''; + const u = String(url).trim(); + if (/^(javascript|vbscript|file):/i.test(u)) return ''; + // 仅允许 data:image/ + if (/^data:/i.test(u) && !/^data:image\//i.test(u)) return ''; + return u; +}; + +/** + * 主解析入口 + * @param {string} md - Markdown 源文本 + * @param {Object} env - 渲染环境 { highlight, locale } + * @returns {string} HTML 字符串 + */ +export const parseMarkdown = (md, env = {}) => { + if (md == null) return ''; + const text = String(md).replace(/\r\n?/g, '\n'); + const lines = text.split('\n'); + const tokens = []; + let i = 0; + + while (i < lines.length) { + const line = lines[i]; + + // 空行 + if (/^\s*$/.test(line)) { i++; continue; } + + // 围栏代码块 + const fence = line.match(/^(\s*)(`{3,}|~{3,})\s*([\w+-]*)\s*$/); + if (fence) { + const fenceChar = fence[2][0]; + const lang = fence[3] || ''; + const codeLines = []; + i++; + const closeRe = new RegExp('^\\s*' + fenceChar + '{3,}\\s*$'); + while (i < lines.length && !closeRe.test(lines[i])) { + codeLines.push(lines[i]); + i++; + } + if (i < lines.length) i++; // 跳过结束围栏 + tokens.push({ type: 'code', lang, content: codeLines.join('\n') }); + continue; + } + + // ATX 标题 + const h = line.match(/^(#{1,6})\s+(.*?)(?:\s+#{1,6})?\s*$/); + if (h) { + tokens.push({ type: 'heading', level: h[1].length, text: h[2] }); + i++; + continue; + } + + // 水平线 + if (/^\s*([-*_])(\s*\1){2,}\s*$/.test(line)) { + tokens.push({ type: 'hr' }); + i++; + continue; + } + + // 引用块 + if (/^\s*>/.test(line)) { + const quote = []; + while (i < lines.length && /^\s*>/.test(lines[i])) { + quote.push(lines[i].replace(/^\s*>?\s?/, '')); + i++; + } + tokens.push({ type: 'quote', content: quote.join('\n') }); + continue; + } + + // 表格:当前行含 |,下一行是分隔行 + if (/\|/.test(line) && i + 1 < lines.length && isTableSeparator(lines[i + 1])) { + const header = line; + i += 2; + const rows = []; + while (i < lines.length && /\|/.test(lines[i]) && !/^\s*$/.test(lines[i])) { + rows.push(lines[i]); + i++; + } + tokens.push({ type: 'table', header, rows }); + continue; + } + + // 无序列表 / 任务列表 + if (/^\s*[-*+]\s/.test(line)) { + const items = []; + while (i < lines.length && /^\s*[-*+]\s/.test(lines[i])) { + const raw = lines[i].replace(/^\s*[-*+]\s/, ''); + const task = raw.match(/^\[([ xX])\]\s+(.*)$/); + if (task) { + items.push({ text: task[2], checked: task[1].toLowerCase() === 'x', task: true }); + } else { + items.push({ text: raw, checked: false, task: false }); + } + i++; + } + tokens.push({ type: 'ul', items }); + continue; + } + + // 有序列表 + if (/^\s*\d+\.\s/.test(line)) { + const items = []; + while (i < lines.length && /^\s*\d+\.\s/.test(lines[i])) { + items.push(lines[i].replace(/^\s*\d+\.\s/, '')); + i++; + } + tokens.push({ type: 'ol', items }); + continue; + } + + // 段落(收集连续非块级行) + const para = []; + while (i < lines.length) { + const l = lines[i]; + if (/^\s*$/.test(l)) break; + if (/^\s*(`{3,}|~{3,})/.test(l)) break; + if (/^#{1,6}\s/.test(l)) break; + if (/^\s*>/.test(l)) break; + if (/^\s*[-*+]\s/.test(l)) break; + if (/^\s*\d+\.\s/.test(l)) break; + if (/^\s*([-*_])(\s*\1){2,}\s*$/.test(l)) break; + para.push(l); + i++; + } + tokens.push({ type: 'paragraph', text: para.join('\n') }); + } + + return tokens.map((tok) => renderToken(tok, env)).join('\n'); +}; + +/** + * 表格分隔行判定 + */ +const isTableSeparator = (line) => { + return /^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/.test(line) && /-/.test(line); +}; + +/** + * 渲染单个块级 token + */ +const renderToken = (tok, env) => { + switch (tok.type) { + case 'heading': { + const id = slugify(tok.text); + return `${renderInline(tok.text, env)}`; + } + case 'paragraph': + return `

      ${renderInline(tok.text, env)}

      `; + case 'hr': + return '
      '; + case 'quote': + return `
      ${parseMarkdown(tok.content, env)}
      `; + case 'code': + return renderCode(tok.content, tok.lang, env); + case 'ul': + return `
        ${tok.items.map((it) => renderListItem(it, env)).join('')}
      `; + case 'ol': + return `
        ${tok.items.map((it) => `
      1. ${renderInline(it, env)}
      2. `).join('')}
      `; + case 'table': + return renderTable(tok, env); + default: + return ''; + } +}; + +/** + * 渲染列表项 + */ +const renderListItem = (it, env) => { + if (it.task) { + const checked = it.checked ? ' checked' : ''; + return `
    3. ${renderInline(it.text, env)}
    4. `; + } + return `
    5. ${renderInline(it.text, env)}
    6. `; +}; + +/** + * 渲染代码块 + */ +const renderCode = (code, lang, env) => { + const langClass = lang ? ` class="language-${escapeHTML(lang)}"` : ''; + if (env.highlight && typeof env.highlight === 'function' && lang) { + try { + const highlighted = env.highlight(code, lang); + if (typeof highlighted === 'string') { + return `
      ${highlighted}
      `; + } + } catch (e) { + console.error('MeEditor highlight error:', e); + } + } + return `
      ${escapeHTML(code)}
      `; +}; + +/** + * 渲染表格 + */ +const renderTable = (tok, env) => { + const splitRow = (r) => r.replace(/^\s*\|?\s*|\s*\|?\s*$/g, '').split(/\s*\|\s*/); + const headers = splitRow(tok.header); + let html = '
      '; + html += headers.map((h) => ``).join(''); + html += ''; + html += tok.rows.map((r) => { + const cells = splitRow(r); + return `${cells.map((c) => ``).join('')}`; + }).join(''); + html += '
      ${renderInline(h, env)}
      ${renderInline(c, env)}
      '; + return html; +}; + +/** + * 渲染内联元素 + * 顺序:提取代码占位 -> escape -> 图片 -> 链接 -> 自动链接 -> 粗体 -> 斜体 -> 删除线 -> 还原代码 -> 换行 + */ +const renderInline = (text, env) => { + if (!text) return ''; + const codes = []; + let s = String(text); + + // 1. 提取行内代码到占位符,避免被后续正则破坏 + s = s.replace(/`+([^`]+?)`+/g, (m, c) => { + const idx = codes.length; + codes.push(c); + return `\u0000${idx}\u0000`; + }); + + // 2. HTML 转义(占位符中的零字符不被影响) + s = escapeHTML(s); + + // 3. 图片 ![alt](url "title") + s = s.replace(/!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^&]*)")?\)/g, (m, alt, url, title) => { + const u = safeUrl(url); + if (!u) return escapeHTML(m); + const t = title ? ` title="${title}"` : ''; + return `${alt}`; + }); + + // 4. 链接 [text](url "title") + s = s.replace(/\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^&]*)")?\)/g, (m, txt, url, title) => { + const u = safeUrl(url); + if (!u) return escapeHTML(m); + const t = title ? ` title="${title}"` : ''; + return `${txt}`; + }); + + // 5. 自动链接 + s = s.replace(/<(https?:\/\/[^\s&]+)>/g, (m, url) => { + const u = safeUrl(url); + if (!u) return m; + return `${url}`; + }); + + // 6. 粗体 **text** / __text__ + s = s.replace(/\*\*([^*]+)\*\*/g, '$1'); + s = s.replace(/__([^_]+)__/g, '$1'); + + // 7. 斜体 *text* / _text_ + s = s.replace(/(^|[^*])\*([^*\n]+)\*/g, '$1$2'); + s = s.replace(/(^|[^_])_([^_\n]+)_/g, '$1$2'); + + // 8. 删除线 ~~text~~ + s = s.replace(/~~([^~]+)~~/g, '$1'); + + // 9. 还原行内代码 + s = s.replace(/\u0000(\d+)\u0000/g, (m, idx) => `${escapeHTML(codes[+idx])}`); + + // 10. 软换行 + s = s.replace(/\n/g, '
      '); + + return s; +}; + +/** + * 生成标题锚点 id(支持中文) + */ +const slugify = (text) => { + return String(text) + .toLowerCase() + .replace(/[^\w\u4e00-\u9fa5\s-]/g, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); +}; + +export { safeUrl, slugify }; +export default parseMarkdown; diff --git a/src/plugins.js b/src/plugins.js new file mode 100644 index 0000000..cac1e86 --- /dev/null +++ b/src/plugins.js @@ -0,0 +1,483 @@ +/** + * MetonaEditor Plugins - 插件系统 + * @module plugins + * @version 0.1.0 + * @description 插件管理器 + 预设插件(autoSave / exportTool / searchReplace) + * + * 插件约定: + * { + * name: string, + * description?: string, + * install(editor, options): void, // 安装时调用,editor 为 MarkdownEditor 实例 + * destroy(editor): void // 卸载时调用,清理事件与 DOM + * } + * install 的 this 指向插件对象本身(core.js 通过 merged.install(editor) 调用), + * 因此可在 this 上存放运行时状态(如定时器、DOM 引用)。 + */ + +import { t } from './i18n.js'; + +/** + * 插件管理器(独立于编辑器实例,用于全局注册与查询;编辑器实例级安装由 core.js 的 use() 负责) + */ +class PluginManager { + constructor() { + this.plugins = new Map(); + } + + register(name, plugin) { + if (this.plugins.has(name)) { + console.warn(`MeEditor: plugin "${name}" already registered`); + return this; + } + if (!plugin || typeof plugin !== 'object' || (!plugin.name && !name)) { + console.error(`MeEditor: invalid plugin "${name}"`); + return this; + } + this.plugins.set(name, { name, ...plugin, enabled: true }); + return this; + } + + unregister(name) { + this.plugins.delete(name); + return this; + } + + get(name) { return this.plugins.get(name) || null; } + has(name) { return this.plugins.has(name); } + getAll() { return Array.from(this.plugins.values()); } + getNames() { return Array.from(this.plugins.keys()); } + enable(name) { const p = this.plugins.get(name); if (p) p.enabled = true; return this; } + disable(name) { const p = this.plugins.get(name); if (p) p.enabled = false; return this; } + isEnabled(name){ const p = this.plugins.get(name); return p ? p.enabled : false; } + + destroy() { + this.plugins.clear(); + } +} + +const defaultPluginManager = new PluginManager(); + +// ============ 预设插件 ============ + +/** + * 自动保存插件 + * 将编辑器内容定时 / 失焦 / 保存时写入 localStorage,并提供草稿恢复与清除。 + * + * 配置: + * key: 存储 key,默认 'me-draft-' + editor.id + * delay: 防抖延迟(ms),默认 1000 + */ +const autoSavePlugin = { + name: 'autoSave', + description: '自动保存到 localStorage,支持草稿恢复', + key: null, + delay: 1000, + + _timer: null, + _onInput: null, + _onBlur: null, + _onSave: null, + + install(editor) { + if (!editor || typeof editor.getValue !== 'function') return; + const key = this.key || ('me-draft-' + (editor.id || '')); + + const save = () => { + if (this._timer) { clearTimeout(this._timer); this._timer = null; } + try { + const value = editor.getValue(); + localStorage.setItem(key, value); + if (typeof editor._emit === 'function') { + editor._emit('autosave', { key, value }); + } + } catch (e) { + console.warn('MeEditor autoSave: save failed', e); + } + }; + + this._save = save; + this._onInput = () => { + if (this._timer) clearTimeout(this._timer); + this._timer = setTimeout(save, this.delay); + }; + this._onBlur = save; + this._onSave = save; + + editor.on('change', this._onInput); + editor.on('blur', this._onBlur); + editor.on('save', this._onSave); + + // 暴露草稿 API + editor.restoreDraft = () => { + try { + const v = localStorage.getItem(key); + if (v != null && typeof editor.setValue === 'function') { + editor.setValue(v); + } + return v; + } catch (e) { return null; } + }; + editor.clearDraft = () => { + try { localStorage.removeItem(key); } catch (e) {} + return editor; + }; + editor.getDraftKey = () => key; + }, + + destroy(editor) { + if (this._timer) { clearTimeout(this._timer); this._timer = null; } + if (editor && typeof editor.off === 'function') { + if (this._onInput) editor.off('change', this._onInput); + if (this._onBlur) editor.off('blur', this._onBlur); + if (this._onSave) editor.off('save', this._onSave); + } + }, +}; + +/** + * 导出工具插件 + * 提供 .md / .html 文件下载。 + */ +const exportToolPlugin = { + name: 'exportTool', + description: '导出 Markdown / HTML 文件', + + install(editor) { + if (!editor || typeof editor.getValue !== 'function') return; + + const download = (filename, content, mime) => { + if (typeof document === 'undefined') return; + const blob = new Blob([content], { type: mime + ';charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + setTimeout(() => URL.revokeObjectURL(url), 0); + }; + + const stamp = () => { + const d = new Date(); + const pad = (n) => String(n).padStart(2, '0'); + return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}`; + }; + + editor.exportMarkdown = (filename) => { + download(filename || `metona-${stamp()}.md`, editor.getValue(), 'text/markdown'); + return editor; + }; + + editor.exportHTML = (filename, opts = {}) => { + const title = opts.title || 'Document'; + const css = opts.css || ''; + const body = typeof editor.getHTML === 'function' ? editor.getHTML() : ''; + const html = ` + + + + +${title} +${css ? `` : ''} + + +${body} + +`; + download(filename || `metona-${stamp()}.html`, html, 'text/html'); + return editor; + }; + }, + + destroy() {}, +}; + +/** + * 查找替换插件 + * Ctrl+F 唤出浮动搜索条,支持上一个 / 下一个 / 替换 / 全部替换。 + */ +const searchReplacePlugin = { + name: 'searchReplace', + description: '查找替换(Ctrl+F)', + + _onKeydown: null, + _panel: null, + _cleanup: null, + + install(editor) { + if (!editor || !editor.textarea) return; + if (typeof document === 'undefined') return; + + this._injectStyle(); + + this._onKeydown = (e) => { + const mod = e.ctrlKey || e.metaKey; + if (!mod) return; + const k = e.key.toLowerCase(); + if (k === 'f') { + e.preventDefault(); + this._open(editor); + } else if (k === 'h') { + e.preventDefault(); + this._open(editor, true); + } else if (k === 'escape' && this._panel) { + this._close(editor); + } + }; + editor.textarea.addEventListener('keydown', this._onKeydown); + }, + + _injectStyle() { + if (document.getElementById('me-search-style')) return; + const style = document.createElement('style'); + style.id = 'me-search-style'; + style.textContent = ` +.me-search{position:absolute;top:8px;right:12px;z-index:20;display:flex;flex-direction:column;gap:6px;padding:8px;background:var(--md-toolbar-bg, #f8f9fa);border:1px solid var(--md-border, rgba(0,0,0,0.1));border-radius:8px;box-shadow:0 8px 24px -8px rgba(0,0,0,0.2);font-size:13px;min-width:280px} +.me-search-row{display:flex;gap:4px;align-items:center} +.me-search input{flex:1;min-width:0;padding:4px 8px;border:1px solid var(--md-border, rgba(0,0,0,0.15));border-radius:4px;background:var(--md-textarea-bg, #fff);color:var(--md-text, #1f2937);font-size:13px} +.me-search input:focus{outline:none;border-color:var(--md-accent, #3b82f6)} +.me-search button{padding:4px 8px;border:1px solid var(--md-border, rgba(0,0,0,0.15));background:var(--md-bg, #fff);color:var(--md-text, #1f2937);border-radius:4px;cursor:pointer;font-size:12px;line-height:1} +.me-search button:hover{background:var(--md-accent, #3b82f6);color:#fff;border-color:var(--md-accent, #3b82f6)} +.me-search .me-search-count{color:var(--md-muted, #6b7280);font-size:12px;min-width:60px;text-align:center} +.me-search .me-search-close{padding:2px 6px} + `; + document.head.appendChild(style); + }, + + _open(editor, showReplace) { + if (this._panel) { + this._panel.dataset.replace = showReplace ? '1' : '0'; + this._updateReplaceVisible(); + const inp = this._panel.querySelector('.me-search-find'); + if (inp) { inp.focus(); inp.select(); } + return; + } + + const selected = editor.textarea.value.substring( + editor.textarea.selectionStart, + editor.textarea.selectionEnd + ); + + const panel = document.createElement('div'); + panel.className = 'me-search'; + panel.dataset.replace = showReplace ? '1' : '0'; + panel.innerHTML = ` +
      + + + + + +
      +
      + + + +
      + `; + editor.el.appendChild(panel); + this._panel = panel; + this._updateReplaceVisible(); + + const findInput = panel.querySelector('.me-search-find'); + const replaceInput = panel.querySelector('.me-search-replace'); + const countEl = panel.querySelector('.me-search-count'); + + const findAll = () => { + const text = editor.textarea.value; + const q = findInput.value; + if (!q) { countEl.textContent = ''; return []; } + const idxs = []; + let from = 0; + while (true) { + const idx = text.indexOf(q, from); + if (idx === -1) break; + idxs.push(idx); + from = idx + q.length; + } + countEl.textContent = idxs.length ? `${idxs.length}` : '0'; + return idxs; + }; + + const selectAt = (idx) => { + const q = findInput.value; + editor.textarea.focus(); + editor.textarea.setSelectionRange(idx, idx + q.length); + // 滚动到可见 + const lineHeight = parseFloat(getComputedStyle(editor.textarea).lineHeight) || 20; + const lineNum = editor.textarea.value.substring(0, idx).split('\n').length - 1; + editor.textarea.scrollTop = Math.max(0, lineNum * lineHeight - editor.textarea.clientHeight / 2); + }; + + let lastIdxs = []; + let cursor = -1; + + const findNext = () => { + lastIdxs = findAll(); + if (!lastIdxs.length) return; + const cur = editor.textarea.selectionEnd; + let next = lastIdxs.find((i) => i >= cur); + if (next == null) next = lastIdxs[0]; + cursor = lastIdxs.indexOf(next); + selectAt(next); + }; + + const findPrev = () => { + lastIdxs = findAll(); + if (!lastIdxs.length) return; + const cur = editor.textarea.selectionStart; + let prev = -1; + for (let i = lastIdxs.length - 1; i >= 0; i--) { + if (lastIdxs[i] < cur) { prev = lastIdxs[i]; break; } + } + if (prev === -1) prev = lastIdxs[lastIdxs.length - 1]; + cursor = lastIdxs.indexOf(prev); + selectAt(prev); + }; + + const replaceOne = () => { + const q = findInput.value; + const r = replaceInput.value; + if (!q) return; + const ta = editor.textarea; + const start = ta.selectionStart; + const end = ta.selectionEnd; + const cur = ta.value.substring(start, end); + if (cur === q) { + ta.value = ta.value.substring(0, start) + r + ta.value.substring(end); + ta.setSelectionRange(start, start + r.length); + editor._value = ta.value; + if (typeof editor._pushHistory === 'function') editor._pushHistory(); + if (typeof editor._render === 'function') editor._render(); + if (typeof editor._emit === 'function') editor._emit('change', editor._value); + } + findNext(); + }; + + const replaceAll = () => { + const q = findInput.value; + const r = replaceInput.value; + if (!q) return; + const ta = editor.textarea; + const before = ta.value; + const after = before.split(q).join(r); + if (before === after) return; + ta.value = after; + ta.setSelectionRange(0, 0); + editor._value = ta.value; + if (typeof editor._pushHistory === 'function') editor._pushHistory(); + if (typeof editor._render === 'function') editor._render(); + if (typeof editor._emit === 'function') editor._emit('change', editor._value); + findAll(); + }; + + findInput.addEventListener('input', () => { lastIdxs = findAll(); cursor = -1; }); + findInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { e.preventDefault(); e.shiftKey ? findPrev() : findNext(); } + if (e.key === 'Escape') { e.preventDefault(); this._close(editor); } + }); + replaceInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { e.preventDefault(); replaceOne(); } + if (e.key === 'Escape') { e.preventDefault(); this._close(editor); } + }); + panel.querySelector('.me-search-next').addEventListener('click', findNext); + panel.querySelector('.me-search-prev').addEventListener('click', findPrev); + panel.querySelector('.me-search-close').addEventListener('click', () => this._close(editor)); + panel.querySelector('.me-search-replace-one').addEventListener('click', replaceOne); + panel.querySelector('.me-search-replace-all').addEventListener('click', replaceAll); + + if (selected) findAll(); + findInput.focus(); + findInput.select(); + + this._cleanup = () => { + if (panel.parentNode) panel.parentNode.removeChild(panel); + }; + }, + + _updateReplaceVisible() { + if (!this._panel) return; + const show = this._panel.dataset.replace === '1'; + const row = this._panel.querySelector('.me-search-replace-row'); + if (row) row.style.display = show ? 'flex' : 'none'; + }, + + _close(editor) { + if (this._cleanup) { this._cleanup(); this._cleanup = null; } + this._panel = null; + if (editor && editor.textarea) editor.textarea.focus(); + }, + + destroy(editor) { + this._close(editor); + if (this._onKeydown && editor && editor.textarea) { + editor.textarea.removeEventListener('keydown', this._onKeydown); + } + this._onKeydown = null; + }, +}; + +/** + * 预设插件注册表 + */ +const presetPlugins = { + autoSave: autoSavePlugin, + exportTool: exportToolPlugin, + searchReplace: searchReplacePlugin, +}; + +/** + * 转义属性值(用于内联 HTML 属性) + */ +const escapeAttr = (s) => String(s == null ? '' : s) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); + +/** + * 插件工具 + */ +const pluginUtils = { + createManager() { return new PluginManager(); }, + manager: defaultPluginManager, + register(name, plugin) { return defaultPluginManager.register(name, plugin); }, + unregister(name) { return defaultPluginManager.unregister(name); }, + get(name) { return defaultPluginManager.get(name); }, + has(name) { return defaultPluginManager.has(name); }, + getAll() { return defaultPluginManager.getAll(); }, + getNames() { return defaultPluginManager.getNames(); }, + enable(name) { return defaultPluginManager.enable(name); }, + disable(name) { return defaultPluginManager.disable(name); }, + isEnabled(name) { return defaultPluginManager.isEnabled(name); }, + getPreset(name) { return presetPlugins[name] ? { ...presetPlugins[name] } : null; }, + getAllPresets() { return Object.keys(presetPlugins).reduce((acc, k) => { acc[k] = { ...presetPlugins[k] }; return acc; }, {}); }, + + createPlugin(config = {}) { + const result = { + name: config.name || 'custom', + description: config.description || '', + // 默认提供空函数,确保 editor.use(plugin) 时 destroy() 调用安全 + install: () => {}, + destroy: () => {}, + ...config, + }; + // 强制类型校验:用户传入非函数时回退为空函数,避免运行时报错 + if (typeof result.install !== 'function') result.install = () => {}; + if (typeof result.destroy !== 'function') result.destroy = () => {}; + return result; + }, + + validatePlugin(plugin) { + const errors = []; + if (!plugin || typeof plugin !== 'object') errors.push('plugin must be an object'); + if (plugin && !plugin.name) errors.push('plugin must have a name'); + if (plugin && plugin.install && typeof plugin.install !== 'function') errors.push('install must be a function'); + return { valid: errors.length === 0, errors }; + }, +}; + +export { presetPlugins, pluginUtils, defaultPluginManager, PluginManager }; +export default presetPlugins; diff --git a/src/styles.js b/src/styles.js new file mode 100644 index 0000000..d11c74f --- /dev/null +++ b/src/styles.js @@ -0,0 +1,381 @@ +/** + * MetonaEditor Styles - 编辑器样式 + * @module styles + * @version 0.1.0 + * @description 编辑器 UI 样式注入与管理 + */ + +import { THEMES } from './constants.js'; + +let styleElement = null; + +/** + * 生成 CSS 样式 + */ +const generateCSS = () => { + return ` + /* ============ CSS 变量默认值(被 themes.js 动态覆盖) ============ */ + :root { + --md-bg: #ffffff; + --md-text: #1f2937; + --md-border: rgba(0, 0, 0, 0.08); + --md-shadow: 0 10px 36px -10px rgba(0,0,0,0.18), 0 4px 14px -4px rgba(0,0,0,0.08); + --md-hover-shadow: 0 14px 48px -10px rgba(0,0,0,0.22), 0 6px 18px -4px rgba(0,0,0,0.10); + --md-toolbar-bg: rgba(248, 249, 250, 0.92); + --md-textarea-bg: #ffffff; + --md-preview-bg: #ffffff; + --md-code-bg: rgba(243, 244, 246, 1); + --md-code-text: #1f2937; + --md-accent: #3b82f6; + --md-muted: #6b7280; + --md-radius: 10px; + --md-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", "Hiragino Sans GB", sans-serif; + --md-mono: "SF Mono", "Cascadia Code", "Consolas", "Liberation Mono", "Courier New", monospace; + } + + /* ============ 容器 ============ */ + .me-wrapper { + display: flex; + flex-direction: column; + box-sizing: border-box; + border: 1px solid var(--md-border); + border-radius: var(--md-radius); + overflow: hidden; + background: var(--md-bg); + color: var(--md-text); + box-shadow: var(--md-shadow); + font-family: var(--md-font); + font-size: 14px; + line-height: 1.6; + position: relative; + transition: box-shadow 0.25s ease, border-color 0.25s ease; + width: 100%; + } + .me-wrapper:hover { box-shadow: var(--md-hover-shadow); } + .me-wrapper.me-disabled { opacity: 0.6; pointer-events: none; } + .me-wrapper * { box-sizing: border-box; } + .me-wrapper.me-fullscreen { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + width: 100vw; height: 100vh; + z-index: 9999; + border-radius: 0; + border: 0; + } + + /* ============ 工具栏 ============ */ + .me-toolbar { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 2px; + padding: 6px 8px; + background: var(--md-toolbar-bg); + border-bottom: 1px solid var(--md-border); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + min-height: 40px; + } + .me-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 30px; height: 30px; + padding: 0; + border: 0; + border-radius: 6px; + background: transparent; + color: var(--md-text); + cursor: pointer; + transition: background 0.15s ease, color 0.15s ease, transform 0.1s ease; + flex-shrink: 0; + } + .me-btn:hover { background: var(--md-code-bg); color: var(--md-accent); } + .me-btn:active { transform: scale(0.92); } + .me-btn.me-active { background: var(--md-accent); color: #fff; } + .me-btn:focus-visible { outline: 2px solid var(--md-accent); outline-offset: 1px; } + .me-btn svg { width: 17px; height: 17px; display: block; } + .me-btn span { font-size: 12px; font-weight: 500; } + + .me-toolbar-sep { + display: inline-block; + width: 1px; height: 20px; + background: var(--md-border); + margin: 0 4px; + flex-shrink: 0; + } + .me-toolbar-group { + display: inline-flex; + gap: 2px; + margin-left: auto; + padding-left: 6px; + border-left: 1px solid var(--md-border); + } + .me-toolbar-group .me-btn { width: auto; padding: 0 8px; } + .me-toolbar-group .me-btn span { font-size: 12px; } + + /* ============ 主体布局 ============ */ + .me-body { + display: flex; + flex: 1; + min-height: 0; + position: relative; + } + .me-editor-pane, .me-preview-pane { + flex: 1 1 50%; + min-width: 0; + overflow: hidden; + position: relative; + } + .me-divider { + flex: 0 0 1px; + background: var(--md-border); + cursor: col-resize; + position: relative; + } + .me-divider::after { + content: ''; + position: absolute; + top: 0; bottom: 0; left: -3px; right: -3px; + } + .me-divider:hover { background: var(--md-accent); } + + /* 编辑区 */ + .me-textarea { + width: 100%; + height: 100%; + padding: 16px 18px; + border: 0; + outline: 0; + resize: none; + background: var(--md-textarea-bg); + color: var(--md-text); + font-family: var(--md-mono); + font-size: 13.5px; + line-height: 1.7; + tab-size: 2; + -moz-tab-size: 2; + display: block; + white-space: pre-wrap; + word-wrap: break-word; + } + .me-textarea::placeholder { color: var(--md-muted); opacity: 0.7; } + .me-textarea:focus { outline: 0; } + + /* 预览区 */ + .me-preview-pane { overflow: auto; background: var(--md-preview-bg); } + .me-preview { + padding: 18px 22px; + max-width: 100%; + word-wrap: break-word; + overflow-wrap: break-word; + } + + /* 模式切换 */ + .me-body.me-mode-edit .me-preview-pane, + .me-body.me-mode-edit .me-divider { display: none; } + .me-body.me-mode-edit .me-editor-pane { flex: 1 1 100%; } + .me-body.me-mode-preview .me-editor-pane, + .me-body.me-mode-preview .me-divider { display: none; } + .me-body.me-mode-preview .me-preview-pane { flex: 1 1 100%; } + + /* ============ 预览区排版 ============ */ + .me-preview > :first-child { margin-top: 0; } + .me-preview > :last-child { margin-bottom: 0; } + .me-preview h1, .me-preview h2, .me-preview h3, + .me-preview h4, .me-preview h5, .me-preview h6 { + margin: 1.4em 0 0.6em; + font-weight: 650; + line-height: 1.3; + } + .me-preview h1 { font-size: 1.9em; padding-bottom: 0.3em; border-bottom: 1px solid var(--md-border); } + .me-preview h2 { font-size: 1.55em; padding-bottom: 0.3em; border-bottom: 1px solid var(--md-border); } + .me-preview h3 { font-size: 1.3em; } + .me-preview h4 { font-size: 1.12em; } + .me-preview h5 { font-size: 1em; } + .me-preview h6 { font-size: 0.9em; color: var(--md-muted); } + .me-preview p { margin: 0.7em 0; } + .me-preview a { color: var(--md-accent); text-decoration: none; } + .me-preview a:hover { text-decoration: underline; } + .me-preview strong { font-weight: 650; } + .me-preview em { font-style: italic; } + .me-preview del { text-decoration: line-through; opacity: 0.75; } + .me-preview ul, .me-preview ol { margin: 0.6em 0; padding-left: 1.6em; } + .me-preview li { margin: 0.25em 0; } + .me-preview li.me-task-item { list-style: none; margin-left: -1.4em; } + .me-preview li.me-task-item input { margin-right: 0.5em; vertical-align: middle; } + .me-preview blockquote { + margin: 0.8em 0; + padding: 0.4em 1em; + border-left: 3px solid var(--md-accent); + background: var(--md-code-bg); + border-radius: 0 6px 6px 0; + color: var(--md-text); + } + .me-preview blockquote > :first-child { margin-top: 0; } + .me-preview blockquote > :last-child { margin-bottom: 0; } + .me-preview hr { + border: 0; + height: 1px; + background: var(--md-border); + margin: 1.6em 0; + } + .me-preview code { + font-family: var(--md-mono); + font-size: 0.88em; + padding: 0.15em 0.4em; + background: var(--md-code-bg); + color: var(--md-code-text); + border-radius: 4px; + } + .me-preview pre { + margin: 0.9em 0; + padding: 14px 16px; + background: var(--md-code-bg); + border-radius: 8px; + overflow-x: auto; + border: 1px solid var(--md-border); + } + .me-preview pre code { + padding: 0; + background: transparent; + color: var(--md-code-text); + font-size: 0.9em; + line-height: 1.6; + border-radius: 0; + } + .me-preview img { + max-width: 100%; + height: auto; + border-radius: 6px; + vertical-align: middle; + } + .me-table-wrap { overflow-x: auto; margin: 0.9em 0; } + .me-preview table { + border-collapse: collapse; + width: 100%; + font-size: 0.93em; + display: block; + } + .me-preview th, .me-preview td { + border: 1px solid var(--md-border); + padding: 7px 12px; + text-align: left; + } + .me-preview th { + background: var(--md-code-bg); + font-weight: 600; + } + .me-preview tr:nth-child(even) td { background: var(--md-code-bg); } + .me-preview tr:nth-child(even) td { background-color: color-mix(in srgb, var(--md-code-bg) 40%, transparent); } + + /* ============ 状态栏 ============ */ + .me-statusbar { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 14px; + padding: 4px 12px; + border-top: 1px solid var(--md-border); + background: var(--md-toolbar-bg); + color: var(--md-muted); + font-size: 12px; + min-height: 26px; + } + .me-statusbar:empty { display: none; } + + /* ============ 响应式 ============ */ + @media (max-width: 768px) { + .me-body.me-mode-split { + flex-direction: column; + } + .me-body.me-mode-split .me-divider { + flex: 0 0 1px; + width: 100%; + height: 1px; + cursor: row-resize; + } + .me-body.me-mode-split .me-divider::after { + top: -3px; bottom: -3px; left: 0; right: 0; + } + .me-body.me-mode-split .me-editor-pane, + .me-body.me-mode-split .me-preview-pane { + flex: 1 1 50%; + width: 100%; + } + .me-toolbar { padding: 4px; } + .me-btn { width: 28px; height: 28px; } + .me-preview { padding: 14px; } + .me-textarea { padding: 12px; font-size: 13px; } + } + + /* ============ 无障碍 ============ */ + .me-wrapper:focus-within { + border-color: var(--md-accent); + } + .me-textarea:focus-visible { outline: 0; } + + /* ============ 动画 ============ */ + @media (prefers-reduced-motion: reduce) { + .me-wrapper, .me-btn, .me-textarea { transition: none !important; } + } + + /* ============ 全屏模式优化 ============ */ + .me-wrapper.me-fullscreen .me-preview, + .me-wrapper.me-fullscreen .me-textarea { font-size: 15px; } + `; +}; + +/** + * 注入样式(单例) + */ +export const injectStyles = () => { + if (typeof document === 'undefined') return; + if (styleElement && document.getElementById('metona-editor-styles')) return; + + const css = generateCSS(); + styleElement = document.createElement('style'); + styleElement.id = 'metona-editor-styles'; + styleElement.textContent = css.replace(/\s+/g, ' '); + document.head.appendChild(styleElement); +}; + +/** + * 更新样式 + */ +export const updateStyles = () => { + if (!styleElement) { injectStyles(); return; } + styleElement.textContent = generateCSS().replace(/\s+/g, ' '); +}; + +/** + * 移除样式 + */ +export const removeStyles = () => { + if (styleElement && styleElement.parentNode) { + styleElement.parentNode.removeChild(styleElement); + } + styleElement = null; +}; + +/** + * 获取系统主题 + */ +export const getSystemTheme = () => { + if (typeof window === 'undefined') return 'light'; + return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; +}; + +/** + * 监听系统主题变化 + */ +export const watchSystemTheme = (callback) => { + if (typeof window === 'undefined' || !window.matchMedia) return () => {}; + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const handler = (e) => callback(e.matches ? 'dark' : 'light'); + mediaQuery.addEventListener('change', handler); + return () => mediaQuery.removeEventListener('change', handler); +}; + +export { THEMES }; +export default { injectStyles, updateStyles, removeStyles, getSystemTheme, watchSystemTheme }; diff --git a/src/themes.js b/src/themes.js new file mode 100644 index 0000000..9ef02db --- /dev/null +++ b/src/themes.js @@ -0,0 +1,388 @@ +/** + * MetonaEditor Themes - 主题管理 + * @module themes + * @version 0.1.0 + * @description 主题系统、自定义主题和主题切换 + */ + +import { THEMES } from './constants.js'; +import { prefersDark } from './utils.js'; + +let currentTheme = 'auto'; +let themeListeners = new Set(); +let systemThemeMediaQuery = null; +let systemThemeListener = null; + +/** + * 获取系统主题 + */ +export const getSystemTheme = () => { + if (typeof window === 'undefined') return 'light'; + return prefersDark() ? 'dark' : 'light'; +}; + +/** + * 获取主题(别名) + */ +export const getTheme = (theme) => { + return resolveTheme(theme); +}; + +/** + * 解析主题 + */ +export const resolveTheme = (theme) => { + if (theme === 'auto') { + if (currentTheme && currentTheme !== 'auto') { + return currentTheme; + } + return getSystemTheme(); + } + return theme; +}; + +/** + * 获取主题配置 + */ +export const getThemeConfig = (theme) => { + const resolved = resolveTheme(theme); + return THEMES[resolved] || THEMES.light; +}; + +/** + * 应用主题 + */ +export const applyTheme = (theme) => { + currentTheme = theme; + const resolved = resolveTheme(theme); + + if (typeof document !== 'undefined') { + document.documentElement.setAttribute('data-md-theme', resolved); + document.documentElement.classList.remove('md-theme-light', 'md-theme-dark', 'md-theme-auto'); + document.documentElement.classList.add(`md-theme-${resolved}`); + + const config = getThemeConfig(theme); + setThemeVariables(config); + } + + notifyThemeListeners(theme, resolved); +}; + +/** + * 设置主题CSS变量 + */ +export const setThemeVariables = (config) => { + if (typeof document === 'undefined' || !config || typeof config !== 'object') return; + + const root = document.documentElement; + // 基础变量(保留用于全局与组件回退) + root.style.setProperty('--md-bg', config.bg); + root.style.setProperty('--md-text', config.text); + root.style.setProperty('--md-border', config.border); + root.style.setProperty('--md-shadow', config.shadow); + root.style.setProperty('--md-hover-shadow', config.hoverShadow); + // 编辑器专属变量 + root.style.setProperty('--md-toolbar-bg', config.toolbarBg || config.bg); + root.style.setProperty('--md-textarea-bg', config.textareaBg || config.bg); + root.style.setProperty('--md-preview-bg', config.previewBg || config.bg); + root.style.setProperty('--md-code-bg', config.codeBg || 'rgba(127,127,127,0.1)'); + root.style.setProperty('--md-code-text', config.codeText || config.text); + root.style.setProperty('--md-accent', config.accent || '#3b82f6'); + root.style.setProperty('--md-muted', config.muted || '#9ca3af'); + // 兼容旧字段 + if (config.progressBg) root.style.setProperty('--md-progress-bg', config.progressBg); + if (config.closeHoverBg) root.style.setProperty('--md-close-hover-bg', config.closeHoverBg); +}; + +/** + * 获取当前主题 + */ +export const getCurrentTheme = () => { + return currentTheme; +}; + +/** + * 获取解析后的主题 + */ +export const getResolvedTheme = () => { + return resolveTheme(currentTheme); +}; + +/** + * 切换主题 + */ +export const switchTheme = (theme) => { + applyTheme(theme); + saveTheme(theme); +}; + +/** + * 切换亮色/暗色主题 + */ +export const toggleTheme = () => { + const resolved = getResolvedTheme(); + const newTheme = resolved === 'dark' ? 'light' : 'dark'; + switchTheme(newTheme); +}; + +/** + * 重置为自动主题 + */ +export const resetToAuto = () => { + switchTheme('auto'); +}; + +/** + * 保存主题到本地存储 + */ +export const saveTheme = (theme) => { + if (typeof localStorage !== 'undefined') { + try { + localStorage.setItem('metona-editor-theme', theme); + } catch (e) { + console.warn('Failed to save theme:', e); + } + } +}; + +/** + * 从本地存储加载主题 + */ +export const loadTheme = () => { + if (typeof localStorage !== 'undefined') { + try { + return localStorage.getItem('metona-editor-theme') || 'auto'; + } catch (e) { + console.warn('Failed to load theme:', e); + } + } + return 'auto'; +}; + +/** + * 初始化主题系统 + */ +export const initTheme = () => { + const savedTheme = loadTheme(); + applyTheme(savedTheme); + watchSystemTheme(); +}; + +/** + * 监听系统主题变化 + */ +export const watchSystemTheme = () => { + if (typeof window === 'undefined' || !window.matchMedia) return; + + if (systemThemeMediaQuery && systemThemeListener) { + systemThemeMediaQuery.removeEventListener('change', systemThemeListener); + } + + systemThemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + systemThemeListener = (e) => { + if (currentTheme === 'auto') { + applyTheme('auto'); + } + }; + + systemThemeMediaQuery.addEventListener('change', systemThemeListener); +}; + +/** + * 停止监听系统主题变化 + */ +export const unwatchSystemTheme = () => { + if (systemThemeMediaQuery && systemThemeListener) { + systemThemeMediaQuery.removeEventListener('change', systemThemeListener); + systemThemeMediaQuery = null; + systemThemeListener = null; + } +}; + +/** + * 添加主题监听器 + */ +export const addThemeListener = (listener) => { + themeListeners.add(listener); + + return () => { + themeListeners.delete(listener); + }; +}; + +/** + * 移除主题监听器 + */ +export const removeThemeListener = (listener) => { + themeListeners.delete(listener); +}; + +/** + * 通知主题监听器 + */ +const notifyThemeListeners = (theme, resolved) => { + themeListeners.forEach((listener) => { + try { + listener(theme, resolved); + } catch (e) { + console.error('Theme listener error:', e); + } + }); +}; + +/** + * 清除所有主题监听器 + */ +export const clearThemeListeners = () => { + themeListeners.clear(); +}; + +/** + * 注册自定义主题 + */ +export const registerTheme = (name, config) => { + const base = THEMES.light; + THEMES[name] = { + bg: config.bg || base.bg, + text: config.text || base.text, + border: config.border || base.border, + shadow: config.shadow || base.shadow, + hoverShadow: config.hoverShadow || base.hoverShadow, + toolbarBg: config.toolbarBg || config.bg || base.toolbarBg, + textareaBg: config.textareaBg || config.bg || base.textareaBg, + previewBg: config.previewBg || config.bg || base.previewBg, + codeBg: config.codeBg || base.codeBg, + codeText: config.codeText || base.codeText, + accent: config.accent || base.accent, + muted: config.muted || base.muted, + // 兼容旧字段 + progressBg: config.progressBg, + closeHoverBg: config.closeHoverBg, + }; +}; + +/** + * 注销自定义主题 + */ +export const unregisterTheme = (name) => { + if (name === 'light' || name === 'dark' || name === 'auto') { + console.warn('Cannot unregister built-in theme:', name); + return; + } + + delete THEMES[name]; +}; + +/** + * 获取所有主题 + */ +export const getAllThemes = () => { + return { ...THEMES }; +}; + +/** + * 获取主题名称列表 + */ +export const getThemeNames = () => { + return Object.keys(THEMES); +}; + +/** + * 检查主题是否存在 + */ +export const hasTheme = (name) => { + return name in THEMES; +}; + +/** + * 主题工具 — 代理层 + */ +export const themeUtils = { + getSystemTheme, + resolveTheme, + getThemeConfig, + applyTheme, + getCurrentTheme, + getResolvedTheme, + switchTheme, + toggleTheme, + resetToAuto, + initTheme, + watchSystemTheme, + unwatchSystemTheme, + addThemeListener, + removeThemeListener, + clearThemeListeners, + registerTheme, + unregisterTheme, + getAllThemes, + getThemeNames, + hasTheme, + saveTheme, + loadTheme, +}; + +/** + * 预设主题 + */ +export const presetThemes = { + light: { + name: '浅色', + description: '明亮清晰的主题', + config: THEMES.light, + }, + dark: { + name: '深色', + description: '护眼舒适的暗色主题', + config: THEMES.dark, + }, + auto: { + name: '自动', + description: '跟随系统主题设置', + config: 'auto', + }, + warm: { + name: '暖色', + description: '温馨舒适的暖色主题', + config: THEMES.warm, + }, +}; + +// 注册预设主题 +Object.entries(presetThemes).forEach(([name, theme]) => { + if (name !== 'auto' && theme.config !== 'auto') { + THEMES[name] = theme.config; + } +}); + +/** + * 创建主题管理器 + */ +export const createThemeManager = () => { + return { + getSystemTheme, + resolveTheme, + getThemeConfig, + applyTheme, + getCurrentTheme, + getResolvedTheme, + switchTheme, + toggleTheme, + resetToAuto, + initTheme, + watchSystemTheme, + unwatchSystemTheme, + addThemeListener, + removeThemeListener, + clearThemeListeners, + registerTheme, + unregisterTheme, + getAllThemes, + getThemeNames, + hasTheme, + saveTheme, + loadTheme, + }; +}; + +export { themeUtils as default }; diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..60aa9af --- /dev/null +++ b/src/utils.js @@ -0,0 +1,138 @@ +/** + * MetonaEditor Utils - 工具函数 + * @module utils + * @version 0.1.0 + * @description 通用工具函数集合 + */ + +/** + * 生成唯一ID + * @returns {string} 唯一ID + */ +export const generateId = () => { + return 'me-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 8); +}; + +/** + * HTML转义 + * @param {string} s - 输入字符串 + * @returns {string} 转义后的字符串 + */ +export const escapeHTML = (s) => { + if (typeof document === 'undefined') { + return String(s == null ? '' : s) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + const div = document.createElement('div'); + div.textContent = String(s == null ? '' : s); + return div.innerHTML; +}; + +/** + * 检测暗色模式偏好 + * @returns {boolean} 是否偏好暗色模式 + */ +export const prefersDark = () => { + if (typeof window === 'undefined' || !window.matchMedia) return false; + return window.matchMedia('(prefers-color-scheme: dark)').matches; +}; + +/** + * 防抖函数 + * @param {Function} func - 要防抖的函数 + * @param {number} wait - 等待时间(毫秒) + * @param {boolean} immediate - 是否立即执行 + * @returns {Function} 防抖后的函数 + */ +export const debounce = (func, wait, immediate = false) => { + let timeout; + return function executedFunction(...args) { + const context = this; + const later = () => { + timeout = null; + if (!immediate) func.apply(context, args); + }; + const callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +}; + +/** + * 节流函数 + * @param {Function} func - 要节流的函数 + * @param {number} limit - 限制时间(毫秒) + * @returns {Function} 节流后的函数 + */ +export const throttle = (func, limit) => { + let inThrottle; + return function executedFunction(...args) { + const context = this; + if (!inThrottle) { + func.apply(context, args); + inThrottle = true; + setTimeout(() => inThrottle = false, limit); + } + }; +}; + +/** + * 深度合并对象 + * @param {Object} target - 目标对象 + * @param {Object} source - 源对象 + * @returns {Object} 合并后的对象 + */ +export const deepMerge = (target, source) => { + const output = { ...target }; + + Object.keys(source).forEach(key => { + if (source[key] instanceof Object && key in target && target[key] instanceof Object) { + output[key] = deepMerge(target[key], source[key]); + } else { + output[key] = source[key]; + } + }); + + return output; +}; + +/** + * 检查是否为浏览器环境 + * @returns {boolean} 是否为浏览器环境 + */ +export const isBrowser = () => { + return typeof window !== 'undefined' && typeof document !== 'undefined'; +}; + +/** + * 格式化文件大小 + * @param {number} bytes - 字节数 + * @param {number} decimals - 小数位数 + * @returns {string} 格式化后的字符串 + */ +export const formatFileSize = (bytes, decimals = 2) => { + if (bytes === 0) return '0 Bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; +}; + +/** + * 延迟执行 + * @param {number} ms - 毫秒数 + * @returns {Promise} Promise对象 + */ +export const sleep = (ms) => { + return new Promise(resolve => setTimeout(resolve, ms)); +}; diff --git a/tests/animations.test.js b/tests/animations.test.js new file mode 100644 index 0000000..3000ff4 --- /dev/null +++ b/tests/animations.test.js @@ -0,0 +1,146 @@ +/** + * animations.js 单元测试 + * 覆盖 animationUtils 全部 API + createAnimation 工厂 + */ + +import { animationUtils, createAnimation, animationPresets } from '../src/animations.js'; +import { ANIMATIONS } from '../src/constants.js'; + +describe('animationUtils - 默认动画注册', () => { + test('默认动画已注册到 animationMap', () => { + const names = animationUtils.getAnimationNames(); + // 与 constants.js 中 ANIMATIONS 的键一致 + Object.keys(ANIMATIONS).forEach((name) => { + expect(names).toContain(name); + }); + }); + + test('内置动画类型完整', () => { + const names = animationUtils.getAnimationNames(); + expect(names).toEqual(expect.arrayContaining([ + 'slide', 'fade', 'scale', 'bounce', 'flip', 'rotate', 'zoom', + ])); + }); +}); + +describe('animationUtils - register / unregister / get', () => { + test('register 注册自定义动画', () => { + animationUtils.register('custom', { + enter: { opacity: 0 }, + leave: { opacity: 0 }, + duration: 500, + easing: 'ease-in', + }); + const a = animationUtils.get('custom'); + expect(a).not.toBeNull(); + expect(a.name).toBe('custom'); + expect(a.duration).toBe(500); + expect(a.easing).toBe('ease-in'); + expect(a.enter).toEqual({ opacity: 0 }); + }); + + test('register 缺省字段使用默认值', () => { + animationUtils.register('minimal', {}); + const a = animationUtils.get('minimal'); + expect(a.enter).toEqual({}); + expect(a.leave).toEqual({}); + expect(a.duration).toBe(300); + expect(a.easing).toBe('cubic-bezier(0.4, 0, 0.2, 1)'); + }); + + test('unregister 移除动画', () => { + animationUtils.register('tmp', { duration: 100 }); + expect(animationUtils.get('tmp')).not.toBeNull(); + animationUtils.unregister('tmp'); + // 移除后回退到 ANIMATIONS,但 'tmp' 不在 ANIMATIONS 中,返回 null + expect(animationUtils.get('tmp')).toBeNull(); + }); + + test('get 不存在的动画返回 null', () => { + expect(animationUtils.get('non-existent-animation')).toBeNull(); + }); + + test('get 内置动画返回配置', () => { + const a = animationUtils.get('slide'); + expect(a).not.toBeNull(); + // 内置动画直接存储 ANIMATIONS.slide 配置(不含 name 字段,只有 register() 才添加) + expect(a.duration).toBe(ANIMATIONS.slide.duration); + expect(a.enter).toEqual(ANIMATIONS.slide.enter); + }); +}); + +describe('animationUtils - 查询 API', () => { + test('getAnimationNames 返回数组', () => { + const names = animationUtils.getAnimationNames(); + expect(Array.isArray(names)).toBe(true); + expect(names.length).toBeGreaterThan(0); + }); + + test('getActiveCount 始终返回 0(CSS 动画由浏览器管理)', () => { + expect(animationUtils.getActiveCount()).toBe(0); + }); + + test('cancelAll 不抛错(无操作)', () => { + expect(() => animationUtils.cancelAll()).not.toThrow(); + }); +}); + +describe('animationUtils - reset / destroy', () => { + test('reset 清除自定义动画并恢复内置', () => { + animationUtils.register('tmp1', {}); + animationUtils.register('tmp2', {}); + expect(animationUtils.getAnimationNames()).toContain('tmp1'); + + animationUtils.reset(); + + const names = animationUtils.getAnimationNames(); + expect(names).not.toContain('tmp1'); + expect(names).not.toContain('tmp2'); + // 内置动画仍存在 + expect(names).toContain('slide'); + expect(names).toContain('fade'); + }); + + test('destroy 清空所有动画', () => { + animationUtils.destroy(); + expect(animationUtils.getAnimationNames()).toEqual([]); + // destroy 后 reset 可恢复 + animationUtils.reset(); + expect(animationUtils.getAnimationNames().length).toBeGreaterThan(0); + }); +}); + +describe('createAnimation 工厂', () => { + test('返回完整动画对象', () => { + const a = createAnimation({ + enter: { transform: 'translateX(10px)' }, + leave: { transform: 'translateX(-10px)' }, + duration: 600, + easing: 'linear', + }); + expect(a.enter).toEqual({ transform: 'translateX(10px)' }); + expect(a.leave).toEqual({ transform: 'translateX(-10px)' }); + expect(a.duration).toBe(600); + expect(a.easing).toBe('linear'); + }); + + test('缺省字段使用默认值', () => { + const a = createAnimation({}); + expect(a.enter).toEqual({}); + expect(a.leave).toEqual({}); + expect(a.duration).toBe(300); + expect(a.easing).toBe('cubic-bezier(0.4, 0, 0.2, 1)'); + }); + + test('无参数也能创建', () => { + const a = createAnimation(); + expect(a).toBeDefined(); + expect(a.duration).toBe(300); + }); +}); + +describe('animationPresets', () => { + test('导出为对象', () => { + expect(typeof animationPresets).toBe('object'); + }); +}); diff --git a/tests/core.test.js b/tests/core.test.js new file mode 100644 index 0000000..880b215 --- /dev/null +++ b/tests/core.test.js @@ -0,0 +1,1227 @@ +/** + * core.js 单元测试 + * 覆盖 MarkdownEditor 类的构造、API、命令、历史栈、模式、事件、销毁 + */ + +import { MarkdownEditor } from '../src/core.js'; + +// jsdom 不提供 PointerEvent,用 MouseEvent 作为基类 polyfill +if (typeof window !== 'undefined' && typeof window.PointerEvent === 'undefined') { + window.PointerEvent = class PointerEvent extends MouseEvent { + constructor(type, init = {}) { + super(type, init); + this.pointerId = init.pointerId || 0; + this.pointerType = init.pointerType || 'mouse'; + this.isPrimary = init.isPrimary !== false; + } + }; +} + +describe('MarkdownEditor - 构造', () => { + let container; + + beforeEach(() => { + document.body.innerHTML = ''; + container = document.createElement('div'); + container.id = 'host'; + document.body.appendChild(container); + }); + + test('通过元素构造', () => { + const ed = new MarkdownEditor(container, { value: '# hi' }); + expect(ed.el).toBeInstanceOf(HTMLElement); + expect(ed.container).toBe(container); + expect(ed.getValue()).toBe('# hi'); + }); + + test('通过选择器构造', () => { + const ed = new MarkdownEditor('#host', { value: 'text' }); + expect(ed.el).toBeDefined(); + expect(ed.getValue()).toBe('text'); + }); + + test('容器不存在时标记为已销毁且不抛错', () => { + const ed = new MarkdownEditor('#not-exist', {}); + expect(ed.isDestroyed()).toBe(true); + }); + + test('生成唯一 id', () => { + const a = new MarkdownEditor(container, {}); + const b = new MarkdownEditor(document.createElement('div'), {}); + expect(a.id).toBeTruthy(); + expect(b.id).toBeTruthy(); + expect(a.id).not.toBe(b.id); + }); + + test('DOM 结构完整', () => { + const ed = new MarkdownEditor(container, {}); + expect(ed.el.className).toContain('me-wrapper'); + expect(ed.toolbarEl).toBeDefined(); + expect(ed.bodyEl).toBeDefined(); + expect(ed.textarea).toBeInstanceOf(HTMLTextAreaElement); + expect(ed.previewEl).toBeDefined(); + }); + + test('工具栏默认渲染按钮', () => { + const ed = new MarkdownEditor(container, {}); + const btns = ed.toolbarEl.querySelectorAll('.me-btn'); + expect(btns.length).toBeGreaterThan(10); + }); + + test('toolbar: false 隐藏工具栏按钮', () => { + const ed = new MarkdownEditor(container, { toolbar: false }); + const btns = ed.toolbarEl.querySelectorAll('.me-btn'); + expect(btns.length).toBe(0); + }); + + test('初始 value 写入 textarea', () => { + const ed = new MarkdownEditor(container, { value: '# Title' }); + expect(ed.textarea.value).toBe('# Title'); + }); + + test('wordCount 关闭时不创建状态栏', () => { + const ed = new MarkdownEditor(container, { wordCount: false }); + expect(ed.statusEl).toBeNull(); + }); + + test('wordCount 开启时创建状态栏', () => { + const ed = new MarkdownEditor(container, { wordCount: true }); + expect(ed.statusEl).not.toBeNull(); + }); +}); + +describe('MarkdownEditor - 内容 API', () => { + let ed; + beforeEach(() => { + document.body.innerHTML = ''; + ed = new MarkdownEditor(document.createElement('div'), { value: '' }); + }); + afterEach(() => ed.destroy()); + + test('getValue / setValue', () => { + ed.setValue('# hello'); + expect(ed.getValue()).toBe('# hello'); + expect(ed.textarea.value).toBe('# hello'); + }); + + test('setValue silent 不触发 change', () => { + let called = 0; + ed.on('change', () => called++); + ed.setValue('x', { silent: true }); + expect(called).toBe(0); + }); + + test('setValue 默认触发 change', () => { + let called = 0; + ed.on('change', () => called++); + ed.setValue('x'); + expect(called).toBeGreaterThan(0); + }); + + test('getHTML 返回渲染后 HTML', () => { + ed.setValue('# Title'); + const html = ed.getHTML(); + expect(html).toContain(' { + ed.setValue('abc'); + ed.textarea.selectionStart = ed.textarea.selectionEnd = 1; + ed.insert('X'); + expect(ed.getValue()).toBe('aXbc'); + }); + + test('wrap 包裹选区', () => { + ed.setValue('hello'); + ed.textarea.selectionStart = 0; + ed.textarea.selectionEnd = 5; + ed.wrap('**', '**'); + expect(ed.getValue()).toBe('**hello**'); + }); + + test('focus / blur 不抛错', () => { + expect(() => ed.focus()).not.toThrow(); + expect(() => ed.blur()).not.toThrow(); + }); + + test('enable / disable', () => { + ed.disable(); + expect(ed.isDisabled()).toBe(true); + expect(ed.el.className).toContain('me-disabled'); + ed.enable(); + expect(ed.isDisabled()).toBe(false); + }); +}); + +describe('MarkdownEditor - exec 命令', () => { + let ed; + beforeEach(() => { + document.body.innerHTML = ''; + ed = new MarkdownEditor(document.createElement('div'), { value: 'hello world' }); + }); + afterEach(() => ed.destroy()); + + function select(start, end) { + ed.textarea.selectionStart = start; + ed.textarea.selectionEnd = end === undefined ? start : end; + } + + test('bold 包裹选区', () => { + select(0, 5); + ed.exec('bold'); + expect(ed.getValue()).toBe('**hello** world'); + }); + + test('italic 包裹选区', () => { + select(0, 5); + ed.exec('italic'); + expect(ed.getValue()).toBe('*hello* world'); + }); + + test('strikethrough 包裹选区', () => { + select(0, 5); + ed.exec('strikethrough'); + expect(ed.getValue()).toBe('~~hello~~ world'); + }); + + test('code 包裹选区', () => { + select(0, 5); + ed.exec('code'); + expect(ed.getValue()).toBe('`hello` world'); + }); + + test('h1 切换行首前缀', () => { + select(0, 0); + ed.exec('h1'); + expect(ed.getValue()).toBe('# hello world'); + }); + + test('quote 切换行首前缀', () => { + select(0, 0); + ed.exec('quote'); + expect(ed.getValue()).toBe('> hello world'); + }); + + test('ul 切换行首前缀', () => { + select(0, 0); + ed.exec('ul'); + expect(ed.getValue()).toBe('- hello world'); + }); + + test('ol 切换行首前缀', () => { + select(0, 0); + ed.exec('ol'); + expect(ed.getValue()).toBe('1. hello world'); + }); + + test('重复执行同一前缀命令会取消前缀', () => { + select(0, 0); + ed.exec('h1'); + ed.exec('h1'); + expect(ed.getValue()).toBe('hello world'); + }); + + test('hr 插入分隔线', () => { + ed.setValue('a'); + ed.textarea.selectionStart = ed.textarea.selectionEnd = 1; + ed.exec('hr'); + expect(ed.getValue()).toContain('---'); + }); + + test('link 插入链接', () => { + ed.setValue('text'); + select(0, 4); + ed.exec('link'); + expect(ed.getValue()).toMatch(/\[text\]\(https?:\/\//); + }); + + test('image 插入图片', () => { + ed.setValue('alt'); + select(0, 3); + ed.exec('image'); + expect(ed.getValue()).toMatch(/!\[alt\]\(https?:\/\//); + }); + + test('table 插入表格', () => { + ed.setValue(''); + ed.exec('table'); + const v = ed.getValue(); + expect(v).toContain('|'); + expect(v).toContain('---'); + }); + + test('未知命令不抛错', () => { + expect(() => ed.exec('nonexistent')).not.toThrow(); + }); + + test('exec 返回 this 支持链式', () => { + expect(ed.exec('bold')).toBe(ed); + }); +}); + +describe('MarkdownEditor - 历史栈', () => { + let ed; + beforeEach(() => { + document.body.innerHTML = ''; + ed = new MarkdownEditor(document.createElement('div'), { value: '' }); + }); + afterEach(() => ed.destroy()); + + test('初始可撤销为 false', () => { + expect(ed.canUndo()).toBe(false); + }); + + test('setValue 后可撤销', () => { + ed.setValue('a'); + expect(ed.canUndo()).toBe(true); + }); + + test('undo 回退', () => { + ed.setValue('a'); + ed.setValue('b'); + ed.undo(); + expect(ed.getValue()).toBe('a'); + }); + + test('redo 前进', () => { + ed.setValue('a'); + ed.setValue('b'); + ed.undo(); + ed.redo(); + expect(ed.getValue()).toBe('b'); + }); + + test('无历史时 undo/redo 无副作用', () => { + expect(() => ed.undo()).not.toThrow(); + expect(() => ed.redo()).not.toThrow(); + }); + + test('undo/redo 返回 this', () => { + ed.setValue('a'); + expect(ed.undo()).toBe(ed); + expect(ed.redo()).toBe(ed); + }); +}); + +describe('MarkdownEditor - 模式与全屏', () => { + let ed; + beforeEach(() => { + document.body.innerHTML = ''; + ed = new MarkdownEditor(document.createElement('div'), { mode: 'split' }); + }); + afterEach(() => ed.destroy()); + + test('初始模式', () => { + expect(ed.getMode()).toBe('split'); + }); + + test('setMode 切换', () => { + ed.setMode('edit'); + expect(ed.getMode()).toBe('edit'); + expect(ed.bodyEl.className).toContain('me-mode-edit'); + }); + + test('setMode 切换到 preview', () => { + ed.setMode('preview'); + expect(ed.getMode()).toBe('preview'); + expect(ed.bodyEl.className).toContain('me-mode-preview'); + }); + + test('非法模式被忽略', () => { + ed.setMode('invalid'); + expect(ed.getMode()).toBe('split'); + }); + + test('toggleFullscreen 切换全屏状态', () => { + expect(ed.isFullscreen()).toBe(false); + ed.toggleFullscreen(); + expect(ed.isFullscreen()).toBe(true); + expect(ed.el.className).toContain('me-fullscreen'); + ed.toggleFullscreen(); + expect(ed.isFullscreen()).toBe(false); + }); + + test('exitFullscreen', () => { + ed.toggleFullscreen(); + ed.exitFullscreen(); + expect(ed.isFullscreen()).toBe(false); + }); + + test('setMode 触发 modeChange 事件', () => { + let mode = null; + ed.on('modeChange', (m) => { mode = m; }); + ed.setMode('edit'); + expect(mode).toBe('edit'); + }); +}); + +describe('MarkdownEditor - 统计与状态', () => { + let ed; + beforeEach(() => { + document.body.innerHTML = ''; + ed = new MarkdownEditor(document.createElement('div'), { value: '' }); + }); + afterEach(() => ed.destroy()); + + test('getStats 返回统计字段', () => { + ed.setValue('hello 世界'); + const stats = ed.getStats(); + expect(stats).toHaveProperty('characters'); + expect(stats).toHaveProperty('words'); + expect(stats).toHaveProperty('chineseChars'); + expect(stats).toHaveProperty('englishWords'); + expect(stats).toHaveProperty('lines'); + expect(stats).toHaveProperty('readingTime'); + expect(stats.chineseChars).toBe(2); + }); + + test('getStatus 返回状态字段', () => { + const status = ed.getStatus(); + expect(status).toHaveProperty('id'); + expect(status).toHaveProperty('mode'); + expect(status).toHaveProperty('theme'); + expect(status).toHaveProperty('locale'); + expect(status).toHaveProperty('fullscreen'); + expect(status).toHaveProperty('destroyed'); + expect(status.destroyed).toBe(false); + }); +}); + +describe('MarkdownEditor - 事件', () => { + let ed; + beforeEach(() => { + document.body.innerHTML = ''; + ed = new MarkdownEditor(document.createElement('div'), { value: '' }); + }); + afterEach(() => ed.destroy()); + + test('on 返回取消函数', () => { + const off = ed.on('change', () => {}); + expect(typeof off).toBe('function'); + }); + + test('off 取消监听', () => { + let called = 0; + const fn = () => called++; + ed.on('change', fn); + ed.setValue('a'); + const before = called; + ed.off('change', fn); + ed.setValue('b'); + expect(called).toBe(before); + }); + + test('静态 on/off 钩子', () => { + let triggered = 0; + const fn = () => { triggered++; }; + MarkdownEditor.on('afterCreate', fn); + const e = new MarkdownEditor(document.createElement('div'), {}); + expect(triggered).toBeGreaterThan(0); + MarkdownEditor.off('afterCreate', fn); + e.destroy(); + }); +}); + +describe('MarkdownEditor - 插件', () => { + let ed; + beforeEach(() => { + document.body.innerHTML = ''; + ed = new MarkdownEditor(document.createElement('div'), {}); + }); + afterEach(() => ed.destroy()); + + test('use 安装对象插件', () => { + let installed = false; + ed.use({ name: 'test', install: () => { installed = true; } }); + expect(installed).toBe(true); + expect(ed.getPlugins().length).toBe(1); + }); + + test('use 安装未知预设插件时 warn 且不崩溃', () => { + const spy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + ed.use('not-a-preset'); + expect(spy).toHaveBeenCalled(); + spy.mockRestore(); + }); + + test('addToolbarButton 追加按钮', () => { + ed.addToolbarButton({ action: 'custom', title: 'Custom' }); + const btn = ed.toolbarEl.querySelector('.me-btn-custom-custom'); + expect(btn).not.toBeNull(); + }); + + test('销毁时调用插件 destroy', () => { + let destroyed = false; + const ed2 = new MarkdownEditor(document.createElement('div'), {}); + ed2.use({ name: 't', install() {}, destroy() { destroyed = true; } }); + ed2.destroy(); + expect(destroyed).toBe(true); + }); +}); + +describe('MarkdownEditor - 销毁', () => { + test('destroy 移除 DOM 并标记', () => { + document.body.innerHTML = ''; + const ed = new MarkdownEditor(document.createElement('div'), {}); + const el = ed.el; + ed.destroy(); + expect(ed.isDestroyed()).toBe(true); + // el 已置空 + expect(ed.el).toBeNull(); + }); + + test('重复 destroy 不抛错', () => { + document.body.innerHTML = ''; + const ed = new MarkdownEditor(document.createElement('div'), {}); + ed.destroy(); + expect(() => ed.destroy()).not.toThrow(); + }); + + test('destroy 触发 destroy 事件', () => { + document.body.innerHTML = ''; + const ed = new MarkdownEditor(document.createElement('div'), {}); + let fired = false; + ed.on('destroy', () => { fired = true; }); + ed.destroy(); + expect(fired).toBe(true); + }); +}); + +// ============ 补充分支测试 ============ + +describe('MarkdownEditor - 构造边界', () => { + test('非浏览器环境直接返回(_destroyed=true)', () => { + // jsdom 是浏览器环境,此分支需通过模拟无法直接覆盖 + // 这里只验证 isBrowser() 返回 true 时正常路径 + const ed = new MarkdownEditor(document.createElement('div'), {}); + expect(ed.isDestroyed()).toBe(false); + ed.destroy(); + }); + + test('options.style 对象会被克隆', () => { + const style = { color: 'red' }; + const ed = new MarkdownEditor(document.createElement('div'), { style }); + expect(ed.config.style).not.toBe(style); + expect(ed.config.style.color).toBe('red'); + ed.destroy(); + }); + + test('非法 mode 回退到 split', () => { + const ed = new MarkdownEditor(document.createElement('div'), { mode: 'invalid-mode' }); + expect(ed.getMode()).toBe('split'); + ed.destroy(); + }); + + test('config.plugins 数组会被安装', () => { + const plugin = { + name: 'test-plugin', + install: jest.fn(), + destroy: jest.fn(), + }; + const ed = new MarkdownEditor(document.createElement('div'), { plugins: [plugin] }); + expect(plugin.install).toHaveBeenCalledWith(ed); + ed.destroy(); + expect(plugin.destroy).toHaveBeenCalledWith(ed); + }); + + test('autofocus 自动聚焦', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { autofocus: true }); + // jsdom 中聚焦需要元素挂载到 document + expect(ed.textarea).toBeDefined(); + ed.destroy(); + }); + + test('onCreate 回调被调用', () => { + const onCreate = jest.fn(); + const ed = new MarkdownEditor(document.createElement('div'), { onCreate }); + expect(onCreate).toHaveBeenCalledWith(ed); + ed.destroy(); + }); + + test('onCreate 抛错被捕获', () => { + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const ed = new MarkdownEditor(document.createElement('div'), { + onCreate: () => { throw new Error('boom'); }, + }); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('自定义 render 函数覆盖内置解析器', () => { + const customRender = jest.fn(() => '

      custom

      '); + const ed = new MarkdownEditor(document.createElement('div'), { + value: '# test', + render: customRender, + }); + expect(customRender).toHaveBeenCalled(); + expect(ed.getHTML()).toBe('

      custom

      '); + ed.destroy(); + }); + + test('自定义 highlight 函数被调用', () => { + const highlight = jest.fn(() => 'code'); + const ed = new MarkdownEditor(document.createElement('div'), { + value: '```js\nvar x = 1\n```', + highlight, + }); + expect(highlight).toHaveBeenCalledWith('var x = 1', 'js'); + ed.destroy(); + }); + + test('自定义 sanitize 净化 HTML', () => { + const ed = new MarkdownEditor(document.createElement('div'), { + value: 'hello', + sanitize: (html) => '
      sanitized
      ', + }); + expect(ed.getHTML()).toBe('
      sanitized
      '); + ed.destroy(); + }); +}); + +describe('MarkdownEditor - 键盘快捷键', () => { + let ed; + beforeEach(() => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + ed = new MarkdownEditor(c, { value: 'hello world' }); + }); + afterEach(() => ed.destroy()); + + function key(key, opts = {}) { + const ev = new KeyboardEvent('keydown', { + key, + ctrlKey: !!opts.ctrl, + shiftKey: !!opts.shift, + bubbles: true, + cancelable: true, + }); + ed.textarea.dispatchEvent(ev); + return ev; + } + + test('Tab 插入缩进(无选区,默认 tabSize=2)', () => { + ed.setValue('hello world'); + // 光标放在位置 5(即 'hello' 之后、原空格之前) + ed.textarea.setSelectionRange(5, 5); + key('Tab'); + // 插入 2 空格:'hello' + ' ' + ' world' = 'hello world'(含原空格共 3 空格) + expect(ed.getValue()).toBe('hello world'); + }); + + test('Tab 在行尾插入缩进', () => { + ed.setValue('hello'); + ed.textarea.setSelectionRange(5, 5); + key('Tab'); + expect(ed.getValue()).toBe('hello '); + }); + + test('Shift+Tab 反缩进(有选区)', () => { + ed.setValue(' line1\n line2'); + ed.textarea.setSelectionRange(0, 14); + key('Tab', { shift: true }); + // 反缩进会移除每行行首的空格 + expect(ed.getValue()).toBe('line1\nline2'); + }); + + test('Tab 有选区时整块缩进', () => { + ed.setValue('line1\nline2'); + ed.textarea.setSelectionRange(0, 11); + key('Tab'); + expect(ed.getValue()).toBe(' line1\n line2'); + }); + + test('Ctrl+B 触发 bold', () => { + ed.textarea.setSelectionRange(0, 5); + key('b', { ctrl: true }); + expect(ed.getValue()).toBe('**hello** world'); + }); + + test('Ctrl+I 触发 italic', () => { + ed.textarea.setSelectionRange(0, 5); + key('i', { ctrl: true }); + expect(ed.getValue()).toBe('*hello* world'); + }); + + test('Ctrl+E 触发 code', () => { + ed.textarea.setSelectionRange(0, 5); + key('e', { ctrl: true }); + expect(ed.getValue()).toBe('`hello` world'); + }); + + test('Ctrl+K 触发 link', () => { + ed.textarea.setSelectionRange(0, 5); + key('k', { ctrl: true }); + // link 使用 prompt,jsdom 返回 null,但不应抛错 + expect(ed.getValue()).toBeDefined(); + }); + + test('Ctrl+U 触发 underline', () => { + ed.textarea.setSelectionRange(0, 5); + key('u', { ctrl: true }); + expect(ed.getValue()).toBe('hello world'); + }); + + test('Ctrl+1 触发 h1', () => { + ed.textarea.setSelectionRange(0, 0); + key('1', { ctrl: true }); + expect(ed.getValue()).toBe('# hello world'); + }); + + test('Ctrl+2 触发 h2', () => { + ed.textarea.setSelectionRange(0, 0); + key('2', { ctrl: true }); + expect(ed.getValue()).toBe('## hello world'); + }); + + test('Ctrl+3 触发 h3', () => { + ed.textarea.setSelectionRange(0, 0); + key('3', { ctrl: true }); + expect(ed.getValue()).toBe('### hello world'); + }); + + test('Ctrl+Q 触发 quote', () => { + ed.textarea.setSelectionRange(0, 0); + key('q', { ctrl: true }); + expect(ed.getValue()).toBe('> hello world'); + }); + + test('Ctrl+Z 撤销', () => { + const initial = ed.getValue(); + ed.exec('bold'); + key('z', { ctrl: true }); + expect(ed.getValue()).toBe(initial); + }); + + test('Ctrl+Shift+Z 重做', () => { + const initial = ed.getValue(); + ed.exec('bold'); + key('z', { ctrl: true }); // undo + key('z', { ctrl: true, shift: true }); // redo + expect(ed.getValue()).not.toBe(initial); + }); + + test('Ctrl+Y 重做', () => { + const initial = ed.getValue(); + ed.exec('bold'); + key('z', { ctrl: true }); // undo + key('y', { ctrl: true }); // redo + expect(ed.getValue()).not.toBe(initial); + }); + + test('Ctrl+S 触发 save 事件', () => { + const handler = jest.fn(); + ed.on('save', handler); + key('s', { ctrl: true }); + expect(handler).toHaveBeenCalled(); + }); + + test('Ctrl+S 调用 onSave 回调', () => { + const onSave = jest.fn(); + ed.config.onSave = onSave; + key('s', { ctrl: true }); + expect(onSave).toHaveBeenCalled(); + }); + + test('无修饰键的按键不触发快捷键', () => { + const initial = ed.getValue(); + key('b'); + expect(ed.getValue()).toBe(initial); + }); +}); + +describe('MarkdownEditor - 同步滚动', () => { + test('分屏模式下 textarea 滚动联动 preview', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const longText = Array.from({ length: 100 }, (_, i) => `line ${i}`).join('\n'); + const ed = new MarkdownEditor(c, { value: longText, mode: 'split', syncScroll: true }); + ed.textarea.scrollTop = 100; + ed.textarea.dispatchEvent(new Event('scroll')); + // preview 应被同步滚动(scrollTop > 0 或为 0,取决于尺寸) + expect(typeof ed.previewPane.scrollTop).toBe('number'); + ed.destroy(); + }); + + test('syncScroll 关闭时不联动', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { + value: 'line1\nline2\nline3', + syncScroll: false, + }); + const before = ed.previewPane.scrollTop; + ed.textarea.scrollTop = 50; + ed.textarea.dispatchEvent(new Event('scroll')); + expect(ed.previewPane.scrollTop).toBe(before); + ed.destroy(); + }); + + test('非 split 模式不联动', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { value: 'test', mode: 'edit' }); + const before = ed.previewPane ? ed.previewPane.scrollTop : 0; + ed.textarea.scrollTop = 50; + ed.textarea.dispatchEvent(new Event('scroll')); + ed.destroy(); + }); +}); + +describe('MarkdownEditor - 分隔条拖拽', () => { + test('pointerdown 触发拖拽(split 模式)', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { value: 'test', mode: 'split' }); + // jsdom 中 getBoundingClientRect 返回全 0,但不应抛错 + expect(() => { + ed.dividerEl.dispatchEvent(new PointerEvent('pointerdown', { clientX: 200, bubbles: true })); + window.dispatchEvent(new PointerEvent('pointermove', { clientX: 250 })); + window.dispatchEvent(new PointerEvent('pointerup')); + }).not.toThrow(); + expect(ed.isDestroyed()).toBe(false); + ed.destroy(); + }); + + test('非 split 模式不触发拖拽', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { value: 'test', mode: 'edit' }); + expect(() => { + ed.dividerEl.dispatchEvent(new PointerEvent('pointerdown', { clientX: 200, bubbles: true })); + }).not.toThrow(); + ed.destroy(); + }); +}); + +describe('MarkdownEditor - 回调异常隔离', () => { + test('onChange 抛错不影响后续 input', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { + value: 'init', + onChange: () => { throw new Error('boom'); }, + }); + expect(() => { + ed.textarea.value = 'new'; + ed.textarea.dispatchEvent(new Event('input', { bubbles: true })); + }).not.toThrow(); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('onInput 抛错被捕获', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { + value: 'init', + onInput: () => { throw new Error('boom'); }, + }); + ed.textarea.value = 'new'; + ed.textarea.dispatchEvent(new Event('input', { bubbles: true })); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('onFocus 抛错被捕获', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { + onFocus: () => { throw new Error('boom'); }, + }); + ed.textarea.dispatchEvent(new Event('focus', { bubbles: true })); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('onBlur 抛错被捕获', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { + onBlur: () => { throw new Error('boom'); }, + }); + ed.textarea.dispatchEvent(new Event('blur', { bubbles: true })); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('onModeChange 抛错被捕获', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { + onModeChange: () => { throw new Error('boom'); }, + }); + ed.setMode('edit'); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('onFullscreen 抛错被捕获', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { + onFullscreen: () => { throw new Error('boom'); }, + }); + ed.toggleFullscreen(); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('onDestroy 抛错被捕获', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { + onDestroy: () => { throw new Error('boom'); }, + }); + ed.destroy(); + expect(spy).toHaveBeenCalled(); + spy.mockRestore(); + }); +}); + +describe('MarkdownEditor - 插件边界', () => { + test('use 字符串引用未知预设插件 warn', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, {}); + ed.use('non-existent-preset'); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('use 无效插件对象 warn', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, {}); + ed.use(null); + ed.use(123); + ed.use({ name: 'no-install' }); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('use install 抛错被捕获', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, {}); + const badPlugin = { + name: 'bad', + install: () => { throw new Error('install boom'); }, + }; + ed.use(badPlugin); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('addToolbarButton 无 action 直接返回', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, {}); + const before = ed.toolbarEl.querySelectorAll('.me-btn').length; + ed.addToolbarButton(null); + ed.addToolbarButton({ title: 'no-action' }); + expect(ed.toolbarEl.querySelectorAll('.me-btn').length).toBe(before); + ed.destroy(); + }); + + test('addToolbarButton 使用 onClick 回调', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, {}); + const onClick = jest.fn(); + ed.addToolbarButton({ + action: 'custom', + title: 'Custom', + icon: '', + onClick, + }); + const btn = ed.toolbarEl.querySelector('.me-btn-custom-custom'); + expect(btn).not.toBeNull(); + btn.click(); + expect(onClick).toHaveBeenCalledWith(ed); + ed.destroy(); + }); + + test('addToolbarButton 使用 text 文本', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, {}); + ed.addToolbarButton({ + action: 'txt', + title: 'Text Button', + text: 'T', + }); + const btn = ed.toolbarEl.querySelector('.me-btn-custom-txt'); + expect(btn).not.toBeNull(); + expect(btn.innerHTML).toContain('>T<'); + ed.destroy(); + }); +}); + +describe('MarkdownEditor - 工具栏点击', () => { + test('点击空白区域不抛错', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, {}); + expect(() => { + ed.toolbarEl.click(); + }).not.toThrow(); + ed.destroy(); + }); + + test('点击 mode 按钮切换模式', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { mode: 'split' }); + const editBtn = ed.toolbarEl.querySelector('.me-btn[data-mode="edit"]'); + editBtn.click(); + expect(ed.getMode()).toBe('edit'); + ed.destroy(); + }); + + test('点击 action 按钮执行命令', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { value: 'test' }); + const boldBtn = ed.toolbarEl.querySelector('.me-btn[data-action="bold"]'); + ed.textarea.setSelectionRange(0, 4); + boldBtn.click(); + expect(ed.getValue()).toBe('**test**'); + ed.destroy(); + }); +}); + +describe('MarkdownEditor - 历史栈合并', () => { + test('连续输入触发防抖合并', (done) => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { value: '' }); + // 连续快速输入 + ed.textarea.value = 'a'; + ed.textarea.dispatchEvent(new Event('input', { bubbles: true })); + ed.textarea.value = 'ab'; + ed.textarea.dispatchEvent(new Event('input', { bubbles: true })); + ed.textarea.value = 'abc'; + ed.textarea.dispatchEvent(new Event('input', { bubbles: true })); + // 等待防抖结束 + setTimeout(() => { + const initialLen = ed._history.length; + // 防抖合并后历史栈不应为 3 + expect(ed._history.length).toBeLessThanOrEqual(initialLen + 1); + ed.destroy(); + done(); + }, 500); + }); + + test('历史栈超过上限自动裁剪', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { value: '', historyLimit: 3 }); + for (let i = 0; i < 10; i++) { + ed.setValue('v' + i, { silent: false }); + } + expect(ed._history.length).toBeLessThanOrEqual(3); + ed.destroy(); + }); +}); + +describe('MarkdownEditor - 零散分支补全', () => { + test('className 配置项被应用到 wrapper', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { className: 'my-cls another' }); + expect(ed.el.className).toContain('my-cls'); + expect(ed.el.className).toContain('another'); + ed.destroy(); + }); + + test('render 抛错时显示错误信息', () => { + document.body.innerHTML = ''; + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { + value: '# test', + render: () => { throw new Error('render boom'); }, + }); + const html = ed.previewEl.innerHTML; + expect(html).toContain('渲染失败'); + expect(html).toContain('render boom'); + expect(spy).toHaveBeenCalled(); + ed.destroy(); + spy.mockRestore(); + }); + + test('canRedo 返回正确值', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { value: '' }); + expect(ed.canRedo()).toBe(false); + ed.setValue('a'); + ed.setValue('b'); + ed.undo(); + expect(ed.canRedo()).toBe(true); + ed.redo(); + expect(ed.canRedo()).toBe(false); + ed.destroy(); + }); + + test('undo/redo 触发 onChange 回调', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const onChange = jest.fn(); + const ed = new MarkdownEditor(c, { value: '', onChange }); + onChange.mockClear(); + ed.setValue('a'); + ed.setValue('b'); + onChange.mockClear(); + ed.undo(); + expect(onChange).toHaveBeenCalled(); + onChange.mockClear(); + ed.redo(); + expect(onChange).toHaveBeenCalled(); + ed.destroy(); + }); + + test('exec("indent")/exec("outdent") 等价于 Tab/Shift+Tab', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { value: 'hello' }); + ed.textarea.selectionStart = ed.textarea.selectionEnd = 0; + ed.exec('indent'); + expect(ed.getValue()).toBe(' hello'); + ed.textarea.selectionStart = 0; + ed.textarea.selectionEnd = 7; + ed.exec('outdent'); + expect(ed.getValue()).toBe('hello'); + ed.destroy(); + }); + + test('exec("undo")/exec("redo") 等价于 undo()/redo()', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { value: '' }); + ed.setValue('a'); + ed.setValue('b'); + ed.exec('undo'); + expect(ed.getValue()).toBe('a'); + ed.exec('redo'); + expect(ed.getValue()).toBe('b'); + ed.destroy(); + }); + + test('exec("edit"/"split"/"preview") 切换模式', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { mode: 'split' }); + ed.exec('edit'); + expect(ed.getMode()).toBe('edit'); + ed.exec('split'); + expect(ed.getMode()).toBe('split'); + ed.exec('preview'); + expect(ed.getMode()).toBe('preview'); + ed.destroy(); + }); + + test('exec("fullscreen") 切换全屏', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, {}); + expect(ed._fullscreen).toBe(false); + ed.exec('fullscreen'); + expect(ed._fullscreen).toBe(true); + ed.exec('fullscreen'); + expect(ed._fullscreen).toBe(false); + ed.destroy(); + }); + + test('_toggleLinePrefix 替换已存在的不同前缀', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, { value: '- hello' }); + ed.textarea.selectionStart = ed.textarea.selectionEnd = 0; + ed.exec('h1'); + // 原本行首是 "- ",执行 h1 后应替换为 "# " + expect(ed.getValue()).toBe('# hello'); + ed.destroy(); + }); + + test('setValue 触发 onChange 回调', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const onChange = jest.fn(); + const ed = new MarkdownEditor(c, { value: '', onChange }); + onChange.mockClear(); + ed.setValue('new'); + expect(onChange).toHaveBeenCalledWith('new', ed); + ed.destroy(); + }); + + test('getStatus 包含 plugins 字段', () => { + document.body.innerHTML = ''; + const c = document.createElement('div'); + document.body.appendChild(c); + const ed = new MarkdownEditor(c, {}); + ed.use({ name: 'demo-plugin', install: () => {} }); + const s = ed.getStatus(); + expect(Array.isArray(s.plugins)).toBe(true); + expect(s.plugins).toContain('demo-plugin'); + ed.destroy(); + }); +}); diff --git a/tests/i18n.test.js b/tests/i18n.test.js new file mode 100644 index 0000000..d03e4b4 --- /dev/null +++ b/tests/i18n.test.js @@ -0,0 +1,412 @@ +/** + * i18n.js 单元测试 + * 覆盖国际化系统:翻译 / 语言切换 / 插值 / 格式化 / 监听 / 持久化 + * + * 注意:i18n.js 使用模块级状态(currentLocale / localeListeners / fallbackLocale), + * 测试间需要重置状态以避免相互影响。 + */ + +import { + i18nUtils, + t, + getCurrentLocale, + setCurrentLocale, + switchLocale, + hasTranslation, + getTranslations, + addTranslations, + getSupportedLocales, + isLocaleSupported, + getLocaleName, + getLocaleDirection, + formatNumber, + formatCurrency, + formatDate, + addLocaleListener, + removeLocaleListener, + clearLocaleListeners, + saveLocale, + loadLocale, + getDefaultLocale, + initI18n, + presetLocales, + createI18nManager, +} from '../src/i18n.js'; +import { LOCALES } from '../src/constants.js'; + +// 确保 localStorage 存在(jsdom 兼容) +if (typeof global.localStorage === 'undefined') { + const store = {}; + global.localStorage = { + getItem: (k) => (k in store ? store[k] : null), + setItem: (k, v) => { store[k] = String(v); }, + removeItem: (k) => { delete store[k]; }, + clear: () => { Object.keys(store).forEach((k) => delete store[k]); }, + }; +} + +describe('当前语言', () => { + beforeEach(() => { + clearLocaleListeners(); + setCurrentLocale('zh-CN'); + localStorage.clear(); + }); + + test('getCurrentLocale 默认 zh-CN', () => { + expect(getCurrentLocale()).toBe('zh-CN'); + }); + + test('setCurrentLocale 切换语言', () => { + setCurrentLocale('en-US'); + expect(getCurrentLocale()).toBe('en-US'); + }); + + test('setCurrentLocale 未知语言回退到 fallback', () => { + const spy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + setCurrentLocale('fr-FR'); + expect(spy).toHaveBeenCalled(); + expect(getCurrentLocale()).toBe('zh-CN'); + spy.mockRestore(); + }); + + test('switchLocale 是 setCurrentLocale 的别名', () => { + switchLocale('en-US'); + expect(getCurrentLocale()).toBe('en-US'); + }); +}); + +describe('翻译函数 t', () => { + beforeEach(() => { + setCurrentLocale('zh-CN'); + }); + + test('返回当前语言的翻译', () => { + expect(t('bold')).toBe('粗体'); + expect(t('italic')).toBe('斜体'); + }); + + test('切换语言后返回新语言翻译', () => { + setCurrentLocale('en-US'); + expect(t('bold')).toBe('Bold'); + expect(t('italic')).toBe('Italic'); + }); + + test('未知 key 返回 key 本身', () => { + const spy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + expect(t('non-existent-key')).toBe('non-existent-key'); + spy.mockRestore(); + }); + + test('插值替换占位符', () => { + // 临时添加带占位符的翻译 + addTranslations('zh-CN', { greeting: '你好,{name}!' }); + expect(t('greeting', { name: '爸爸' })).toBe('你好,爸爸!'); + }); + + test('插值未提供占位符值时保留原占位符', () => { + addTranslations('zh-CN', { greeting: '你好,{name}!' }); + expect(t('greeting', {})).toBe('你好,{name}!'); + }); + + test('fallback 语言提供翻译', () => { + // 临时切换到不存在的语言会回退;这里用 en-US 但 key 只在 zh-CN 中 + setCurrentLocale('en-US'); + // en-US 中也有 bold,所以构造一个只在 zh-CN 中的场景较难, + // 改为直接验证:en-US 切换后 t('bold') 来自 en-US 而非 zh-CN + expect(t('bold')).toBe('Bold'); + }); +}); + +describe('翻译查询', () => { + test('hasTranslation 已知 key 返回 true', () => { + setCurrentLocale('zh-CN'); + expect(hasTranslation('bold')).toBe(true); + expect(hasTranslation('italic')).toBe(true); + }); + + test('hasTranslation 未知 key 返回 false', () => { + expect(hasTranslation('non-existent')).toBe(false); + }); + + test('getTranslations 返回指定语言的翻译对象', () => { + const tr = getTranslations('zh-CN'); + expect(tr.bold).toBe('粗体'); + expect(tr.italic).toBe('斜体'); + }); + + test('getTranslations 未知语言返回空对象', () => { + expect(getTranslations('fr-FR')).toEqual({}); + }); +}); + +describe('添加翻译', () => { + beforeEach(() => { + setCurrentLocale('zh-CN'); + }); + + test('addTranslations 添加新语言', () => { + addTranslations('ja', { bold: '太字' }); + expect(LOCALES.ja).toBeDefined(); + expect(LOCALES.ja.bold).toBe('太字'); + setCurrentLocale('ja'); + expect(t('bold')).toBe('太字'); + // 清理 + delete LOCALES.ja; + }); + + test('addTranslations 深度合并到已有语言', () => { + addTranslations('zh-CN', { customKey: '自定义值' }); + expect(LOCALES['zh-CN'].customKey).toBe('自定义值'); + // 原有 key 不受影响 + expect(LOCALES['zh-CN'].bold).toBe('粗体'); + delete LOCALES['zh-CN'].customKey; + }); +}); + +describe('语言支持查询', () => { + test('getSupportedLocales 返回内置语言', () => { + const locales = getSupportedLocales(); + expect(locales).toContain('zh-CN'); + expect(locales).toContain('en-US'); + }); + + test('isLocaleSupported 已知语言返回 true', () => { + expect(isLocaleSupported('zh-CN')).toBe(true); + expect(isLocaleSupported('en-US')).toBe(true); + }); + + test('isLocaleSupported 未知语言返回 false', () => { + expect(isLocaleSupported('fr-FR')).toBe(false); + expect(isLocaleSupported('')).toBe(false); + }); +}); + +describe('语言元信息', () => { + test('getLocaleName 返回语言显示名', () => { + expect(getLocaleName('zh-CN')).toBe('简体中文'); + expect(getLocaleName('en-US')).toBe('English (US)'); + expect(getLocaleName('ja')).toBe('日本語'); + }); + + test('getLocaleName 未知语言返回语言代码本身', () => { + expect(getLocaleName('unknown')).toBe('unknown'); + }); + + test('getLocaleDirection LTR 语言', () => { + expect(getLocaleDirection('zh-CN')).toBe('ltr'); + expect(getLocaleDirection('en-US')).toBe('ltr'); + }); + + test('getLocaleDirection RTL 语言', () => { + expect(getLocaleDirection('ar')).toBe('rtl'); + expect(getLocaleDirection('he')).toBe('rtl'); + expect(getLocaleDirection('fa')).toBe('rtl'); + }); +}); + +describe('格式化函数', () => { + beforeEach(() => { + setCurrentLocale('en-US'); + }); + + test('formatNumber 格式化数字', () => { + const r = formatNumber(1234567); + expect(typeof r).toBe('string'); + expect(r).toContain('1'); + expect(r).toContain('234'); + }); + + test('formatNumber 接受 options', () => { + const r = formatNumber(0.5, { style: 'percent' }); + expect(r).toContain('50'); + }); + + test('formatCurrency 格式化货币', () => { + const r = formatCurrency(99.99, 'USD'); + expect(typeof r).toBe('string'); + expect(r).toContain('99'); + }); + + test('formatDate 格式化日期', () => { + const r = formatDate('2024-01-15'); + expect(typeof r).toBe('string'); + // 至少包含年份信息 + expect(r.length).toBeGreaterThan(0); + }); + + test('formatDate 接受 Date 对象', () => { + const r = formatDate(new Date('2024-06-15')); + expect(typeof r).toBe('string'); + expect(r.length).toBeGreaterThan(0); + }); + + test('formatNumber 无效输入回退为字符串', () => { + const r = formatNumber('not-a-number'); + expect(typeof r).toBe('string'); + }); +}); + +describe('语言监听', () => { + beforeEach(() => { + clearLocaleListeners(); + setCurrentLocale('zh-CN'); + }); + + test('addLocaleListener 注册回调', () => { + const fn = jest.fn(); + addLocaleListener(fn); + setCurrentLocale('en-US'); + expect(fn).toHaveBeenCalledWith('en-US'); + }); + + test('addLocaleListener 返回取消函数', () => { + const fn = jest.fn(); + const unsub = addLocaleListener(fn); + expect(typeof unsub).toBe('function'); + unsub(); + setCurrentLocale('en-US'); + expect(fn).not.toHaveBeenCalled(); + }); + + test('removeLocaleListener 移除回调', () => { + const fn = jest.fn(); + addLocaleListener(fn); + removeLocaleListener(fn); + setCurrentLocale('en-US'); + expect(fn).not.toHaveBeenCalled(); + }); + + test('clearLocaleListeners 清空所有回调', () => { + const fn1 = jest.fn(); + const fn2 = jest.fn(); + addLocaleListener(fn1); + addLocaleListener(fn2); + clearLocaleListeners(); + setCurrentLocale('en-US'); + expect(fn1).not.toHaveBeenCalled(); + expect(fn2).not.toHaveBeenCalled(); + }); + + test('监听器抛错不影响其他监听器', () => { + const errFn = jest.fn(() => { throw new Error('boom'); }); + const okFn = jest.fn(); + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + addLocaleListener(errFn); + addLocaleListener(okFn); + setCurrentLocale('en-US'); + expect(errFn).toHaveBeenCalled(); + expect(okFn).toHaveBeenCalled(); + spy.mockRestore(); + }); +}); + +describe('语言持久化', () => { + beforeEach(() => { + localStorage.clear(); + setCurrentLocale('zh-CN'); + }); + + test('saveLocale 写入 localStorage', () => { + saveLocale('en-US'); + expect(localStorage.getItem('metona-editor-locale')).toBe('en-US'); + }); + + test('loadLocale 无保存时返回默认语言', () => { + // 无保存时调用 getDefaultLocale + const r = loadLocale(); + expect(typeof r).toBe('string'); + expect(r.length).toBeGreaterThan(0); + }); + + test('loadLocale 读取已保存值', () => { + localStorage.setItem('metona-editor-locale', 'en-US'); + expect(loadLocale()).toBe('en-US'); + }); + + test('initI18n 加载已保存的语言', () => { + localStorage.setItem('metona-editor-locale', 'en-US'); + initI18n(); + expect(getCurrentLocale()).toBe('en-US'); + }); +}); + +describe('默认语言探测', () => { + test('getDefaultLocale 返回字符串', () => { + const r = getDefaultLocale(); + expect(typeof r).toBe('string'); + expect(r.length).toBeGreaterThan(0); + }); + + test('navigator 不支持时返回 fallback', () => { + const origNav = global.navigator; + Object.defineProperty(global, 'navigator', { value: undefined, configurable: true }); + expect(getDefaultLocale()).toBe('zh-CN'); + Object.defineProperty(global, 'navigator', { value: origNav, configurable: true }); + }); +}); + +describe('fallback 语言', () => { + test('getFallbackLocale 默认 zh-CN', () => { + expect(i18nUtils.getFallbackLocale()).toBe('zh-CN'); + }); + + test('setFallbackLocale 修改 fallback', () => { + i18nUtils.setFallbackLocale('en-US'); + expect(i18nUtils.getFallbackLocale()).toBe('en-US'); + // 恢复 + i18nUtils.setFallbackLocale('zh-CN'); + }); +}); + +describe('i18nUtils 代理层', () => { + test('暴露所有 i18n API', () => { + [ + 't', 'getCurrentLocale', 'setCurrentLocale', 'switchLocale', + 'getFallbackLocale', 'setFallbackLocale', 'hasTranslation', + 'getTranslations', 'addTranslations', 'getSupportedLocales', + 'isLocaleSupported', 'getLocaleName', 'getLocaleDirection', + 'formatNumber', 'formatCurrency', 'formatDate', + 'addLocaleListener', 'removeLocaleListener', 'clearLocaleListeners', + 'initI18n', 'saveLocale', 'loadLocale', 'getDefaultLocale', + ].forEach((fn) => { + expect(typeof i18nUtils[fn]).toBe('function'); + }); + }); + + test('代理方法与具名导出一致', () => { + expect(i18nUtils.t).toBe(t); + expect(i18nUtils.getCurrentLocale).toBe(getCurrentLocale); + expect(i18nUtils.switchLocale).toBe(switchLocale); + }); +}); + +describe('presetLocales', () => { + test('包含 zh-CN 和 en-US', () => { + expect(presetLocales['zh-CN']).toBeDefined(); + expect(presetLocales['en-US']).toBeDefined(); + }); + + test('包含 name / nativeName / direction / translations', () => { + const z = presetLocales['zh-CN']; + expect(z.name).toBeDefined(); + expect(z.nativeName).toBeDefined(); + expect(z.direction).toBe('ltr'); + expect(z.translations).toBe(LOCALES['zh-CN']); + }); +}); + +describe('createI18nManager', () => { + test('返回包含全部方法的管理器对象', () => { + const m = createI18nManager(); + expect(typeof m.t).toBe('function'); + expect(typeof m.setCurrentLocale).toBe('function'); + expect(typeof m.formatNumber).toBe('function'); + }); + + test('管理器方法可独立调用', () => { + const m = createI18nManager(); + m.setCurrentLocale('en-US'); + expect(m.getCurrentLocale()).toBe('en-US'); + expect(m.t('bold')).toBe('Bold'); + }); +}); diff --git a/tests/parser.test.js b/tests/parser.test.js new file mode 100644 index 0000000..5a2f83b --- /dev/null +++ b/tests/parser.test.js @@ -0,0 +1,353 @@ +/** + * parser.js 单元测试 + * 覆盖 Markdown 解析器的所有语法分支与安全特性 + */ + +import { parseMarkdown, safeUrl, slugify } from '../src/parser.js'; + +describe('parseMarkdown - 基础', () => { + test('空输入返回空字符串', () => { + expect(parseMarkdown('')).toBe(''); + expect(parseMarkdown(null)).toBe(''); + expect(parseMarkdown(undefined)).toBe(''); + }); + + test('非字符串输入被转换为字符串', () => { + expect(parseMarkdown(123)).toBe('

      123

      '); + }); + + test('Windows 换行符被规范化', () => { + const html = parseMarkdown('line1\r\nline2'); + expect(html).toContain('line1'); + expect(html).toContain('line2'); + expect(html).not.toContain('\r'); + }); +}); + +describe('parseMarkdown - 标题', () => { + test('h1 到 h6', () => { + expect(parseMarkdown('# T1')).toBe('

      T1

      '); + expect(parseMarkdown('## T2')).toBe('

      T2

      '); + expect(parseMarkdown('### T3')).toBe('

      T3

      '); + expect(parseMarkdown('#### T4')).toBe('

      T4

      '); + expect(parseMarkdown('##### T5')).toBe('
      T5
      '); + expect(parseMarkdown('###### T6')).toBe('
      T6
      '); + }); + + test('标题文本被内联渲染', () => { + const html = parseMarkdown('# Hello **world**'); + expect(html).toContain('world'); + }); + + test('标题生成锚点 id', () => { + expect(parseMarkdown('# Hello World')).toContain('id="hello-world"'); + }); + + test('超过 6 个 # 不作为标题', () => { + const html = parseMarkdown('####### not heading'); + expect(html).not.toContain(''); + }); +}); + +describe('parseMarkdown - 段落', () => { + test('单行段落', () => { + expect(parseMarkdown('hello')).toBe('

      hello

      '); + }); + + test('多行段落合并', () => { + const html = parseMarkdown('line1\nline2'); + expect(html).toBe('

      line1
      line2

      '); + }); + + test('空行分隔段落', () => { + const html = parseMarkdown('p1\n\np2'); + expect(html).toBe('

      p1

      \n

      p2

      '); + }); +}); + +describe('parseMarkdown - 行内格式', () => { + test('粗体 **text**', () => { + expect(parseMarkdown('**bold**')).toBe('

      bold

      '); + }); + + test('粗体 __text__', () => { + expect(parseMarkdown('__bold__')).toBe('

      bold

      '); + }); + + test('斜体 *text*', () => { + expect(parseMarkdown('a *italic* b')).toBe('

      a italic b

      '); + }); + + test('斜体 _text_', () => { + expect(parseMarkdown('a _italic_ b')).toBe('

      a italic b

      '); + }); + + test('删除线 ~~text~~', () => { + expect(parseMarkdown('~~del~~')).toBe('

      del

      '); + }); + + test('行内代码 `code`', () => { + const html = parseMarkdown('use `code` here'); + expect(html).toContain('code'); + }); + + test('行内代码内容不被解析为格式', () => { + const html = parseMarkdown('`a **b** c`'); + expect(html).toContain('a **b** c'); + expect(html).not.toContain(''); + }); + + test('行内代码内容被转义', () => { + const html = parseMarkdown('`'); + expect(html).toContain('<script>'); + expect(html).not.toContain('