├── .nvmrc
├── example
├── env.d.ts
├── tsconfig.json
├── tsconfig.app.json
├── src
│ ├── main.ts
│ └── App.vue
├── vite.config.ts
├── index.html
├── tsconfig.node.json
├── .gitignore
├── package.json
└── pnpm-lock.yaml
├── .gitattributes
├── .eslintrc.json
├── .vscode
├── settings.json
└── extensions.json
├── demo-i18n.png
├── demo-async.png
├── demo-debug.png
├── docs
├── editor.png
├── .vitepress
│ ├── components
│ │ └── DemoContainer.vue
│ ├── theme
│ │ └── index.js
│ └── config.js
├── editor-demo.md
├── executor-demo.md
├── ExecutorDemo.vue
├── EditorDemo.vue
└── index.md
├── src
├── utils
│ ├── index.ts
│ └── basic.ts
├── components
│ ├── index.ts
│ ├── Debug.vue
│ ├── CmWrap.vue
│ └── Editor.vue
├── processes
│ ├── index.ts
│ ├── funcLib.ts
│ ├── executor.ts
│ ├── interface.ts
│ └── cmEditor.ts
├── assets
│ ├── icon.ts
│ ├── main.css
│ └── locales
│ │ ├── zh-CN.json
│ │ └── en.json
├── env.d.ts
├── index.ts
└── locales.ts
├── demo-diagnostic.png
├── .prettierignore
├── .gitignore
├── postcss.config.js
├── eslint.config.js
├── .prettierrc.json
├── typedoc.json
├── tailwind.config.js
├── components.d.ts
├── .github
└── workflows
│ ├── leak_checker.yml
│ └── CICD.yml
├── tsconfig.json
├── vite.config.ts
├── LICENSE
├── README.md
├── package.json
└── test
├── executor.test.ts
└── cmEditor.test.ts
/.nvmrc:
--------------------------------------------------------------------------------
1 | lts/*
2 |
--------------------------------------------------------------------------------
/example/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.sh eol=lf
2 | Dockerfile eol=lf
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "prettier/prettier": "off"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "vue-i18n.i18nPaths": "src\\assets\\locales"
3 | }
4 |
--------------------------------------------------------------------------------
/demo-i18n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ideal-world/formula-editor/HEAD/demo-i18n.png
--------------------------------------------------------------------------------
/demo-async.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ideal-world/formula-editor/HEAD/demo-async.png
--------------------------------------------------------------------------------
/demo-debug.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ideal-world/formula-editor/HEAD/demo-debug.png
--------------------------------------------------------------------------------
/docs/editor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ideal-world/formula-editor/HEAD/docs/editor.png
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | import * as IwUtils from './basic'
2 |
3 | export {
4 | IwUtils,
5 | }
6 |
--------------------------------------------------------------------------------
/demo-diagnostic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ideal-world/formula-editor/HEAD/demo-diagnostic.png
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | import IwEditor from './Editor.vue'
2 |
3 | export {
4 | IwEditor,
5 | }
6 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | /dist/*
2 | .local
3 | .output.js
4 | /node_modules/**
5 |
6 | **/*.svg
7 | **/*.sh
8 |
9 | /public/*
--------------------------------------------------------------------------------
/docs/.vitepress/components/DemoContainer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | docs/.vitepress/cache
7 | docs/.vitepress/.temp
8 | docs/api
9 | types
10 | .idea
--------------------------------------------------------------------------------
/src/processes/index.ts:
--------------------------------------------------------------------------------
1 | import * as iwInterface from './interface'
2 | import * as iwExecutor from './executor'
3 |
4 | export {
5 | iwInterface,
6 | iwExecutor,
7 | }
8 |
--------------------------------------------------------------------------------
/src/assets/icon.ts:
--------------------------------------------------------------------------------
1 | // https://primer.style/foundations/icons
2 |
3 | export const DEBUG = 'octicon-bug-24'
4 | export const INFO = 'octicon-code-review-24'
5 | export const RUN = 'octicon-play-24'
6 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | 'postcss-import': {},
4 | 'tailwindcss/nesting': 'postcss-nesting',
5 | 'tailwindcss': {},
6 | 'autoprefixer': {},
7 | },
8 | }
9 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "references": [
3 | {
4 | "path": "./tsconfig.node.json"
5 | },
6 | {
7 | "path": "./tsconfig.app.json"
8 | }
9 | ],
10 | "files": []
11 | }
12 |
--------------------------------------------------------------------------------
/docs/editor-demo.md:
--------------------------------------------------------------------------------
1 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module '*.vue' {
4 | import type { DefineComponent } from 'vue'
5 |
6 | const component: DefineComponent<{}, {}, any>
7 | export default component
8 | }
9 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "vue.volar",
4 | "dbaeumer.vscode-eslint",
5 | "bradlc.vscode-tailwindcss",
6 | "ms-vscode.vscode-typescript-next",
7 | "vitest.explorer"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import antfu from '@antfu/eslint-config'
2 |
3 | export default antfu({
4 | overrides: {
5 | vue: {
6 | 'vue/no-mutating-props': ['error', {
7 | shallowOnly: true,
8 | }],
9 | },
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/prettierrc",
3 | "semi": false,
4 | "tabWidth": 2,
5 | "useTabs": false,
6 | "singleQuote": true,
7 | "printWidth": 180,
8 | "trailingComma": "all",
9 | "embeddedLanguageFormatting": "auto"
10 | }
11 |
--------------------------------------------------------------------------------
/example/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.dom.json",
3 | "compilerOptions": {
4 | "composite": true,
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": ["./src/*"]
8 | }
9 | },
10 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
11 | "exclude": ["src/**/__tests__/*"]
12 | }
13 |
--------------------------------------------------------------------------------
/example/src/main.ts:
--------------------------------------------------------------------------------
1 | import FormulaEditor from '@idealworld/formula-editor'
2 | import '@idealworld/formula-editor/dist/style.css'
3 | import ElementPlus from 'element-plus'
4 | import 'element-plus/dist/index.css'
5 | import { createApp } from 'vue'
6 | import App from './App.vue'
7 |
8 | createApp(App)
9 | .use(ElementPlus)
10 | .use(FormulaEditor)
11 | .mount('#app')
12 |
--------------------------------------------------------------------------------
/typedoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugin": ["typedoc-plugin-markdown", "typedoc-vitepress-theme"],
3 | "entryFileName": "exports.md",
4 | "entryPoints": ["src/index.ts"],
5 | "includes": "src/*.ts",
6 | "readme": "none",
7 | "includeVersion": true,
8 | "disableSources": false,
9 | "sidebar": {
10 | "autoConfiguration": false
11 | },
12 | "out":"./docs/api"
13 | }
14 |
--------------------------------------------------------------------------------
/example/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { URL, fileURLToPath } from 'node:url'
2 |
3 | import { defineConfig } from 'vite'
4 | import vue from '@vitejs/plugin-vue'
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | plugins: [
9 | vue(),
10 | ],
11 | resolve: {
12 | alias: {
13 | '@': fileURLToPath(new URL('./src', import.meta.url)),
14 | },
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Formula Example
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | import daisyui from 'daisyui'
3 |
4 | export default {
5 | content: ['./src/**/*.{vue,html,js}'],
6 | theme: {
7 | extend: {},
8 | },
9 | plugins: [daisyui],
10 | daisyui: {
11 | prefix: 'iw-',
12 | themes: ['light', 'dark', 'cupcake', 'forest', 'lofi', 'black', 'acid', 'lemonade', 'night', 'coffee'],
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/index.js:
--------------------------------------------------------------------------------
1 | import DefaultTheme from 'vitepress/theme'
2 | import DemoContainer from '../components/DemoContainer.vue'
3 | import FormulaEditor from '../../../src'
4 |
5 | globalThis.__VUE_PROD_DEVTOOLS__ = false
6 |
7 | export default {
8 | ...DefaultTheme,
9 | enhanceApp({ app }) {
10 | app.use(FormulaEditor)
11 | app.component('DemoContainer', DemoContainer)
12 | },
13 | }
14 |
--------------------------------------------------------------------------------
/docs/executor-demo.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | This component provides an independent formula execution engine.
6 |
7 | For the API of the execution engine, see: [Execute API](api/namespaces/iwExecutor/functions/execute.html)
8 |
9 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/example/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@tsconfig/node18/tsconfig.json",
3 | "compilerOptions": {
4 | "composite": true,
5 | "module": "ESNext",
6 | "moduleResolution": "Bundler",
7 | "types": ["node"]
8 | },
9 | "include": [
10 | "vite.config.*",
11 | "vitest.config.*",
12 | "cypress.config.*",
13 | "nightwatch.conf.*",
14 | "playwright.config.*"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist
13 | dist-ssr
14 | coverage
15 | *.local
16 | package-lock.json
17 |
18 | /cypress/videos/
19 | /cypress/screenshots/
20 |
21 | # Editor directories and files
22 | .vscode/*
23 | !.vscode/extensions.json
24 | .idea
25 | *.suo
26 | *.ntvs*
27 | *.njsproj
28 | *.sln
29 | *.sw?
30 |
--------------------------------------------------------------------------------
/components.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /* prettier-ignore */
3 | // @ts-nocheck
4 | // Generated by unplugin-vue-components
5 | // Read more: https://github.com/vuejs/core/pull/3399
6 | export {}
7 |
8 | declare module 'vue' {
9 | export interface GlobalComponents {
10 | CmWrap: typeof import('./src/components/CmWrap.vue')['default']
11 | Debug: typeof import('./src/components/Debug.vue')['default']
12 | Editor: typeof import('./src/components/Editor.vue')['default']
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import type { App } from 'vue'
2 | import { defineCustomElement } from 'vue'
3 | import './assets/main.css'
4 | import locales from './locales'
5 | import { IwEditor } from './components'
6 |
7 | export default (app: App): void => {
8 | app.use(locales).component('IwEditor', IwEditor)
9 | }
10 |
11 | export const IwEditorComp = defineCustomElement(IwEditor)
12 |
13 | declare module 'vue' {
14 | export interface GlobalComponents {
15 | 'IwEditor': typeof IwEditorComp
16 | }
17 | }
18 |
19 | export * from './processes'
20 |
--------------------------------------------------------------------------------
/src/locales.ts:
--------------------------------------------------------------------------------
1 | import { createI18n } from 'vue-i18n'
2 | import en from './assets/locales/en.json'
3 | import zh from './assets/locales/zh-CN.json'
4 |
5 | const locales = createI18n({
6 | legacy: false,
7 | locale: (typeof localStorage !== 'undefined' ? localStorage.getItem('locale') : undefined) || (typeof navigator !== 'undefined' && typeof navigator.language !== 'undefined' ? navigator.language.slice(0, 2) : undefined),
8 | fallbackLocale: 'en',
9 | messages: {
10 | zh,
11 | en,
12 | },
13 | })
14 |
15 | export default locales
16 |
--------------------------------------------------------------------------------
/.github/workflows/leak_checker.yml:
--------------------------------------------------------------------------------
1 | name: LeakChecker
2 |
3 | on: [push, pull_request_target]
4 |
5 | jobs:
6 | hello_world_job:
7 | runs-on: ubuntu-latest
8 | name: Scan Keywords
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v3
12 | - name: Scan Keywords
13 | uses: PPG007/keywords-scanner@v1.0
14 | with:
15 | keywords: ${{secrets.LEAK_WORDS}}
16 | ignoreCase: true
17 | ignoredDirs: '["docs","pnpm-lock.yaml","node_modules","demo-diagnostic.png","demo-debug.png","demo-async.png","demo-i18n.png"]'
18 |
--------------------------------------------------------------------------------
/docs/.vitepress/config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | title: 'Formula Editor',
3 | description: 'A relatively complete formula editor',
4 | base: '/formula-editor/',
5 | themeConfig: {
6 | nav: [
7 | { text: 'Introduction', link: '/' },
8 | { text: 'Edit Demo', link: '/editor-demo' },
9 | { text: 'Execute Demo', link: '/executor-demo' },
10 | { text: 'API', link: '/api/exports' },
11 | ],
12 | socialLinks: [{ icon: 'github', link: 'https://github.com/ideal-world/formula-editor' }],
13 | },
14 | vite: {
15 | build: {
16 | chunkSizeWarningLimit: 1500,
17 | },
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/src/assets/main.css:
--------------------------------------------------------------------------------
1 | @import 'octicons-css/octicons.min.css';
2 |
3 | @tailwind base;
4 | @tailwind components;
5 | @tailwind utilities;
6 |
7 | /* Eliminating the effects of vitepress */
8 | .iw-editor {
9 |
10 | button,
11 | input,
12 | optgroup,
13 | select,
14 | textarea {
15 | border-width: 1px;
16 | border-style: solid;
17 | }
18 |
19 | a {
20 | text-decoration: none !important;
21 | }
22 |
23 | p {
24 | line-height: 1;
25 | }
26 |
27 | p,
28 | summary {
29 | margin: 0;
30 | }
31 |
32 | ul {
33 | margin: 0;
34 | }
35 |
36 | li+li {
37 | margin-top: 0;
38 | }
39 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "lib": [
5 | "esnext",
6 | "dom"
7 | ],
8 | "useDefineForClassFields": true,
9 | "module": "esnext",
10 | "moduleResolution": "node",
11 | "resolveJsonModule": true,
12 | "types": [
13 | "vite/client"
14 | ],
15 | "strict": true,
16 | "declaration": true,
17 | "declarationDir": "./dist/types",
18 | "emitDeclarationOnly": true,
19 | "sourceMap": true,
20 | "esModuleInterop": true,
21 | "isolatedModules": true,
22 | "skipLibCheck": true
23 | },
24 | "include": [
25 | "src/**/*.ts",
26 | "src/**/*.d.ts",
27 | "src/**/*.vue",
28 | "src/index.js"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "formula-example",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "run-p type-check \"build-only {@}\" --",
8 | "preview": "vite preview",
9 | "build-only": "vite build",
10 | "type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false"
11 | },
12 | "dependencies": {
13 | "@element-plus/icons-vue": "^2.3.1",
14 | "@idealworld/formula-editor": "file:../",
15 | "element-plus": "^2.4.3",
16 | "vue": "^3.3.11"
17 | },
18 | "devDependencies": {
19 | "@tsconfig/node18": "^18.2.2",
20 | "@types/node": "^20.10.4",
21 | "@vitejs/plugin-vue": "^4.5.2",
22 | "@vue/tsconfig": "^0.4.0",
23 | "npm-run-all2": "^6.1.1",
24 | "typescript": "^5.3.3",
25 | "vite": "^5.0.8",
26 | "vue-tsc": "^1.8.25"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'node:path'
2 | import { defineConfig } from 'vite'
3 | import Components from 'unplugin-vue-components/vite'
4 | import vue from '@vitejs/plugin-vue'
5 |
6 | export default defineConfig({
7 | plugins: [
8 | vue(),
9 | Components({
10 | dirs: ['src/components'],
11 | extensions: ['vue'],
12 | dts: 'components.d.ts',
13 | }),
14 | ],
15 | build: {
16 | lib: {
17 | entry: path.resolve(__dirname, 'src/index.ts'),
18 | name: 'formula-editor',
19 | formats: ['es', 'umd'],
20 | fileName: format => `formula-editor.${format}.js`,
21 | },
22 | rollupOptions: {
23 | external: ['vue', 'vue-i18n', 'codemirror', 'octicons-css', 'eslint-linter-browserify', 'vue-codemirror6', /@codemirror\/.+/, /@lezer\/.+/],
24 | output: {
25 | exports: 'named',
26 | globals: {
27 | vue: 'Vue',
28 | },
29 | },
30 | },
31 | emptyOutDir: false,
32 | },
33 | test: {
34 | globals: true,
35 | environment: 'happy-dom',
36 | },
37 | })
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Ideal World
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Formula Editor
2 |
3 | [](https://www.npmjs.com/package/@idealworld/formula-editor)
4 | [](https://github.com/ideal-world/formula-editor)
5 | [](https://github.com/ideal-world/formula-editor/commits/main)
6 | [](https://github.com/ideal-world/formula-editor/actions/workflows/CICD.yml)
7 |
8 | **A relatively complete formula editor.**
9 |
10 | ## Features
11 |
12 | - Visual design
13 | - Relatively comprehensive error prompts
14 | - Auto-completion prompts
15 | - Customizable variables and synchronous/asynchronous functions
16 | - Online debugging
17 | - Dynamic formula execution engine
18 |
19 | ## Screenshots
20 |
21 | **Error diagnosis**
22 |
23 | 
24 |
25 | **Online debugging**
26 |
27 | 
28 |
29 | **Asynchronous call**
30 |
31 | 
32 |
33 | **I18n**
34 |
35 | 
36 |
37 | ## Documentation and examples
38 |
39 | [https://ideal-world.github.io/formula-editor/](https://ideal-world.github.io/formula-editor/)
40 |
--------------------------------------------------------------------------------
/src/utils/basic.ts:
--------------------------------------------------------------------------------
1 | export function groupBy(array: T[], predicate: (value: T, index: number, array: T[]) => string) {
2 | return array.reduce((acc, value, index, array) => {
3 | (acc[predicate(value, index, array)] ||= []).push(value)
4 | return acc
5 | }, {} as { [key: string]: T[] })
6 | }
7 |
8 | export function hasParentWithClass(element: HTMLElement | null, className: string): boolean {
9 | return getParentWithClass(element, className) != null
10 | }
11 |
12 | export function getParentWithClass(element: HTMLElement | null, className: string): HTMLElement | null {
13 | while (element) {
14 | if (element.classList && element.classList.contains(className))
15 | return element
16 |
17 | element = element.parentElement
18 | }
19 | return null
20 | }
21 |
22 | export function getChildIndex(parent: HTMLElement, child: HTMLElement): number {
23 | return Array.prototype.indexOf.call(parent.children, child)
24 | }
25 |
26 | export function getRandomInt(min: number, max: number) {
27 | min = Math.ceil(min)
28 | max = Math.floor(max)
29 | return Math.floor(Math.random() * (max - min + 1)) + min
30 | }
31 |
32 | export function getRandomString(length: number) {
33 | let result = ''
34 | const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
35 | for (let i = 0; i < length; i++)
36 | result += characters.charAt(Math.floor(Math.random() * characters.length))
37 |
38 | return result
39 | }
40 |
--------------------------------------------------------------------------------
/src/assets/locales/zh-CN.json:
--------------------------------------------------------------------------------
1 | {
2 | "_": {
3 | "var": "变量",
4 | "fun": "函数"
5 | },
6 | "data_kind": {
7 | "STRING": "字符串",
8 | "NUMBER": "数值",
9 | "BOOLEAN": "布尔",
10 | "NULL": "空",
11 | "STRINGS": "字符串数组",
12 | "NUMBERS": "数值数组",
13 | "BOOLEANS": "布尔数组",
14 | "ANY": "任意类型"
15 | },
16 | "diagnostic": {
17 | "unterminated_string_constant": "未终止的字符串常量",
18 | "syntax_error": "公式语法错误",
19 | "format_error": "期望格式为[{expect}],实际为[{real}]",
20 | "bracket_error": "括号格式错误",
21 | "binary_expression_error": "二元表达式格式错误",
22 | "conditional_expression_error": "条件表达式格式错误",
23 | "var_fun_not_exist_error": "变量/函数不存在",
24 | "var_not_exist_error": "变量不存在",
25 | "fun_not_exist_error": "函数不存在",
26 | "param_not_exist_error": "缺少参数",
27 | "fun_format_error": "函数格式错误",
28 | "namespace_not_exist_error": "命名空间不存在",
29 | "param_length_error": "期望参数长度为[{expect}],实际为[{real}]"
30 | },
31 | "executor": {
32 | "param_value_not_exist_error": "参数 [{var}] 值不存在",
33 | "execute_error": "公式执行错误:"
34 | },
35 | "editor": {
36 | "placeholder": "在此输入公式",
37 | "debug": "调试",
38 | "search_var": "搜索变量",
39 | "search_fun": "搜索函数/API",
40 | "empty": "暂无数据",
41 | "tips": "小提示",
42 | "tip1": "指向右侧函数时可在此处显示使用说明",
43 | "tip2": "点击右侧函数可将其插入公式编辑器光标所在位置",
44 | "tip3": "公式编辑器中输入任何字符会显示可用变量/函数列表",
45 | "tip4": "公式编辑器中输入 {entrance} 会显示分类可用列表"
46 | },
47 | "debug": {
48 | "run": "运行",
49 | "result": "结果",
50 | "formula_error": "请先修正公式错误后再运行"
51 | },
52 | "fun_lib": {
53 | "inner": "内置",
54 | "cate_common": "常用",
55 | "cate_calc": "计算",
56 | "cate_txt": "文本",
57 | "cate_time": "时间",
58 | "cate_api": "接口",
59 | "sum_label": "求和",
60 | "sum_note": "获取一组数值的总和。
用法:SUM(数字1,数字2,...)",
61 | "now_label": "当前时间",
62 | "now_note": "返回当前时间戳",
63 | "concat_label": "合并文本",
64 | "concat_note": "将多个文本合并成一个文本。
用法:concat(文本1,文本2,...)",
65 | "lower_label": "转成小写",
66 | "lower_note": "将一个文本中的所有大写字母转换为小写字母。
用法:lower(文本)",
67 | "upper_label": "转成大写",
68 | "upper_note": "将一个文本中的所有小写字母转换为大写字母。
用法:upper(文本)",
69 | "httpGet_label": "HTTP Get请求",
70 | "httpGet_note": "发起HTTP Get请求,返回Json格式。
用法:httpGet(https://httpbin.org/get)",
71 | "httpGet_input1": "请求地址"
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@idealworld/formula-editor",
3 | "type": "module",
4 | "version": "0.6.2",
5 | "description": "A relatively complete formula editor",
6 | "author": {
7 | "name": "gudaoxuri",
8 | "email": "i@sunisle.org",
9 | "url": "https://idealworld.group/"
10 | },
11 | "license": "MIT",
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/ideal-world/formula-editor.git"
15 | },
16 | "keywords": [
17 | "formula",
18 | "editor",
19 | "code"
20 | ],
21 | "exports": {
22 | ".": {
23 | "types": "./dist/types/index.d.ts",
24 | "import": "./dist/formula-editor.es.js",
25 | "require": "./dist/formula-editor.umd.js"
26 | },
27 | "./dist/style.css": "./dist/style.css"
28 | },
29 | "module": "./dist/formula-editor.es.js",
30 | "types": "./dist/types/index.d.ts",
31 | "files": [
32 | "dist"
33 | ],
34 | "scripts": {
35 | "build": "rimraf dist && vue-tsc && vite build",
36 | "docs:dev": "vitepress dev docs --host 0.0.0.0",
37 | "docs:build": "npm run publish-typedoc && vitepress build docs",
38 | "docs:serve": "vitepress serve docs",
39 | "test": "vitest --run",
40 | "lint": "eslint .",
41 | "lint:fix": "eslint . --fix",
42 | "coverage": "vitest run --coverage",
43 | "publish-typedoc": "typedoc --options typedoc.json"
44 | },
45 | "peerDependencies": {
46 | "vue": "^3.4.21"
47 | },
48 | "dependencies": {
49 | "@codemirror/autocomplete": "^6.15.0",
50 | "@codemirror/commands": "^6.3.3",
51 | "@codemirror/lang-javascript": "^6.2.2",
52 | "@codemirror/language": "^6.10.1",
53 | "@codemirror/lint": "^6.5.0",
54 | "@codemirror/search": "^6.5.6",
55 | "@codemirror/state": "^6.4.1",
56 | "@codemirror/view": "^6.26.0",
57 | "@lezer/common": "^1.2.1",
58 | "codemirror": "=6.0.1",
59 | "eslint-linter-browserify": "^8.57.0",
60 | "octicons-css": "^19.8.0",
61 | "vue-codemirror6": "^1.2.5",
62 | "vue-i18n": "^9.10.2"
63 | },
64 | "devDependencies": {
65 | "@antfu/eslint-config": "^2.8.3",
66 | "@types/node": "^20.11.29",
67 | "@vitejs/plugin-vue": "^5.0.4",
68 | "@vitest/ui": "^1.4.0",
69 | "autoprefixer": "^10.4.18",
70 | "daisyui": "^4.7.3",
71 | "eslint": "^8.57.0",
72 | "happy-dom": "^14.0.0",
73 | "postcss": "^8.4.36",
74 | "postcss-import": "^16.0.1",
75 | "postcss-nesting": "^12.1.0",
76 | "rimraf": "^5.0.5",
77 | "tailwindcss": "^3.4.1",
78 | "typedoc": "^0.25.12",
79 | "typedoc-plugin-markdown": "4.0.0-next.53",
80 | "typedoc-vitepress-theme": "1.0.0-next.9",
81 | "typescript": "^5.4.2",
82 | "unplugin-vue-components": "^0.26.0",
83 | "vite": "^5.1.6",
84 | "vitepress": "1.0.0-rc.45",
85 | "vitest": "^1.4.0",
86 | "vue-tsc": "^2.0.6"
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/example/src/App.vue:
--------------------------------------------------------------------------------
1 |
90 |
91 |
92 |
93 | 打开
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/src/processes/funcLib.ts:
--------------------------------------------------------------------------------
1 | import locales from '../locales'
2 | import { VarKind } from './interface'
3 |
4 | const { t } = locales.global
5 |
6 | /**
7 | * Default function library
8 | */
9 | export const DEFAULT_FUN_LIB
10 | = {
11 | name: 'fun',
12 | label: t('fun_lib.inner'),
13 | isVar: false,
14 | showLabel: false,
15 | color: '#d9ecff',
16 | items: [
17 | {
18 | name: 'sum',
19 | label: t('fun_lib.sum_label'),
20 | note: t('fun_lib.sum_note'),
21 | input: [
22 | {
23 | kind: VarKind.NUMBER,
24 | },
25 | ],
26 | isVarLen: true,
27 | isAsync: false,
28 | output: {
29 | kind: VarKind.NUMBER,
30 | },
31 | body: `return Array.from(arguments).reduce((a, b) => a + b)`,
32 | cates: [t('fun_lib.cate_common'), t('fun_lib.cate_calc')],
33 | },
34 | {
35 | name: 'now',
36 | label: t('fun_lib.now_label'),
37 | note: t('fun_lib.now_note'),
38 | input: [],
39 | isVarLen: false,
40 | isAsync: false,
41 | output: {
42 | kind: VarKind.NUMBER,
43 | },
44 | body: `return Date.now()`,
45 | cates: [t('fun_lib.cate_common'), t('fun_lib.cate_time')],
46 | },
47 | {
48 | name: 'concat',
49 | label: t('fun_lib.concat_label'),
50 | note: t('fun_lib.concat_note'),
51 | input: [
52 | {
53 | kind: VarKind.ANY,
54 | },
55 | ],
56 | isVarLen: true,
57 | isAsync: false,
58 | output: {
59 | kind: VarKind.STRING,
60 | },
61 | body: `return Array.from(arguments).join('')`,
62 | cates: [t('fun_lib.cate_common'), t('fun_lib.cate_txt')],
63 | },
64 | {
65 | name: 'lower',
66 | label: t('fun_lib.lower_label'),
67 | note: t('fun_lib.lower_note'),
68 | input: [
69 | {
70 | kind: VarKind.STRING,
71 | },
72 | ],
73 | isVarLen: false,
74 | isAsync: false,
75 | output: {
76 | kind: VarKind.STRING,
77 | },
78 | body: `return arguments[0].toLowerCase()`,
79 | cates: [t('fun_lib.cate_txt')],
80 | },
81 | {
82 | name: 'upper',
83 | label: t('fun_lib.upper_label'),
84 | note: t('fun_lib.upper_note'),
85 | input: [
86 | {
87 | kind: VarKind.STRING,
88 | },
89 | ],
90 | isVarLen: false,
91 | isAsync: false,
92 | output: {
93 | kind: VarKind.STRING,
94 | },
95 | body: `return arguments[0].toUpperCase()`,
96 | cates: [t('fun_lib.cate_txt')],
97 | },
98 | {
99 | name: 'httpGet',
100 | label: t('fun_lib.httpGet_label'),
101 | note: t('fun_lib.httpGet_note'),
102 | input: [
103 | {
104 | label: t('fun_lib.httpGet_input1'),
105 | kind: VarKind.STRING,
106 | },
107 | ],
108 | isVarLen: false,
109 | isAsync: true,
110 | output: {
111 | kind: VarKind.ANY,
112 | },
113 | body: `return await (await fetch(arguments[0])).json()`,
114 | cates: [t('fun_lib.cate_api')],
115 | },
116 | ],
117 | }
118 |
--------------------------------------------------------------------------------
/docs/ExecutorDemo.vue:
--------------------------------------------------------------------------------
1 |
71 |
72 |
73 |
74 |
75 | Formula
76 |
77 |
78 |
79 |
80 |
81 | Parameters
82 |
83 |
84 |
85 |
86 |
87 | Materials
88 |
89 |
90 |
91 |
92 |
93 | Entrance
94 |
95 |
96 |
97 |
98 |
99 |
102 |
103 |
104 | Result
105 |
106 |
107 | {{ form.result }}
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/.github/workflows/CICD.yml:
--------------------------------------------------------------------------------
1 | name: CICD
2 |
3 | on: [push, pull_request]
4 |
5 | permissions:
6 | contents: read
7 | pages: write
8 | id-token: write
9 |
10 | concurrency:
11 | group: pages
12 | cancel-in-progress: false
13 |
14 | jobs:
15 | publish:
16 | name: Publish
17 | if: startsWith(github.ref, 'refs/tags/')
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - name: Checkout repo
22 | uses: actions/checkout@v3
23 |
24 | - name: Setup Node
25 | uses: actions/setup-node@v3
26 | with:
27 | node-version: 18
28 |
29 | - name: Install pnpm
30 | uses: pnpm/action-setup@v2
31 | with:
32 | version: 8
33 | run_install: false
34 |
35 | - name: Get pnpm store directory
36 | shell: bash
37 | run: |
38 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
39 |
40 | - name: Setup pnpm cache
41 | uses: actions/cache@v3
42 | with:
43 | path: ${{ env.STORE_PATH }}
44 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
45 | restore-keys: |
46 | ${{ runner.os }}-pnpm-store-
47 |
48 | - name: Install dependencies
49 | run: pnpm install
50 |
51 | - name: Test
52 | run: pnpm run test
53 |
54 | - name: Build
55 | run: pnpm run build
56 |
57 | - name: Publish to NPM
58 | uses: JS-DevTools/npm-publish@v2
59 | with:
60 | token: ${{ secrets.NPM_TOKEN }}
61 | access: public
62 |
63 | doc-build:
64 | name: DocBuild
65 | runs-on: ubuntu-latest
66 |
67 | steps:
68 | - name: Checkout repo
69 | uses: actions/checkout@v3
70 |
71 | - name: Setup Node
72 | uses: actions/setup-node@v3
73 | with:
74 | node-version: 18
75 |
76 | - name: Install pnpm
77 | uses: pnpm/action-setup@v2
78 | with:
79 | version: 8
80 | run_install: false
81 |
82 | - name: Get pnpm store directory
83 | shell: bash
84 | run: |
85 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
86 |
87 | - name: Setup pnpm cache
88 | uses: actions/cache@v3
89 | with:
90 | path: ${{ env.STORE_PATH }}
91 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
92 | restore-keys: |
93 | ${{ runner.os }}-pnpm-store-
94 |
95 | - name: Setup Pages
96 | uses: actions/configure-pages@v3
97 |
98 | - name: Install dependencies
99 | run: pnpm install
100 |
101 | - name: Test
102 | run: pnpm run test
103 |
104 | - name: Build with VitePress
105 | run: |
106 | pnpm run docs:build
107 | touch docs/.vitepress/dist/.nojekyll
108 |
109 | - name: Upload artifact
110 | uses: actions/upload-pages-artifact@v2
111 | with:
112 | path: docs/.vitepress/dist
113 |
114 | doc-deploy:
115 | name: DocDeploy
116 | runs-on: ubuntu-latest
117 | needs: doc-build
118 | environment:
119 | name: github-pages
120 | url: ${{ steps.deployment.outputs.page_url }}
121 |
122 | steps:
123 | - name: Deploy to GitHub Pages
124 | id: deployment
125 | uses: actions/deploy-pages@v2
126 |
--------------------------------------------------------------------------------
/src/assets/locales/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "_": {
3 | "var": "variable",
4 | "fun": "function"
5 | },
6 | "data_kind": {
7 | "STRING": "string",
8 | "NUMBER": "numeric",
9 | "BOOLEAN": "boolean",
10 | "NULL": "null",
11 | "STRINGS": "string array",
12 | "NUMBERS": "numeric array",
13 | "BOOLEANS": "boolean array",
14 | "ANY": "any type"
15 | },
16 | "diagnostic": {
17 | "unterminated_string_constant": "Unterminated string constant",
18 | "syntax_error": "Formula syntax error",
19 | "format_error": "The expected format is [{expect}], the actual format is [{real}]",
20 | "bracket_error": "brackets error",
21 | "binary_expression_error": "Binary expression error",
22 | "conditional_expression_error": "Conditional expression error",
23 | "var_fun_not_exist_error": "Variable/function does not exist",
24 | "var_not_exist_error": "Variable does not exist",
25 | "fun_not_exist_error": "Function does not exist",
26 | "param_not_exist_error": "Missing parameters",
27 | "fun_format_error": "Function format error",
28 | "namespace_not_exist_error": "Namespace does not exist",
29 | "param_length_error": "The expected parameter length is [{expect}], the actual length is [{real}]"
30 | },
31 | "executor": {
32 | "param_value_not_exist_error": "Parameter [{var}] value does not exist",
33 | "execute_error": "Formula execution error:"
34 | },
35 | "editor": {
36 | "placeholder": "Enter formula here",
37 | "debug": "Debug",
38 | "search_var": "Search variables",
39 | "search_fun": "Search functions/apis",
40 | "empty": "No data",
41 | "tips": "Tips",
42 | "tip1": "Instructions for use can be displayed here when pointing to the function on the right",
43 | "tip2": "Click the function on the right to insert it at the position of the cursor in the formula editor",
44 | "tip3": "Entering any characters in the formula editor will display a list of available variables/functions",
45 | "tip4": "Enter in the formula editor {entrance} a list of available categories will be displayed"
46 | },
47 | "debug": {
48 | "run": "Run",
49 | "result": "Result",
50 | "formula_error": "Please correct the formula error before running"
51 | },
52 | "fun_lib": {
53 | "inner": "built-in",
54 | "cate_common": "COMMON",
55 | "cate_calc": "CALC",
56 | "cate_txt": "TXT",
57 | "cate_time": "TIME",
58 | "cate_api": "API",
59 | "sum_label": "Sum",
60 | "sum_note": "Get the sum of a set of values.
Usage: sum(number 1, number 2,...)",
61 | "now_label": "Current time",
62 | "now_note": "Returns the current timestamp",
63 | "concat_label": "Merge text",
64 | "concat_note": "Combine multiple texts into one text.
Usage: concat(text1,text2,...)",
65 | "lower_label": "Convert to lowercase",
66 | "lower_note": "Convert all uppercase letters in a text to lowercase letters.
Usage: lower(text)",
67 | "upper_label": "Convert to uppercase",
68 | "upper_note": "Convert all lowercase letters in a text to uppercase letters.
Usage: upper(text)",
69 | "httpGet_label": "HTTP Get request",
70 | "httpGet_note": "Request an HTTP Get request and return Json format.
Usage: httpGet(https://httpbin.org/get)`",
71 | "httpGet_input1": "Request address"
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/test/executor.test.ts:
--------------------------------------------------------------------------------
1 | import { assert, describe, expect, it } from 'vitest'
2 | import { execute } from '../src/processes/executor'
3 | import { DEFAULT_FUN_LIB } from '../src/processes/funcLib'
4 | import type { Namespace, VarInfo } from '../src/processes/interface'
5 |
6 | describe('executor execute()', async () => {
7 | const testMaterialVars: Namespace[] = [
8 | {
9 | name: 'field',
10 | label: '字段',
11 | isVar: true,
12 | showLabel: true,
13 | items: [
14 | {
15 | name: 'age',
16 | label: '年龄',
17 | note: '年龄',
18 | } as VarInfo,
19 | ],
20 | },
21 | ]
22 |
23 | it('error by properties undefined', async () => {
24 | try {
25 | await execute(new Map(), '$.field.age', [], '$')
26 | }
27 | catch (e) {
28 | assert.equal(e.message, `Formula execution error:Cannot read properties of undefined (reading 'age')`)
29 | }
30 | })
31 |
32 | it('error by input parameter undefined', async () => {
33 | try {
34 | await execute(new Map(), '$.field.age', testMaterialVars, '$')
35 | }
36 | catch (e) {
37 | assert.equal(e.message, `Parameter [年龄] value does not exist`)
38 | }
39 | })
40 |
41 | it('mini var', async () => {
42 | const inputParams = new Map()
43 | inputParams.set('$.field.age', 18)
44 | expect(await execute(inputParams, '$.field.age', testMaterialVars, '$')).eq(18)
45 | })
46 |
47 | it('mini fun', async () => {
48 | expect(await execute(new Map(), '$.fun.sum(1,2,3,4)', [DEFAULT_FUN_LIB], '$')).eq(10)
49 | })
50 |
51 | it('mini var and fun', async () => {
52 | const inputParams = new Map()
53 | inputParams.set('$.field.age', 18)
54 | const materials = testMaterialVars
55 | materials.push(DEFAULT_FUN_LIB)
56 | expect(await execute(inputParams, '$.fun.sum(1,2,3,4,$.field.age)', materials, '$')).eq(28)
57 | })
58 |
59 | it('expression', async () => {
60 | const inputParams = new Map()
61 | inputParams.set('$.field.age', 18)
62 | const materials = testMaterialVars
63 | materials.push(DEFAULT_FUN_LIB)
64 | expect(await execute(inputParams, `$.fun.sum(1,2,3,4,$.field.age)>100?'字段1':'字段2'`, materials, '$')).eq('字段2')
65 | })
66 |
67 | it('async fun', async () => {
68 | expect(await execute(new Map(), `(await $.fun.httpGet('https://postman-echo.com/get')).url`, [DEFAULT_FUN_LIB], '$')).eq('https://postman-echo.com/get')
69 | })
70 |
71 | it('async fun2', async () => {
72 | expect(await execute(new Map(), `$.fun.concat((await $.fun.httpGet('https://postman-echo.com/get')).url)`, [DEFAULT_FUN_LIB], '$')).eq('https://postman-echo.com/get')
73 | })
74 |
75 | // $.fun.concat((await $.fun.httpGet('https://postman-echo.com/get')))
76 | it('complex', async () => {
77 | const inputParams = new Map()
78 | inputParams.set('$.field.age', 18)
79 | inputParams.set('$.model.addr', 'https://postman-echo.com/get')
80 | const materials = testMaterialVars
81 | materials.push(
82 | {
83 | name: 'model',
84 | label: '模型',
85 | isVar: true,
86 | showLabel: true,
87 | items: [
88 | {
89 | name: 'addr',
90 | label: '地址',
91 | } as VarInfo,
92 | ],
93 | },
94 | )
95 | materials.push(DEFAULT_FUN_LIB)
96 | expect(await execute(inputParams, `$.fun.concat($.fun.sum(1,$.field.age),3, true, ['1','2'], 'string',null,(await $.fun.httpGet($.model.addr)).url)`, materials, '$')).eq(`193true1,2stringhttps://postman-echo.com/get`)
97 | })
98 | })
99 |
--------------------------------------------------------------------------------
/src/components/Debug.vue:
--------------------------------------------------------------------------------
1 |
78 |
79 |
80 |
81 |
82 |
85 |
86 |
87 |
88 | {{ materialVar.nsLabel }}
89 |
90 |
91 |
92 |
{{ varInfo.label }}
93 |
94 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | {{ $t('debug.result') }}
105 |
106 |
107 | {{ debugResult }}
108 |
109 |
110 |
111 |
112 |
120 |
--------------------------------------------------------------------------------
/src/processes/executor.ts:
--------------------------------------------------------------------------------
1 | import locales from '../locales'
2 | import type { FunInfo, Namespace, VarInfo } from './interface'
3 |
4 | const { t } = locales.global
5 |
6 | /**
7 | * Formula execution engine
8 | *
9 | * @param inputParams input parameters
10 | * @param formulaValue the formula to be executed
11 | * @param materials materials used in the formula.
12 | * You need to ensure that the variables or functions used in the formula are defined,
13 | * and the variables in materials have corresponding input params values.
14 | * @param entrance entrance variable of the formula
15 | * @returns formula execution results
16 | *
17 | * @example
18 | * ```js
19 | * let inputParams = new Map()
20 | * inputParams.set('$.field.age', 18)
21 | * let formulaValue = `$.fun.concat($.fun.sum(1,$.field.age))`
22 | * let entrance = '$'
23 | * let materials = [
24 | * {
25 | * name: 'field',
26 | * label: '字段',
27 | * isVar: true,
28 | * items: [
29 | * {
30 | * name: 'applicant',
31 | * label: '申请人'
32 | * },
33 | * {
34 | * name: 'age',
35 | * label: '年龄'
36 | * }
37 | * ]
38 | * },
39 | * {
40 | * name: 'fun',
41 | * label: '内置',
42 | * isVar: false,
43 | * items: [
44 | * {
45 | * name: 'sum',
46 | * label: '求和',
47 | * body: `return Array.from(arguments).reduce((a, b) => a + b)`
48 | * },
49 | * {
50 | * name: 'concat',
51 | * label: '合并文本',
52 | * body: `return Array.from(arguments).join('')`
53 | * ]
54 | * }]
55 | * try{
56 | * let result = await execute(inputParams, formulaValue, materials, entrance)
57 | * }catch(e){
58 | * console.error(e)
59 | * throw e
60 | * }
61 | * ```
62 | */
63 | export async function execute(inputParams: Map, formulaValue: string, materials: Namespace[], entrance: string): Promise {
64 | const $ = packageEntrance(inputParams, materials, entrance)
65 | return await doExecute($, formulaValue)
66 | }
67 |
68 | /**
69 | * Convert to js object
70 | *
71 | * @param inputParams input parameters
72 | * @param materials materials used in the formula
73 | * @param entrance entrance variable of the formula
74 | * @returns js object
75 | *
76 | * @example
77 | * ```js
78 | * const $ = {
79 | * fun: {
80 | * concat: function () {
81 | * return Array.from(arguments).join('')
82 | * },
83 | * sum: function () {
84 | * return Array.from(arguments).reduce((a, b) => a + b)
85 | * }
86 | * },
87 | * field: {
88 | * age: 18
89 | * }
90 | * }
91 | * ```
92 | */
93 | function packageEntrance(inputParams: Map, materials: Namespace[], entrance: string): any {
94 | const $: any = {}
95 | materials.forEach((ns) => {
96 | if (!$[ns.name]) {
97 | // Add namespace
98 | $[ns.name] = {}
99 | }
100 | if (ns.isVar) {
101 | (ns.items as VarInfo[]).forEach((varInfo) => {
102 | const paramName = `${entrance}.${ns.name}.${varInfo.name}`
103 | // The value of the variable comes from the input and cannot be taken from the default value
104 | const paramValue = inputParams.has(paramName) ? inputParams.get(paramName) : undefined
105 | if (paramValue === undefined)
106 | throw new Error(t('executor.param_value_not_exist_error', { var: varInfo.label }))
107 |
108 | // Add variable name and corresponding value
109 | $[ns.name][varInfo.name!] = paramValue
110 | })
111 | }
112 | else {
113 | (ns.items as FunInfo[]).forEach((funInfo) => {
114 | const funcInputParam = funInfo.input ? funInfo.input.map(item => item.name) : ''
115 | if (funInfo.isAsync) {
116 | const AsyncFunction = Object.getPrototypeOf(async () => {
117 | }).constructor
118 | // Add async function
119 | $[ns.name][funInfo.name] = funcInputParam ? new AsyncFunction(...funcInputParam, funInfo.body) : new AsyncFunction(funInfo.body)
120 | }
121 | else {
122 | const SyncFunction = Object.getPrototypeOf(() => {
123 | }).constructor
124 | // Add sync function
125 | $[ns.name][funInfo.name] = funcInputParam ? new SyncFunction(...funcInputParam, funInfo.body) : new SyncFunction(funInfo.body)
126 | }
127 | })
128 | }
129 | })
130 | return $
131 | }
132 |
133 | /**
134 | * The real execution formula
135 | * @param $ The js object required by the formula to be called
136 | * @param formulaValue formula value
137 | * @returns execution results
138 | */
139 | export async function doExecute($: any, formulaValue: string): Promise {
140 | const AsyncFunction = Object.getPrototypeOf(async () => {
141 | }).constructor
142 | try {
143 | const asyncFn = new AsyncFunction('$', `return ${formulaValue}`)
144 | return await asyncFn($)
145 | }
146 | catch (e: any) {
147 | throw new Error(t('executor.execute_error') + e.message)
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/docs/EditorDemo.vue:
--------------------------------------------------------------------------------
1 |
148 |
149 |
150 |
151 | formulaValue:
152 | targetVar Label:
153 | materials[0].label:
154 | Result:checkPass:{{ checkPass }} formulaValue:{{ formulaValue }}
155 |
156 |
157 |
160 |
168 |
169 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | # Formula Editor
2 |
3 | **A relatively complete formula editor.**
4 |
5 | ## Features
6 |
7 | - Visual design
8 | - Relatively comprehensive error prompts
9 | - Auto-completion prompts
10 | - Customizable variables and synchronous/asynchronous functions
11 | - Online debugging
12 | - Dynamic formula execution engine
13 |
14 | ## Design
15 |
16 | 
17 |
18 | The formulas generated by the online editor fully comply with js syntax.
19 |
20 | In order to better display the formula, we abstract an object named `material`. This object can be a ``variable`` or ``function``. Users can customize `materials`. These ``materials`` will be displayed in the editor for easy viewing and use.
21 |
22 | The data structure of the ``variable`` and examples can be found in [VarInfo API](api/namespaces/iwInterface/interfaces/VarInfo.html)
23 |
24 | The data structure of the ``function`` and examples can be found in [FunInfo API](api/namespaces/iwInterface/interfaces/FunInfo.html)
25 |
26 | In order to enable the editor to render `material` correctly, we have limited its format. A `material` consists of `entry variable + namespace + material name`. The default `entry variable` is `$`. The following are legal material definitions:
27 |
28 | - Age field `$.field.age`
29 | - Applicant field `$.model.applicant` in the model
30 | - Addition function `$.fun.sum`
31 | - Get the current time function `$.fun.now`
32 | - Call xxAPI `$.api.xx`
33 |
34 | The data structure of the ``namespace`` and examples can be found in [Namespace API](api/namespaces/iwInterface/interfaces/Namespace.html)
35 |
36 | Therefore, a regular `formula` would be: `$.fun.sum($.field.age, 3)` .
37 |
38 | During actual execution, the execution engine will convert `material` and `formula` into JS code, such as:
39 |
40 | ```js
41 | const $ = {
42 | fun: {
43 | sum() {
44 | return Array.from(arguments).reduce((a, b) => a + b)
45 | },
46 | },
47 | field: {
48 | age: 18, // From default value or user input value
49 | },
50 | }
51 |
52 | const asyncFunction = Object.getPrototypeOf(async () => {}).constructor
53 | const asyncFn = new asyncFunction('$', `return ${formulaValue}`)
54 | return await asyncFn($)
55 | ```
56 |
57 | ## Setup
58 |
59 | This setup assumes your client app is created with Vite and vue-ts template.
60 |
61 | In your `package.json`, you shall have the dependencies compatible with the following:
62 |
63 | ```json
64 | "dependencies": {
65 | "@idealworld/formula-editor":"^1.0.0",
66 | "@element-plus/icons-vue": "^2.1.0",
67 | "element-plus": "^2.3.12",
68 | "vue": "^3.3.4"
69 | }
70 | ```
71 |
72 | In your `main.ts`, you shall import the libraries and CSS:
73 |
74 | ```ts
75 | import { createApp } from 'vue'
76 | import App from './App.vue'
77 | import ElementPlus from 'element-plus'
78 | import 'element-plus/dist/index.css'
79 | import FormulaEditor from ''@idealworld/formula-editor'
80 | import ''@idealworld/formula-editor/dist/style.css'
81 |
82 | createApp(App).use(ElementPlus).use(FormulaEditor).mount('#app')
83 | ```
84 |
85 | Import components from this library in your own component:
86 |
87 | ```html
88 |
177 |
178 |
179 | 打开
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 | ```
189 |
190 | For the complete parameters of ``iw-editor``, see: [EditorProps API](api/namespaces/iwInterface/interfaces/EditorProps.html)
191 |
--------------------------------------------------------------------------------
/src/processes/interface.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Variable type enum
3 | *
4 | * Used to specify the type of a variable, function parameter, or function return value
5 | */
6 | export enum VarKind {
7 | STRING = 'STRING',
8 | NUMBER = 'NUMBER',
9 | BOOLEAN = 'BOOLEAN',
10 | NULL = 'NULL',
11 | STRINGS = 'STRINGS',
12 | NUMBERS = 'NUMBERS',
13 | BOOLEANS = 'BOOLEANS',
14 | ANY = 'ANY',
15 | }
16 |
17 | /**
18 | * Editor configuration properties
19 | *
20 | */
21 | export interface EditorProps {
22 | /**
23 | * Target variable
24 | *
25 | * Used to specify the target object to which the current formula is applied. The editor will check whether the formula return type matches it.
26 | *
27 | * @example
28 | * ```
29 | * {
30 | * name: 'formName',
31 | * label: '表单标题',
32 | * kind: VarKind.STRING,
33 | * note: '表单的显示名称',
34 | * minLen: 2,
35 | * maxLen: 20
36 | * }
37 | * ```
38 | */
39 | targetVar: VarInfo
40 | /**
41 | * Material list
42 | *
43 | * Used to specify the variables and functions available in the current editor
44 | *
45 | * {@link Namespace}
46 | *
47 | * @example
48 | * ```
49 | * [
50 | * {
51 | * name: 'field',
52 | * label: '字段',
53 | * isVar: true,
54 | * showLabel: true,
55 | * showField: true,
56 | * color: '#f8e3c5',
57 | * items: [
58 | * {
59 | * name: 'applicant',
60 | * label: '申请人',
61 | * kind: VarKind.STRING,
62 | * note: '表单申请人姓名',
63 | * minLen: 2,
64 | * maxLen: 20,
65 | * cates: ['基础信息']
66 | * },
67 | * {
68 | * name: 'age',
69 | * label: '年龄',
70 | * kind: VarKind.NUMBER,
71 | * note: '年龄',
72 | * minLen: 18,
73 | * maxLen: 60,
74 | * defaultValue:36,
75 | * cates: ['基础信息']
76 | * }
77 | * ]
78 | * },
79 | * {
80 | * name: 'fun',
81 | * label: '函数',
82 | * isVar: false,
83 | * showLabel: false,
84 | * color: '#d9ecff',
85 | * items: [
86 | * {
87 | * name: 'now',
88 | * label: '当前时间',
89 | * note: `返回当前时间戳`,
90 | * input: [],
91 | * isVarLen: false,
92 | * isAsync: false,
93 | * output: {
94 | * kind: VarKind.NUMBER
95 | * },
96 | * body: `return Date.now()`,
97 | * cates: ['常用', '日期处理']
98 | * }
99 | * ]
100 | * }
101 | * ]
102 | * ```
103 | */
104 | materials: Namespace[]
105 | /**
106 | * Formula value
107 | *
108 | * @example
109 | * ```
110 | * $.fun.concat($.fun.sum(1,$.field.age),3, true, ['1','2'], 'string')
111 | * ```
112 | */
113 | formulaValue?: string
114 | /**
115 | * Entry variables for material items
116 | *
117 | * Default is `$`
118 | */
119 | entrance?: string
120 | /**
121 | * Whether to add a default function library
122 | */
123 | addDefaultFunLib?: boolean
124 | }
125 |
126 | /**
127 | * Material namespace
128 | *
129 | * Used to distinguish different materials, such as fields, functions, etc.
130 | */
131 | export interface Namespace {
132 | /**
133 | * Namespace name
134 | *
135 | * This field will serve as a unique identifier for the namespace
136 | */
137 | name: string
138 | /**
139 | * Namespace display name
140 | */
141 | label: string
142 | /**
143 | * Used to distinguish different namespaces by color
144 | */
145 | color?: string
146 | /**
147 | * Whether all items under this namespace are variables
148 | *
149 | * When it is true, it is a variable; when it is false, it is a function.
150 | */
151 | isVar: boolean
152 | /**
153 | * Whether to use display names in the formula editor
154 | *
155 | * Use {@link Namespace#label} when true, use {@link Namespace#name} when false
156 | */
157 | showLabel: boolean
158 | /**
159 | * Whether to display field in field management
160 | *
161 | * Use {@link Namespace#label} when true, use {@link Namespace#name} when false
162 | */
163 | showField?: boolean
164 | /**
165 | * All items in the namespace
166 | */
167 | items: VarInfo[] | FunInfo[]
168 | }
169 |
170 | /**
171 | * Variable qualification
172 | */
173 | export interface VarGuard {
174 | /**
175 | * Variable type
176 | */
177 | kind: VarKind
178 | /**
179 | * Minimum length (minimum value)
180 | */
181 | minLen?: number
182 | /**
183 | * Maximum length (maximum value)
184 | */
185 | maxLen?: number
186 | /**
187 | * Check regularity
188 | */
189 | checkReg?: string
190 | /**
191 | * Optional
192 | */
193 | options?: [{ value: any, label: string }]
194 | }
195 |
196 | /**
197 | * Variable information
198 | *
199 | *
200 | * @example
201 | * ```
202 | * {
203 | * name: 'age',
204 | * label: '年龄',
205 | * kind: VarKind.NUMBER,
206 | * note: '年龄',
207 | * minLen: 18,
208 | * maxLen: 60,
209 | * defaultValue:36,
210 | * cates: ['基础','隐私']
211 | * }
212 | * ```
213 | */
214 | export interface VarInfo extends VarGuard {
215 | /**
216 | * Variable name
217 | */
218 | name?: string
219 | /**
220 | * Variable display name
221 | */
222 | label?: string
223 | /**
224 | * Variable description
225 | */
226 | note?: string
227 | /**
228 | * Variable category
229 | *
230 | * One variable can correspond to multiple categories
231 | */
232 | cates?: string[]
233 | /**
234 | * Default value
235 | */
236 | defaultValue?: any
237 | }
238 |
239 | /**
240 | * Function information
241 | *
242 | * @example
243 | * ```
244 | * {
245 | * name: 'sum',
246 | * label: '求和',
247 | * note: `获取一组数值的总和。
用法:SUM(数字1,数字2,...)`,
248 | * input: [
249 | * {
250 | * kind: VarKind.NUMBER,
251 | * },
252 | * ],
253 | * isVarLen: true,
254 | * isAsync: false,
255 | * output: {
256 | * kind: VarKind.NUMBER,
257 | * },
258 | * body: `return Array.from(arguments).reduce((a, b) => a + b)`,
259 | * cates: ['常用', '计算'],
260 | * }
261 | * ```
262 | */
263 | export interface FunInfo {
264 | /**
265 | * Function name
266 | */
267 | name: string
268 | /**
269 | * Function display name
270 | */
271 | label: string
272 | /**
273 | * Function description
274 | *
275 | * Support html tags
276 | */
277 | note?: string
278 | /**
279 | * Function input parameters
280 | */
281 | input: VarInfo[]
282 | /**
283 | * Whether the parameter is of variable length
284 | *
285 | * The variable length type is the type of the last input
286 | */
287 | isVarLen: boolean
288 | /**
289 | * Whether it is an asynchronous function
290 | *
291 | * Asynchronous functions will be called in the formula in the form of `(await )`
292 | */
293 | isAsync: boolean
294 | /**
295 | * Function return qualification
296 | */
297 | output: VarGuard
298 | /**
299 | * Function body
300 | *
301 | * The function body is a JavaScript structure, which can be a single expression or a block statement
302 | * When it is a block-level statement, you need to use the `return` keyword to return the value
303 | * Function parameters are called using `arguments`
304 | */
305 | body: string
306 | /**
307 | * Function category
308 | *
309 | * One function can correspond to multiple categories
310 | */
311 | cates: string[]
312 | }
313 |
--------------------------------------------------------------------------------
/src/processes/cmEditor.ts:
--------------------------------------------------------------------------------
1 | import type { SyntaxNode } from '@lezer/common'
2 | import type { Diagnostic } from '@codemirror/lint'
3 | import type { EditorState } from '@codemirror/state'
4 | import locales from '../locales'
5 | import type { Namespace } from './interface'
6 | import { iwInterface } from './index'
7 |
8 | const { t } = locales.global
9 |
10 | /**
11 | * Add diagnostic information
12 | *
13 | * @param diagnostics diagnostic information collection
14 | * @param message error description
15 | * @param from The starting position of the error in the formula
16 | * @param to wrong ending group in formula
17 | */
18 | function addDiagnostic(diagnostics: Diagnostic[], message: string, from: number, to: number) {
19 | diagnostics.push({
20 | from,
21 | to,
22 | severity: 'error',
23 | message,
24 | markClass: 'iw-cm-wrap--error',
25 | })
26 | }
27 |
28 | /**
29 | * Diagnose errors in formulas
30 | *
31 | * @param currNode current node
32 | * @param state formula state
33 | * @param expectedOutputKind expected output type
34 | * @param diagnostics diagnostic collection
35 | * @param entrance material entrance variable name
36 | * @param diagnosedNode The start and end position of the diagnosed node
37 | * @param findMaterials Query material information
38 | * @param processHit material hit processing
39 | *
40 | * E.g.
41 | * code:
42 | * $.fun.concat($.fun.sum(1, $.field.age),3, true, ['1','2'], 'string', null, $.param.someVar)
43 | *
44 | * syntaxTree:
45 | * CallExpression(
46 | * MemberExpression(MemberExpression(VariableName, '.', PropertyName), '.', PropertyName),
47 | * ArgList(
48 | * '(',
49 | * CallExpression(
50 | * MemberExpression(MemberExpression(VariableName, '.', PropertyName), '.', PropertyName),
51 | * ArgList('(', Number, ',', MemberExpression(MemberExpression(VariableName, '.', PropertyName), '.', PropertyName), ')')
52 | * ),
53 | * ',',
54 | * Number,
55 | * ',',
56 | * BooleanLiteral,
57 | * ',',
58 | * ArrayExpression('[', String, ',', String, ']'),
59 | * ',',
60 | * String,
61 | * ',',
62 | * null,
63 | * ',',
64 | * MemberExpression(MemberExpression(VariableName, '.', PropertyName), '.', PropertyName),
65 | * ')'
66 | * )
67 | * )
68 | */
69 | export function diagnosticFormula(currNode: SyntaxNode, state: EditorState, expectedOutputKind: iwInterface.VarKind, diagnostics: Diagnostic[], entrance: string, diagnosedNode: [number, number][], findMaterials: (ns: string) => Namespace | undefined, processHit: (name: string) => void): void {
70 | // console.debug('#node:' + currNode + '#from:' + currNode.from + '#to:' + currNode.to + '#kind' + expectedOutputKind)
71 |
72 | /**
73 | * Exclude processed tokens
74 | */
75 | if (diagnosedNode.find((offset) => {
76 | const from = offset[0]
77 | const to = offset[1]
78 | return from <= currNode.from && to >= currNode.to
79 | }))
80 | return
81 |
82 | /**
83 | * ----------------------------------
84 | * Basic type processing
85 | * ----------------------------------
86 | */
87 | let paramKind = null
88 | switch (currNode.name) {
89 | case 'String': {
90 | paramKind = iwInterface.VarKind.STRING
91 | break
92 | }
93 | case 'Number': {
94 | paramKind = iwInterface.VarKind.NUMBER
95 | break
96 | }
97 | case 'BooleanLiteral': {
98 | paramKind = iwInterface.VarKind.BOOLEAN
99 | break
100 | }
101 | case 'null': {
102 | paramKind = iwInterface.VarKind.NULL
103 | break
104 | }
105 | case 'ArrayExpression': {
106 | // TODO
107 | paramKind = iwInterface.VarKind.STRINGS
108 | break
109 | }
110 | }
111 | if (paramKind != null) {
112 | if (expectedOutputKind !== paramKind && expectedOutputKind !== iwInterface.VarKind.ANY)
113 | addDiagnostic(diagnostics, t('diagnostic.format_error', { expect: t(`data_kind.${expectedOutputKind}`), real: t(`data_kind.${paramKind}`) }), currNode.from, currNode.to)
114 |
115 | diagnosedNode.push([currNode.from, currNode.to])
116 | return
117 | }
118 |
119 | /**
120 | * ----------------------------------
121 | * Parentheses expression processing
122 | * ----------------------------------
123 | */
124 | if (currNode.name === 'ParenthesizedExpression') {
125 | // (1+0+3)
126 | // ParenthesizedExpression("(",BinaryExpression(BinaryExpression(Number,ArithOp,Number),ArithOp,Number),")")
127 | const expr = currNode.firstChild?.nextSibling
128 | if (!expr) {
129 | addDiagnostic(diagnostics, t('diagnostic.bracket_error'), currNode.from, currNode.to)
130 | diagnosedNode.push([currNode.from, currNode.to])
131 | return
132 | }
133 | diagnosticFormula(expr, state, expectedOutputKind, diagnostics, entrance, diagnosedNode, findMaterials, processHit)
134 | return
135 | }
136 |
137 | /**
138 | * ----------------------------------
139 | * Binary expression processing
140 | * ----------------------------------
141 | */
142 | if (currNode.name === 'BinaryExpression') {
143 | // $.field.age>3
144 | // BinaryExpression(MemberExpression(MemberExpression(VariableName, ".", PropertyName), ".", PropertyName), CompareOp, Number)
145 | const left = currNode.firstChild
146 | const op = left?.nextSibling
147 | const right = op?.nextSibling
148 | if (!left || !op || !right) {
149 | addDiagnostic(diagnostics, t('diagnostic.binary_expression_error'), currNode.from, currNode.to)
150 | diagnosedNode.push([currNode.from, currNode.to])
151 | return
152 | }
153 | // Under comparison operators, the left and right sides can be of any type
154 | const expectedInputKind = op.name === 'CompareOp' ? iwInterface.VarKind.ANY : expectedOutputKind
155 | diagnosticFormula(left, state, expectedInputKind, diagnostics, entrance, diagnosedNode, findMaterials, processHit)
156 | diagnosticFormula(right, state, expectedInputKind, diagnostics, entrance, diagnosedNode, findMaterials, processHit)
157 | return
158 | }
159 |
160 | /**
161 | * ----------------------------------
162 | * Conditional expression processing
163 | * ----------------------------------
164 | */
165 | if (currNode.name === 'ConditionalExpression') {
166 | // false?'1':$.field.age>3?(1?'222':'333'):'22'
167 | // ConditionalExpression(
168 | // BooleanLiteral, LogicOp, String, LogicOp, ConditionalExpression(
169 | // BinaryExpression(MemberExpression(MemberExpression(VariableName, ".", PropertyName), ".", PropertyName), CompareOp, Number),
170 | // LogicOp,
171 | // ParenthesizedExpression("(", ConditionalExpression(
172 | // Number, LogicOp, String, LogicOp, String
173 | // ), ")"),
174 | // LogicOp,
175 | // String
176 | // )
177 | // )
178 | const cond = currNode.firstChild
179 | const trueResult = cond?.nextSibling?.nextSibling
180 | const falseResult = trueResult?.nextSibling?.nextSibling
181 | if (!cond || !trueResult || !falseResult) {
182 | addDiagnostic(diagnostics, t('diagnostic.conditional_expression_error'), currNode.from, currNode.to)
183 | diagnosedNode.push([currNode.from, currNode.to])
184 | return
185 | }
186 | diagnosticFormula(cond, state, iwInterface.VarKind.BOOLEAN, diagnostics, entrance, diagnosedNode, findMaterials, processHit)
187 | diagnosticFormula(trueResult, state, expectedOutputKind, diagnostics, entrance, diagnosedNode, findMaterials, processHit)
188 | diagnosticFormula(falseResult, state, expectedOutputKind, diagnostics, entrance, diagnosedNode, findMaterials, processHit)
189 | return
190 | }
191 |
192 | /**
193 | * ----------------------------------
194 | * Contains material token processing
195 | * ----------------------------------
196 | */
197 | let kind = null
198 | let memberName: string
199 | if (currNode.name === 'CallExpression' && currNode.firstChild?.name === 'MemberExpression' && currNode.firstChild?.firstChild?.name === 'MemberExpression' && currNode.firstChild?.nextSibling?.name === 'ArgList') {
200 | kind = 'fun'
201 | memberName = state.sliceDoc(currNode.firstChild?.from, currNode.firstChild?.to)
202 | }
203 | else if (currNode.name === 'MemberExpression' && currNode.firstChild?.name === 'MemberExpression') {
204 | kind = 'var'
205 | memberName = state.sliceDoc(currNode.from, currNode.to)
206 | }
207 | else if (currNode.name === 'VariableName') {
208 | // Custom variables are not supported
209 | addDiagnostic(diagnostics, t('diagnostic.var_fun_not_exist_error'), currNode.from, currNode.to)
210 | diagnosedNode.push([currNode.from, currNode.to])
211 | return
212 | }
213 | else {
214 | return
215 | }
216 |
217 | if (!memberName.startsWith(`${entrance}.`))
218 | return
219 |
220 | const memberNameSplit = memberName.split('.')
221 | const namespace = memberNameSplit[1]
222 | const name = memberNameSplit[2]
223 | const ns = findMaterials(namespace)
224 | if (!ns) {
225 | addDiagnostic(diagnostics, t('diagnostic.namespace_not_exist_error'), currNode.from, currNode.to)
226 | diagnosedNode.push([currNode.from, currNode.to])
227 | return
228 | }
229 |
230 | if (kind === 'var') {
231 | const varInfo = (ns.items as iwInterface.VarInfo[]).find(item => item.name === name)
232 | if (!ns.isVar || !varInfo) {
233 | addDiagnostic(diagnostics, t('diagnostic.var_not_exist_error'), currNode.from, currNode.to)
234 | diagnosedNode.push([currNode.from, currNode.to])
235 | return
236 | }
237 | if (varInfo.kind !== expectedOutputKind && expectedOutputKind !== iwInterface.VarKind.ANY) {
238 | diagnostics.push({
239 | from: currNode.from,
240 | to: currNode.to,
241 | severity: 'error',
242 | message: t('diagnostic.format_error', { expect: t(`data_kind.${expectedOutputKind}`), real: t(`data_kind.${varInfo.kind}`) }),
243 | markClass: 'iw-cm-wrap--error',
244 | })
245 | }
246 | }
247 | else {
248 | if (!currNode.firstChild?.nextSibling?.firstChild) {
249 | addDiagnostic(diagnostics, t('diagnostic.fun_format_error'), currNode.from, currNode.to)
250 | diagnosedNode.push([currNode.from, currNode.to])
251 | return
252 | }
253 | const funInfo = (ns.items as iwInterface.FunInfo[]).find(item => item.name === name)
254 | if (!funInfo) {
255 | addDiagnostic(diagnostics, t('diagnostic.fun_not_exist_error'), currNode.from, currNode.to)
256 | diagnosedNode.push([currNode.from, currNode.to])
257 | return
258 | }
259 |
260 | let tempParamNode = currNode.firstChild?.nextSibling?.firstChild
261 | const paramStartOffset = tempParamNode.nextSibling!.from - 1
262 | if (funInfo.output.kind !== expectedOutputKind && expectedOutputKind !== iwInterface.VarKind.ANY && funInfo.output.kind !== iwInterface.VarKind.ANY) {
263 | // Only mark the method body. If it is an asynchronous function, you need to subtract the length of `await`
264 | addDiagnostic(diagnostics, t('diagnostic.format_error', { expect: t(`data_kind.${expectedOutputKind}`), real: t(`data_kind.${funInfo.output.kind}`) }), currNode.from + (funInfo.isAsync ? -6 : 0), paramStartOffset)
265 | }
266 |
267 | // Function parameter checking
268 | let paramIdx = 0
269 | let realParamLen = 0
270 | while (tempParamNode.nextSibling != null) {
271 | tempParamNode = tempParamNode.nextSibling!
272 | const paramName = tempParamNode.name
273 | if (paramName === '(' || paramName === ')' || paramName === ',')
274 | continue
275 |
276 | if (funInfo.input.length <= paramIdx) {
277 | // Only mark parameters that are too long
278 | addDiagnostic(diagnostics, t('diagnostic.param_length_error', { expect: funInfo.input.length, real: (paramIdx + 1) }), tempParamNode.from, currNode.to - 1)
279 | break
280 | }
281 | const tempInputKind = funInfo.input[paramIdx].kind
282 | tempParamNode.cursor()
283 | .iterate((node) => {
284 | diagnosticFormula(node.node, state, tempInputKind, diagnostics, entrance, diagnosedNode, findMaterials, processHit)
285 | })
286 | if (funInfo.input.length - 1 !== paramIdx || !funInfo.isVarLen) {
287 | // This judgment is used to ensure that when the last parameter is a variable-length parameter, it will not be included in the actual parameter length.
288 | paramIdx++
289 | }
290 | realParamLen++
291 | }
292 | if (paramIdx === 0 && funInfo.input.length !== 0 && !funInfo.isVarLen) {
293 | // Only mark ()
294 | addDiagnostic(diagnostics, t('diagnostic.param_length_error', { expect: funInfo.input.length, real: 0 }), paramStartOffset, currNode.to)
295 | }
296 | if (realParamLen === 0 && funInfo.isVarLen) {
297 | // Only mark functions
298 | addDiagnostic(diagnostics, t('diagnostic.param_not_exist_error'), currNode.from, paramStartOffset)
299 | }
300 | }
301 | // Process hit
302 | processHit(`${entrance}.${namespace}.${name}`)
303 | diagnosedNode.push([currNode.from, currNode.to])
304 | }
305 |
--------------------------------------------------------------------------------
/src/components/CmWrap.vue:
--------------------------------------------------------------------------------
1 |
367 |
368 |
369 |
370 |
371 |
372 |
403 |
--------------------------------------------------------------------------------
/test/cmEditor.test.ts:
--------------------------------------------------------------------------------
1 | import { assert, describe, it } from 'vitest'
2 | import { syntaxTree } from '@codemirror/language'
3 | import type { Diagnostic } from '@codemirror/lint'
4 | import { EditorState } from '@codemirror/state'
5 | import { javascriptLanguage } from '@codemirror/lang-javascript'
6 | import { VarKind } from '../src/processes/interface'
7 | import type { Namespace, VarInfo } from '../src/processes/interface'
8 | import { diagnosticFormula } from '../src/processes/cmEditor'
9 | import { DEFAULT_FUN_LIB } from '../src/processes/funcLib'
10 | import type { iwInterface } from '../src'
11 |
12 | describe('cmEditor verify', () => {
13 | const testMaterialVars: Namespace[] = [
14 | {
15 | name: 'field',
16 | label: '字段',
17 | isVar: true,
18 | showLabel: true,
19 | items: [
20 | {
21 | name: 'age',
22 | label: '年龄',
23 | note: '年龄',
24 | kind: VarKind.NUMBER,
25 | } as VarInfo,
26 | ],
27 | },
28 | ]
29 |
30 | const miniMaterials = testMaterialVars
31 | miniMaterials.push(DEFAULT_FUN_LIB)
32 |
33 | function verify(formula: string, materials: Namespace[], expectedOutputKind: iwInterface.VarKind) {
34 | const state = EditorState.create({
35 | doc: formula,
36 | extensions: [javascriptLanguage.extension],
37 | })
38 | const diagnostics: Diagnostic[] = []
39 | const verifiedNode: [number, number][] = []
40 | const usedMaterials: string[] = []
41 | syntaxTree(state)
42 | .topNode.cursor()
43 | .iterate((node) => {
44 | diagnosticFormula(node.node, state, expectedOutputKind, diagnostics, '$', verifiedNode, namespace => materials.find(ns => ns.name === namespace), (name) => {
45 | if (!usedMaterials.includes(name))
46 | usedMaterials.push(name)
47 | })
48 | })
49 | return [diagnostics, usedMaterials]
50 | }
51 |
52 | function expect(message: string, from: number, to: number): Diagnostic {
53 | return {
54 | from,
55 | to,
56 | severity: 'error',
57 | message,
58 | markClass: 'iw-cm-wrap--error',
59 | }
60 | }
61 |
62 | function expects(...diagnostics: [string, number, number][]): Diagnostic[] {
63 | return diagnostics.map(d => expect(d[0], d[1], d[2]))
64 | }
65 |
66 | it('empty', () => {
67 | assert.deepEqual(verify('', [], VarKind.STRING)[0], [])
68 | })
69 |
70 | it('only number', () => {
71 | assert.deepEqual(verify('1', [], VarKind.NUMBER)[0], [])
72 | })
73 |
74 | it('only string', () => {
75 | assert.deepEqual(verify(`'gudaoxuri'`, [], VarKind.STRING)[0], [])
76 | })
77 |
78 | it('unknown variable/function', () => {
79 | assert.deepEqual(verify(`hi`, [], VarKind.STRING)[0], expects(['Variable/function does not exist', 0, 2]))
80 | assert.deepEqual(verify(`hi()`, [], VarKind.STRING)[0], expects(['Variable/function does not exist', 0, 2]))
81 | assert.deepEqual(verify(`$.field.hi`, [], VarKind.STRING)[0], expects(['Namespace does not exist', 0, 10]))
82 | assert.deepEqual(verify(`$.fun.hi()`, [], VarKind.STRING)[0], expects(['Namespace does not exist', 0, 10]))
83 | assert.deepEqual(verify(`$.field.hi`, testMaterialVars, VarKind.STRING)[0], expects(['Variable does not exist', 0, 10]))
84 | assert.deepEqual(verify(`$.fun.hi()`, [DEFAULT_FUN_LIB], VarKind.STRING)[0], expects(['Function does not exist', 0, 10]))
85 | })
86 |
87 | it('return data type', () => {
88 | assert.deepEqual(verify('1', [], VarKind.STRING)[0], expects(['The expected format is [string], the actual format is [numeric]', 0, 1]))
89 | assert.deepEqual(verify('true', [], VarKind.STRING)[0], expects(['The expected format is [string], the actual format is [boolean]', 0, 4]))
90 | assert.deepEqual(verify(`'hi'`, [], VarKind.NUMBER)[0], expects(['The expected format is [numeric], the actual format is [string]', 0, 4]))
91 | assert.deepEqual(verify(`'hi'+'hi'`, [], VarKind.NUMBER)[0], expects(
92 | ['The expected format is [numeric], the actual format is [string]', 0, 4],
93 | ['The expected format is [numeric], the actual format is [string]', 5, 9],
94 | ))
95 | assert.deepEqual(verify(`$.field.age`, testMaterialVars, VarKind.STRING)[0], expects(['The expected format is [string], the actual format is [numeric]', 0, 11]))
96 | assert.deepEqual(verify(`$.fun.sum(1,2)`, [DEFAULT_FUN_LIB], VarKind.STRING)[0], expects(['The expected format is [string], the actual format is [numeric]', 0, 9]))
97 | assert.deepEqual(verify(`$.fun.sum(1,$.fun.lower('1'))`, [DEFAULT_FUN_LIB], VarKind.STRING)[0], expects(
98 | ['The expected format is [string], the actual format is [numeric]', 0, 9],
99 | ['The expected format is [numeric], the actual format is [string]', 12, 23],
100 | ))
101 | assert.deepEqual(verify(`$.fun.sum(1,$.fun.sum($.fun.lower('1')))`, [DEFAULT_FUN_LIB], VarKind.STRING)[0], expects(
102 | ['The expected format is [string], the actual format is [numeric]', 0, 9],
103 | ['The expected format is [numeric], the actual format is [string]', 22, 33],
104 | ))
105 | assert.deepEqual(verify(`$.fun.sum($.field.age,$.fun.sum($.fun.lower('1')))+$.field.age`, miniMaterials, VarKind.STRING)[0], expects(
106 | ['The expected format is [string], the actual format is [numeric]', 0, 9],
107 | ['The expected format is [numeric], the actual format is [string]', 32, 43],
108 | ['The expected format is [string], the actual format is [numeric]', 51, 62],
109 | ))
110 | assert.deepEqual(verify(`$.field.age+$.fun.sum($.field.age,$.fun.sum($.fun.lower('1')))`, miniMaterials, VarKind.STRING)[0], expects(
111 | ['The expected format is [string], the actual format is [numeric]', 0, 11],
112 | ['The expected format is [string], the actual format is [numeric]', 12, 21],
113 | ['The expected format is [numeric], the actual format is [string]', 44, 55],
114 | ))
115 | assert.deepEqual(verify(`1`, miniMaterials, VarKind.NUMBER)[0], [])
116 | assert.deepEqual(verify(`true`, miniMaterials, VarKind.BOOLEAN)[0], [])
117 | assert.deepEqual(verify(`'hi'`, miniMaterials, VarKind.STRING)[0], [])
118 | assert.deepEqual(verify(`'hi'`, miniMaterials, VarKind.ANY)[0], [])
119 | assert.deepEqual(verify(`'hi'+'hi'`, miniMaterials, VarKind.STRING)[0], [])
120 | assert.deepEqual(verify(`$.fun.sum(1,2)`, miniMaterials, VarKind.ANY)[0], [])
121 | assert.deepEqual(verify(`$.fun.sum(1,2)`, miniMaterials, VarKind.NUMBER)[0], [])
122 | assert.deepEqual(verify(`$.fun.concat(1,2)`, miniMaterials, VarKind.STRING)[0], [])
123 | assert.deepEqual(verify(`$.fun.concat($.field.age,$.fun.sum(1,2))`, miniMaterials, VarKind.STRING)[0], [])
124 | assert.deepEqual(verify(`$.fun.sum($.field.age,$.fun.sum(1,$.fun.sum(1,2)))`, miniMaterials, VarKind.NUMBER)[0], [])
125 | assert.deepEqual(verify(`$.fun.sum(1,2)+100`, miniMaterials, VarKind.ANY)[0], [])
126 | assert.deepEqual(verify(`$.fun.sum(1,2)+100`, miniMaterials, VarKind.NUMBER)[0], [])
127 | assert.deepEqual(verify(`$.fun.concat(1,2)+'hi'`, miniMaterials, VarKind.STRING)[0], [])
128 | assert.deepEqual(verify(`$.fun.concat($.field.age,$.fun.sum(1,2))+'hi'`, miniMaterials, VarKind.STRING)[0], [])
129 | assert.deepEqual(verify(`$.fun.sum($.field.age,$.fun.sum(1,$.fun.sum(1,2)))-100`, miniMaterials, VarKind.NUMBER)[0], [])
130 | })
131 |
132 | it('parameter', () => {
133 | assert.deepEqual(verify(`$.fun.sum(1,'2')`, miniMaterials, VarKind.NUMBER)[0], expects(['The expected format is [numeric], the actual format is [string]', 12, 15]))
134 | assert.deepEqual(verify(`$.fun.lower('2','2')`, miniMaterials, VarKind.STRING)[0], expects(['The expected parameter length is [1], the actual length is [2]', 16, 19]))
135 | assert.deepEqual(verify(`$.fun.lower(2)`, miniMaterials, VarKind.STRING)[0], expects(['The expected format is [string], the actual format is [numeric]', 12, 13]))
136 | assert.deepEqual(verify(`$.fun.lower()`, miniMaterials, VarKind.STRING)[0], expects(['The expected parameter length is [1], the actual length is [0]', 11, 13]))
137 | assert.deepEqual(verify(`$.fun.sum(1,$.fun.sum($.field.age,'2'))`, miniMaterials, VarKind.NUMBER)[0], expects(['The expected format is [numeric], the actual format is [string]', 34, 37]))
138 | assert.deepEqual(verify(`1+$.fun.sum(1,$.fun.sum($.field.age,'2'))`, miniMaterials, VarKind.NUMBER)[0], expects(['The expected format is [numeric], the actual format is [string]', 36, 39]))
139 | assert.deepEqual(verify(`$.fun.sum(1,$.fun.sum($.field.age,'2'))+1`, miniMaterials, VarKind.NUMBER)[0], expects(['The expected format is [numeric], the actual format is [string]', 34, 37]))
140 | assert.deepEqual(verify(`$.fun.sum(1,$.fun.sum($.field.age,'2',$.fun.lower(2)))+1`, miniMaterials, VarKind.NUMBER)[0], expects(
141 | ['The expected format is [numeric], the actual format is [string]', 34, 37],
142 | ['The expected format is [numeric], the actual format is [string]', 38, 49],
143 | ['The expected format is [string], the actual format is [numeric]', 50, 51],
144 | ))
145 | assert.deepEqual(verify(`$.fun.sum(1,$.fun.sum($.field.age,'2',$.fun.lower($.fun.lower())))+1`, miniMaterials, VarKind.NUMBER)[0], expects(
146 | ['The expected format is [numeric], the actual format is [string]', 34, 37],
147 | ['The expected format is [numeric], the actual format is [string]', 38, 49],
148 | ['The expected parameter length is [1], the actual length is [0]', 61, 63],
149 | ))
150 | assert.deepEqual(verify(`$.fun.concat(1,'2')`, [DEFAULT_FUN_LIB], VarKind.STRING)[0], [])
151 | assert.deepEqual(verify(`$.fun.concat($.fun.sum(12))`, [DEFAULT_FUN_LIB], VarKind.STRING)[0], [])
152 | assert.deepEqual(verify(`$.fun.concat($.fun.sum(12))+'hi'`, [DEFAULT_FUN_LIB], VarKind.STRING)[0], [])
153 | assert.deepEqual(verify(`'hi'+$.fun.concat($.fun.sum(12))`, [DEFAULT_FUN_LIB], VarKind.STRING)[0], [])
154 | })
155 |
156 | it('expression', () => {
157 | assert.deepEqual(verify(`$.fun.sum(1,2)>0?'':''`, miniMaterials, VarKind.NUMBER)[0], expects(
158 | ['The expected format is [numeric], the actual format is [string]', 17, 19],
159 | ['The expected format is [numeric], the actual format is [string]', 20, 22],
160 | ))
161 | assert.deepEqual(verify(`$.fun.sum($.fun.sum(1,2)>0?'':'',2)>0?'':''`, miniMaterials, VarKind.STRING)[0], expects(
162 | ['The expected format is [numeric], the actual format is [string]', 27, 29],
163 | ['The expected format is [numeric], the actual format is [string]', 30, 32],
164 | ))
165 | assert.deepEqual(verify(`false?1:$.field.age>'3'?(1?'222':333):'22'`, miniMaterials, VarKind.STRING)[0], expects(
166 | ['The expected format is [string], the actual format is [numeric]', 6, 7],
167 | ['The expected format is [boolean], the actual format is [numeric]', 25, 26],
168 | ['The expected format is [string], the actual format is [numeric]', 33, 36],
169 | ))
170 | assert.deepEqual(verify(`false?'1':$.field.age>3?($.fun.sum(1,'2')>0?'222':'333'):'22'`, miniMaterials, VarKind.STRING)[0], expects(
171 | ['The expected format is [numeric], the actual format is [string]', 37, 40],
172 | ))
173 | assert.deepEqual(verify(`false?'1':$.field.age>3?($.fun.sum(1,2)?'222':'333'):'22'`, miniMaterials, VarKind.STRING)[0], expects(
174 | ['The expected format is [boolean], the actual format is [numeric]', 25, 34],
175 | ))
176 |
177 | assert.deepEqual(verify(`'1'&&'2'`, miniMaterials, VarKind.NUMBER)[0], expects(
178 | ['The expected format is [numeric], the actual format is [string]', 0, 3],
179 | ['The expected format is [numeric], the actual format is [string]', 5, 8],
180 | ))
181 |
182 | assert.deepEqual(verify(`'1'&&'2'`, miniMaterials, VarKind.STRING)[0], [])
183 | assert.deepEqual(verify(`(1+0)>2?'222':'343'`, miniMaterials, VarKind.STRING)[0], [])
184 | assert.deepEqual(verify(`$.fun.sum(1,2)>0?'':''`, miniMaterials, VarKind.STRING)[0], [])
185 | assert.deepEqual(verify(`$.fun.sum($.fun.sum(1,2)>0?3:4,2)>0?'':''`, miniMaterials, VarKind.STRING)[0], [])
186 | // ConditionalExpression(BooleanLiteral,LogicOp,String,LogicOp,ConditionalExpression(BinaryExpression(MemberExpression(MemberExpression(VariableName,".",PropertyName),".",PropertyName),CompareOp,Number),LogicOp,ParenthesizedExpression("(",ConditionalExpression(Number,LogicOp,String,LogicOp,String),")"),LogicOp,String))
187 | assert.deepEqual(verify(`false?'1':$.field.age>3?($.fun.sum(1,2)>0?'222':'333'):'22'`, miniMaterials, VarKind.STRING)[0], [])
188 | })
189 |
190 | it('complex', () => {
191 | assert.deepEqual(verify(`$.fun.sum(($.fun.sum(1,$.fun.sum($.field.age,'2',$.fun.lower($.fun.lower(2,2))))+1)>0?'':'',2)>0?'':''`, miniMaterials, VarKind.STRING)[0], expects(
192 | ['The expected format is [numeric], the actual format is [string]', 45, 48],
193 | ['The expected format is [numeric], the actual format is [string]', 49, 60],
194 | ['The expected format is [string], the actual format is [numeric]', 73, 74],
195 | ['The expected format is [string], the actual format is [numeric]', 75, 76],
196 | ['The expected parameter length is [1], the actual length is [2]', 75, 76],
197 | ['The expected format is [numeric], the actual format is [string]', 86, 88],
198 | ['The expected format is [numeric], the actual format is [string]', 89, 91],
199 | ))
200 | assert.deepEqual(verify(`$.fun.sum(($.fun.sum(1,$.fun.sum($.field.age,'2',$.fun.lower($.fun.lower('hi'))==='hi'?0:1))+1)>0?'':'',2)>0?'':(await $.fun.httpGet('xxx')).code`, miniMaterials, VarKind.STRING)[0], expects(
201 | ['The expected format is [numeric], the actual format is [string]', 45, 48],
202 | ['The expected format is [numeric], the actual format is [string]', 98, 100],
203 | ['The expected format is [numeric], the actual format is [string]', 101, 103],
204 | ))
205 | assert.deepEqual(verify(`$.fun.sum(($.fun.sum(1,$.fun.sum($.field.age,2,$.fun.lower($.fun.lower('hi'))==='hi'?0:1))+1)>0?11:222,2)>0?'':(await $.fun.httpGet('xxx')).code`, miniMaterials, VarKind.STRING)[0], [])
206 | })
207 |
208 | it('used materials', () => {
209 | assert.deepEqual(verify(`$.fun.sum(($.fun.sum(1,$.fun.sum($.field.age,2,$.fun.lower($.fun.lower('hi'))==='hi'?0:1))+1)>0?11:222,2)>0?'':(await $.fun.httpGet('xxx')).code`, miniMaterials, VarKind.STRING)[1], [
210 | '$.field.age',
211 | '$.fun.lower',
212 | '$.fun.sum',
213 | '$.fun.httpGet',
214 | ])
215 | })
216 | })
217 |
--------------------------------------------------------------------------------
/src/components/Editor.vue:
--------------------------------------------------------------------------------
1 |
304 |
305 |
306 |
307 |
308 |
309 |
310 | {{ props.targetVar.label }}
311 |
312 |
313 |
316 |
317 |
318 |
319 |
323 |
324 |
325 |
326 |
331 |
332 |
340 |
341 |
342 |
343 |
344 |
349 |
350 |
358 |
359 |
360 |
361 |
362 |
363 |
364 | {{ $t('editor.tips') }}
365 | {{ $t('editor.tip1') }}
366 | {{ $t('editor.tip2') }}
367 | {{ $t('editor.tip3') }}
368 |
369 |
370 |
371 |
372 |
373 |
374 |
378 |
382 |
383 |
384 |
385 |
386 |
412 |
--------------------------------------------------------------------------------
/example/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '6.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | dependencies:
8 | '@element-plus/icons-vue':
9 | specifier: ^2.3.1
10 | version: 2.3.1(vue@3.3.11)
11 | '@idealworld/formula-editor':
12 | specifier: file:../
13 | version: file:..(vue@3.3.11)
14 | element-plus:
15 | specifier: ^2.4.3
16 | version: 2.4.3(vue@3.3.11)
17 | vue:
18 | specifier: ^3.3.11
19 | version: 3.3.11(typescript@5.3.3)
20 |
21 | devDependencies:
22 | '@tsconfig/node18':
23 | specifier: ^18.2.2
24 | version: 18.2.2
25 | '@types/node':
26 | specifier: ^20.10.4
27 | version: 20.10.4
28 | '@vitejs/plugin-vue':
29 | specifier: ^4.5.2
30 | version: 4.5.2(vite@5.0.8)(vue@3.3.11)
31 | '@vue/tsconfig':
32 | specifier: ^0.4.0
33 | version: 0.4.0
34 | npm-run-all2:
35 | specifier: ^6.1.1
36 | version: 6.1.1
37 | typescript:
38 | specifier: ^5.3.3
39 | version: 5.3.3
40 | vite:
41 | specifier: ^5.0.8
42 | version: 5.0.8(@types/node@20.10.4)
43 | vue-tsc:
44 | specifier: ^1.8.25
45 | version: 1.8.25(typescript@5.3.3)
46 |
47 | packages:
48 |
49 | /@babel/code-frame@7.23.5:
50 | resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
51 | engines: {node: '>=6.9.0'}
52 | dependencies:
53 | '@babel/highlight': 7.23.4
54 | chalk: 2.4.2
55 | dev: true
56 |
57 | /@babel/helper-string-parser@7.23.4:
58 | resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
59 | engines: {node: '>=6.9.0'}
60 |
61 | /@babel/helper-validator-identifier@7.22.20:
62 | resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
63 | engines: {node: '>=6.9.0'}
64 |
65 | /@babel/highlight@7.23.4:
66 | resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
67 | engines: {node: '>=6.9.0'}
68 | dependencies:
69 | '@babel/helper-validator-identifier': 7.22.20
70 | chalk: 2.4.2
71 | js-tokens: 4.0.0
72 | dev: true
73 |
74 | /@babel/parser@7.23.6:
75 | resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==}
76 | engines: {node: '>=6.0.0'}
77 | hasBin: true
78 | dependencies:
79 | '@babel/types': 7.23.6
80 |
81 | /@babel/types@7.23.6:
82 | resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==}
83 | engines: {node: '>=6.9.0'}
84 | dependencies:
85 | '@babel/helper-string-parser': 7.23.4
86 | '@babel/helper-validator-identifier': 7.22.20
87 | to-fast-properties: 2.0.0
88 |
89 | /@codemirror/autocomplete@6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.3)(@codemirror/view@6.22.3)(@lezer/common@1.1.2):
90 | resolution: {integrity: sha512-L5UInv8Ffd6BPw0P3EF7JLYAMeEbclY7+6Q11REt8vhih8RuLreKtPy/xk8wPxs4EQgYqzI7cdgpiYwWlbS/ow==}
91 | peerDependencies:
92 | '@codemirror/language': ^6.0.0
93 | '@codemirror/state': ^6.0.0
94 | '@codemirror/view': ^6.0.0
95 | '@lezer/common': ^1.0.0
96 | dependencies:
97 | '@codemirror/language': 6.9.3
98 | '@codemirror/state': 6.3.3
99 | '@codemirror/view': 6.22.3
100 | '@lezer/common': 1.1.2
101 | dev: false
102 |
103 | /@codemirror/commands@6.3.2:
104 | resolution: {integrity: sha512-tjoi4MCWDNxgIpoLZ7+tezdS9OEB6pkiDKhfKx9ReJ/XBcs2G2RXIu+/FxXBlWsPTsz6C9q/r4gjzrsxpcnqCQ==}
105 | dependencies:
106 | '@codemirror/language': 6.9.3
107 | '@codemirror/state': 6.3.3
108 | '@codemirror/view': 6.22.3
109 | '@lezer/common': 1.1.2
110 | dev: false
111 |
112 | /@codemirror/lang-javascript@6.2.1:
113 | resolution: {integrity: sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==}
114 | dependencies:
115 | '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.3)(@codemirror/view@6.22.3)(@lezer/common@1.1.2)
116 | '@codemirror/language': 6.9.3
117 | '@codemirror/lint': 6.4.2
118 | '@codemirror/state': 6.3.3
119 | '@codemirror/view': 6.22.3
120 | '@lezer/common': 1.1.2
121 | '@lezer/javascript': 1.4.10
122 | dev: false
123 |
124 | /@codemirror/language@6.9.3:
125 | resolution: {integrity: sha512-qq48pYzoi6ldYWV/52+Z9Ou6QouVI+8YwvxFbUypI33NbjG2UeRHKENRyhwljTTiOqjQ33FjyZj6EREQ9apAOQ==}
126 | dependencies:
127 | '@codemirror/state': 6.3.3
128 | '@codemirror/view': 6.22.3
129 | '@lezer/common': 1.1.2
130 | '@lezer/highlight': 1.2.0
131 | '@lezer/lr': 1.3.14
132 | style-mod: 4.1.0
133 | dev: false
134 |
135 | /@codemirror/lint@6.4.2:
136 | resolution: {integrity: sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==}
137 | dependencies:
138 | '@codemirror/state': 6.3.3
139 | '@codemirror/view': 6.22.3
140 | crelt: 1.0.6
141 | dev: false
142 |
143 | /@codemirror/search@6.5.5:
144 | resolution: {integrity: sha512-PIEN3Ke1buPod2EHbJsoQwlbpkz30qGZKcnmH1eihq9+bPQx8gelauUwLYaY4vBOuBAuEhmpDLii4rj/uO0yMA==}
145 | dependencies:
146 | '@codemirror/state': 6.3.3
147 | '@codemirror/view': 6.22.3
148 | crelt: 1.0.6
149 | dev: false
150 |
151 | /@codemirror/state@6.3.3:
152 | resolution: {integrity: sha512-0wufKcTw2dEwEaADajjHf6hBy1sh3M6V0e+q4JKIhLuiMSe5td5HOWpUdvKth1fT1M9VYOboajoBHpkCd7PG7A==}
153 | dev: false
154 |
155 | /@codemirror/view@6.22.3:
156 | resolution: {integrity: sha512-rqnq+Zospwoi3x1vZ8BGV1MlRsaGljX+6qiGYmIpJ++M+LCC+wjfDaPklhwpWSgv7pr/qx29KiAKQBH5+DOn4w==}
157 | dependencies:
158 | '@codemirror/state': 6.3.3
159 | style-mod: 4.1.0
160 | w3c-keyname: 2.2.8
161 | dev: false
162 |
163 | /@ctrl/tinycolor@3.6.1:
164 | resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==}
165 | engines: {node: '>=10'}
166 | dev: false
167 |
168 | /@element-plus/icons-vue@2.3.1(vue@3.3.11):
169 | resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==}
170 | peerDependencies:
171 | vue: ^3.2.0
172 | dependencies:
173 | vue: 3.3.11(typescript@5.3.3)
174 | dev: false
175 |
176 | /@esbuild/android-arm64@0.19.9:
177 | resolution: {integrity: sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==}
178 | engines: {node: '>=12'}
179 | cpu: [arm64]
180 | os: [android]
181 | requiresBuild: true
182 | dev: true
183 | optional: true
184 |
185 | /@esbuild/android-arm@0.19.9:
186 | resolution: {integrity: sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==}
187 | engines: {node: '>=12'}
188 | cpu: [arm]
189 | os: [android]
190 | requiresBuild: true
191 | dev: true
192 | optional: true
193 |
194 | /@esbuild/android-x64@0.19.9:
195 | resolution: {integrity: sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==}
196 | engines: {node: '>=12'}
197 | cpu: [x64]
198 | os: [android]
199 | requiresBuild: true
200 | dev: true
201 | optional: true
202 |
203 | /@esbuild/darwin-arm64@0.19.9:
204 | resolution: {integrity: sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==}
205 | engines: {node: '>=12'}
206 | cpu: [arm64]
207 | os: [darwin]
208 | requiresBuild: true
209 | dev: true
210 | optional: true
211 |
212 | /@esbuild/darwin-x64@0.19.9:
213 | resolution: {integrity: sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==}
214 | engines: {node: '>=12'}
215 | cpu: [x64]
216 | os: [darwin]
217 | requiresBuild: true
218 | dev: true
219 | optional: true
220 |
221 | /@esbuild/freebsd-arm64@0.19.9:
222 | resolution: {integrity: sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==}
223 | engines: {node: '>=12'}
224 | cpu: [arm64]
225 | os: [freebsd]
226 | requiresBuild: true
227 | dev: true
228 | optional: true
229 |
230 | /@esbuild/freebsd-x64@0.19.9:
231 | resolution: {integrity: sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==}
232 | engines: {node: '>=12'}
233 | cpu: [x64]
234 | os: [freebsd]
235 | requiresBuild: true
236 | dev: true
237 | optional: true
238 |
239 | /@esbuild/linux-arm64@0.19.9:
240 | resolution: {integrity: sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==}
241 | engines: {node: '>=12'}
242 | cpu: [arm64]
243 | os: [linux]
244 | requiresBuild: true
245 | dev: true
246 | optional: true
247 |
248 | /@esbuild/linux-arm@0.19.9:
249 | resolution: {integrity: sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==}
250 | engines: {node: '>=12'}
251 | cpu: [arm]
252 | os: [linux]
253 | requiresBuild: true
254 | dev: true
255 | optional: true
256 |
257 | /@esbuild/linux-ia32@0.19.9:
258 | resolution: {integrity: sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==}
259 | engines: {node: '>=12'}
260 | cpu: [ia32]
261 | os: [linux]
262 | requiresBuild: true
263 | dev: true
264 | optional: true
265 |
266 | /@esbuild/linux-loong64@0.19.9:
267 | resolution: {integrity: sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==}
268 | engines: {node: '>=12'}
269 | cpu: [loong64]
270 | os: [linux]
271 | requiresBuild: true
272 | dev: true
273 | optional: true
274 |
275 | /@esbuild/linux-mips64el@0.19.9:
276 | resolution: {integrity: sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==}
277 | engines: {node: '>=12'}
278 | cpu: [mips64el]
279 | os: [linux]
280 | requiresBuild: true
281 | dev: true
282 | optional: true
283 |
284 | /@esbuild/linux-ppc64@0.19.9:
285 | resolution: {integrity: sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==}
286 | engines: {node: '>=12'}
287 | cpu: [ppc64]
288 | os: [linux]
289 | requiresBuild: true
290 | dev: true
291 | optional: true
292 |
293 | /@esbuild/linux-riscv64@0.19.9:
294 | resolution: {integrity: sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==}
295 | engines: {node: '>=12'}
296 | cpu: [riscv64]
297 | os: [linux]
298 | requiresBuild: true
299 | dev: true
300 | optional: true
301 |
302 | /@esbuild/linux-s390x@0.19.9:
303 | resolution: {integrity: sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==}
304 | engines: {node: '>=12'}
305 | cpu: [s390x]
306 | os: [linux]
307 | requiresBuild: true
308 | dev: true
309 | optional: true
310 |
311 | /@esbuild/linux-x64@0.19.9:
312 | resolution: {integrity: sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==}
313 | engines: {node: '>=12'}
314 | cpu: [x64]
315 | os: [linux]
316 | requiresBuild: true
317 | dev: true
318 | optional: true
319 |
320 | /@esbuild/netbsd-x64@0.19.9:
321 | resolution: {integrity: sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==}
322 | engines: {node: '>=12'}
323 | cpu: [x64]
324 | os: [netbsd]
325 | requiresBuild: true
326 | dev: true
327 | optional: true
328 |
329 | /@esbuild/openbsd-x64@0.19.9:
330 | resolution: {integrity: sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==}
331 | engines: {node: '>=12'}
332 | cpu: [x64]
333 | os: [openbsd]
334 | requiresBuild: true
335 | dev: true
336 | optional: true
337 |
338 | /@esbuild/sunos-x64@0.19.9:
339 | resolution: {integrity: sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==}
340 | engines: {node: '>=12'}
341 | cpu: [x64]
342 | os: [sunos]
343 | requiresBuild: true
344 | dev: true
345 | optional: true
346 |
347 | /@esbuild/win32-arm64@0.19.9:
348 | resolution: {integrity: sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==}
349 | engines: {node: '>=12'}
350 | cpu: [arm64]
351 | os: [win32]
352 | requiresBuild: true
353 | dev: true
354 | optional: true
355 |
356 | /@esbuild/win32-ia32@0.19.9:
357 | resolution: {integrity: sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==}
358 | engines: {node: '>=12'}
359 | cpu: [ia32]
360 | os: [win32]
361 | requiresBuild: true
362 | dev: true
363 | optional: true
364 |
365 | /@esbuild/win32-x64@0.19.9:
366 | resolution: {integrity: sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==}
367 | engines: {node: '>=12'}
368 | cpu: [x64]
369 | os: [win32]
370 | requiresBuild: true
371 | dev: true
372 | optional: true
373 |
374 | /@floating-ui/core@1.5.2:
375 | resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==}
376 | dependencies:
377 | '@floating-ui/utils': 0.1.6
378 | dev: false
379 |
380 | /@floating-ui/dom@1.5.3:
381 | resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==}
382 | dependencies:
383 | '@floating-ui/core': 1.5.2
384 | '@floating-ui/utils': 0.1.6
385 | dev: false
386 |
387 | /@floating-ui/utils@0.1.6:
388 | resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==}
389 | dev: false
390 |
391 | /@intlify/core-base@9.8.0:
392 | resolution: {integrity: sha512-UxaSZVZ1DwqC/CltUZrWZNaWNhfmKtfyV4BJSt/Zt4Or/fZs1iFj0B+OekYk1+MRHfIOe3+x00uXGQI4PbO/9g==}
393 | engines: {node: '>= 16'}
394 | dependencies:
395 | '@intlify/message-compiler': 9.8.0
396 | '@intlify/shared': 9.8.0
397 | dev: false
398 |
399 | /@intlify/message-compiler@9.8.0:
400 | resolution: {integrity: sha512-McnYWhcoYmDJvssVu6QGR0shqlkJuL1HHdi5lK7fNqvQqRYaQ4lSLjYmZxwc8tRNMdIe9/KUKfyPxU9M6yCtNQ==}
401 | engines: {node: '>= 16'}
402 | dependencies:
403 | '@intlify/shared': 9.8.0
404 | source-map-js: 1.0.2
405 | dev: false
406 |
407 | /@intlify/shared@9.8.0:
408 | resolution: {integrity: sha512-TmgR0RCLjzrSo+W3wT0ALf9851iFMlVI9EYNGeWvZFUQTAJx0bvfsMlPdgVtV1tDNRiAfhkFsMKu6jtUY1ZLKQ==}
409 | engines: {node: '>= 16'}
410 | dev: false
411 |
412 | /@jridgewell/sourcemap-codec@1.4.15:
413 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
414 |
415 | /@lezer/common@1.1.2:
416 | resolution: {integrity: sha512-V+GqBsga5+cQJMfM0GdnHmg4DgWvLzgMWjbldBg0+jC3k9Gu6nJNZDLJxXEBT1Xj8KhRN4jmbC5CY7SIL++sVw==}
417 | dev: false
418 |
419 | /@lezer/highlight@1.2.0:
420 | resolution: {integrity: sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==}
421 | dependencies:
422 | '@lezer/common': 1.1.2
423 | dev: false
424 |
425 | /@lezer/javascript@1.4.10:
426 | resolution: {integrity: sha512-XJu3fZjHVVjJcRS7kHdwBO50irXc4H8rQwgm6SmT3Y8IHWk7WzpaLsaR2vdr/jSk/J4pQhXc1WLul7jVdxC+0Q==}
427 | dependencies:
428 | '@lezer/highlight': 1.2.0
429 | '@lezer/lr': 1.3.14
430 | dev: false
431 |
432 | /@lezer/lr@1.3.14:
433 | resolution: {integrity: sha512-z5mY4LStlA3yL7aHT/rqgG614cfcvklS+8oFRFBYrs4YaWLJyKKM4+nN6KopToX0o9Hj6zmH6M5kinOYuy06ug==}
434 | dependencies:
435 | '@lezer/common': 1.1.2
436 | dev: false
437 |
438 | /@rollup/rollup-android-arm-eabi@4.9.0:
439 | resolution: {integrity: sha512-+1ge/xmaJpm1KVBuIH38Z94zj9fBD+hp+/5WLaHgyY8XLq1ibxk/zj6dTXaqM2cAbYKq8jYlhHd6k05If1W5xA==}
440 | cpu: [arm]
441 | os: [android]
442 | requiresBuild: true
443 | dev: true
444 | optional: true
445 |
446 | /@rollup/rollup-android-arm64@4.9.0:
447 | resolution: {integrity: sha512-im6hUEyQ7ZfoZdNvtwgEJvBWZYauC9KVKq1w58LG2Zfz6zMd8gRrbN+xCVoqA2hv/v6fm9lp5LFGJ3za8EQH3A==}
448 | cpu: [arm64]
449 | os: [android]
450 | requiresBuild: true
451 | dev: true
452 | optional: true
453 |
454 | /@rollup/rollup-darwin-arm64@4.9.0:
455 | resolution: {integrity: sha512-u7aTMskN6Dmg1lCT0QJ+tINRt+ntUrvVkhbPfFz4bCwRZvjItx2nJtwJnJRlKMMaQCHRjrNqHRDYvE4mBm3DlQ==}
456 | cpu: [arm64]
457 | os: [darwin]
458 | requiresBuild: true
459 | dev: true
460 | optional: true
461 |
462 | /@rollup/rollup-darwin-x64@4.9.0:
463 | resolution: {integrity: sha512-8FvEl3w2ExmpcOmX5RJD0yqXcVSOqAJJUJ29Lca29Ik+3zPS1yFimr2fr5JSZ4Z5gt8/d7WqycpgkX9nocijSw==}
464 | cpu: [x64]
465 | os: [darwin]
466 | requiresBuild: true
467 | dev: true
468 | optional: true
469 |
470 | /@rollup/rollup-linux-arm-gnueabihf@4.9.0:
471 | resolution: {integrity: sha512-lHoKYaRwd4gge+IpqJHCY+8Vc3hhdJfU6ukFnnrJasEBUvVlydP8PuwndbWfGkdgSvZhHfSEw6urrlBj0TSSfg==}
472 | cpu: [arm]
473 | os: [linux]
474 | requiresBuild: true
475 | dev: true
476 | optional: true
477 |
478 | /@rollup/rollup-linux-arm64-gnu@4.9.0:
479 | resolution: {integrity: sha512-JbEPfhndYeWHfOSeh4DOFvNXrj7ls9S/2omijVsao+LBPTPayT1uKcK3dHW3MwDJ7KO11t9m2cVTqXnTKpeaiw==}
480 | cpu: [arm64]
481 | os: [linux]
482 | requiresBuild: true
483 | dev: true
484 | optional: true
485 |
486 | /@rollup/rollup-linux-arm64-musl@4.9.0:
487 | resolution: {integrity: sha512-ahqcSXLlcV2XUBM3/f/C6cRoh7NxYA/W7Yzuv4bDU1YscTFw7ay4LmD7l6OS8EMhTNvcrWGkEettL1Bhjf+B+w==}
488 | cpu: [arm64]
489 | os: [linux]
490 | requiresBuild: true
491 | dev: true
492 | optional: true
493 |
494 | /@rollup/rollup-linux-riscv64-gnu@4.9.0:
495 | resolution: {integrity: sha512-uwvOYNtLw8gVtrExKhdFsYHA/kotURUmZYlinH2VcQxNCQJeJXnkmWgw2hI9Xgzhgu7J9QvWiq9TtTVwWMDa+w==}
496 | cpu: [riscv64]
497 | os: [linux]
498 | requiresBuild: true
499 | dev: true
500 | optional: true
501 |
502 | /@rollup/rollup-linux-x64-gnu@4.9.0:
503 | resolution: {integrity: sha512-m6pkSwcZZD2LCFHZX/zW2aLIISyzWLU3hrLLzQKMI12+OLEzgruTovAxY5sCZJkipklaZqPy/2bEEBNjp+Y7xg==}
504 | cpu: [x64]
505 | os: [linux]
506 | requiresBuild: true
507 | dev: true
508 | optional: true
509 |
510 | /@rollup/rollup-linux-x64-musl@4.9.0:
511 | resolution: {integrity: sha512-VFAC1RDRSbU3iOF98X42KaVicAfKf0m0OvIu8dbnqhTe26Kh6Ym9JrDulz7Hbk7/9zGc41JkV02g+p3BivOdAg==}
512 | cpu: [x64]
513 | os: [linux]
514 | requiresBuild: true
515 | dev: true
516 | optional: true
517 |
518 | /@rollup/rollup-win32-arm64-msvc@4.9.0:
519 | resolution: {integrity: sha512-9jPgMvTKXARz4inw6jezMLA2ihDBvgIU9Ml01hjdVpOcMKyxFBJrn83KVQINnbeqDv0+HdO1c09hgZ8N0s820Q==}
520 | cpu: [arm64]
521 | os: [win32]
522 | requiresBuild: true
523 | dev: true
524 | optional: true
525 |
526 | /@rollup/rollup-win32-ia32-msvc@4.9.0:
527 | resolution: {integrity: sha512-WE4pT2kTXQN2bAv40Uog0AsV7/s9nT9HBWXAou8+++MBCnY51QS02KYtm6dQxxosKi1VIz/wZIrTQO5UP2EW+Q==}
528 | cpu: [ia32]
529 | os: [win32]
530 | requiresBuild: true
531 | dev: true
532 | optional: true
533 |
534 | /@rollup/rollup-win32-x64-msvc@4.9.0:
535 | resolution: {integrity: sha512-aPP5Q5AqNGuT0tnuEkK/g4mnt3ZhheiXrDIiSVIHN9mcN21OyXDVbEMqmXPE7e2OplNLDkcvV+ZoGJa2ZImFgw==}
536 | cpu: [x64]
537 | os: [win32]
538 | requiresBuild: true
539 | dev: true
540 | optional: true
541 |
542 | /@sxzz/popperjs-es@2.11.7:
543 | resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
544 | dev: false
545 |
546 | /@tsconfig/node18@18.2.2:
547 | resolution: {integrity: sha512-d6McJeGsuoRlwWZmVIeE8CUA27lu6jLjvv1JzqmpsytOYYbVi1tHZEnwCNVOXnj4pyLvneZlFlpXUK+X9wBWyw==}
548 | dev: true
549 |
550 | /@types/lodash-es@4.17.12:
551 | resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
552 | dependencies:
553 | '@types/lodash': 4.14.202
554 | dev: false
555 |
556 | /@types/lodash@4.14.202:
557 | resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==}
558 | dev: false
559 |
560 | /@types/node@20.10.4:
561 | resolution: {integrity: sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==}
562 | dependencies:
563 | undici-types: 5.26.5
564 | dev: true
565 |
566 | /@types/normalize-package-data@2.4.4:
567 | resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
568 | dev: true
569 |
570 | /@types/web-bluetooth@0.0.16:
571 | resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
572 | dev: false
573 |
574 | /@vitejs/plugin-vue@4.5.2(vite@5.0.8)(vue@3.3.11):
575 | resolution: {integrity: sha512-UGR3DlzLi/SaVBPX0cnSyE37vqxU3O6chn8l0HJNzQzDia6/Au2A4xKv+iIJW8w2daf80G7TYHhi1pAUjdZ0bQ==}
576 | engines: {node: ^14.18.0 || >=16.0.0}
577 | peerDependencies:
578 | vite: ^4.0.0 || ^5.0.0
579 | vue: ^3.2.25
580 | dependencies:
581 | vite: 5.0.8(@types/node@20.10.4)
582 | vue: 3.3.11(typescript@5.3.3)
583 | dev: true
584 |
585 | /@volar/language-core@1.11.1:
586 | resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==}
587 | dependencies:
588 | '@volar/source-map': 1.11.1
589 | dev: true
590 |
591 | /@volar/source-map@1.11.1:
592 | resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==}
593 | dependencies:
594 | muggle-string: 0.3.1
595 | dev: true
596 |
597 | /@volar/typescript@1.11.1:
598 | resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==}
599 | dependencies:
600 | '@volar/language-core': 1.11.1
601 | path-browserify: 1.0.1
602 | dev: true
603 |
604 | /@vue/compiler-core@3.3.11:
605 | resolution: {integrity: sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w==}
606 | dependencies:
607 | '@babel/parser': 7.23.6
608 | '@vue/shared': 3.3.11
609 | estree-walker: 2.0.2
610 | source-map-js: 1.0.2
611 |
612 | /@vue/compiler-dom@3.3.11:
613 | resolution: {integrity: sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==}
614 | dependencies:
615 | '@vue/compiler-core': 3.3.11
616 | '@vue/shared': 3.3.11
617 |
618 | /@vue/compiler-sfc@3.3.11:
619 | resolution: {integrity: sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==}
620 | dependencies:
621 | '@babel/parser': 7.23.6
622 | '@vue/compiler-core': 3.3.11
623 | '@vue/compiler-dom': 3.3.11
624 | '@vue/compiler-ssr': 3.3.11
625 | '@vue/reactivity-transform': 3.3.11
626 | '@vue/shared': 3.3.11
627 | estree-walker: 2.0.2
628 | magic-string: 0.30.5
629 | postcss: 8.4.32
630 | source-map-js: 1.0.2
631 |
632 | /@vue/compiler-ssr@3.3.11:
633 | resolution: {integrity: sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==}
634 | dependencies:
635 | '@vue/compiler-dom': 3.3.11
636 | '@vue/shared': 3.3.11
637 |
638 | /@vue/devtools-api@6.5.1:
639 | resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==}
640 | dev: false
641 |
642 | /@vue/language-core@1.8.25(typescript@5.3.3):
643 | resolution: {integrity: sha512-NJk/5DnAZlpvXX8BdWmHI45bWGLViUaS3R/RMrmFSvFMSbJKuEODpM4kR0F0Ofv5SFzCWuNiMhxameWpVdQsnA==}
644 | peerDependencies:
645 | typescript: '*'
646 | peerDependenciesMeta:
647 | typescript:
648 | optional: true
649 | dependencies:
650 | '@volar/language-core': 1.11.1
651 | '@volar/source-map': 1.11.1
652 | '@vue/compiler-dom': 3.3.11
653 | '@vue/shared': 3.3.11
654 | computeds: 0.0.1
655 | minimatch: 9.0.3
656 | muggle-string: 0.3.1
657 | path-browserify: 1.0.1
658 | typescript: 5.3.3
659 | vue-template-compiler: 2.7.15
660 | dev: true
661 |
662 | /@vue/reactivity-transform@3.3.11:
663 | resolution: {integrity: sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==}
664 | dependencies:
665 | '@babel/parser': 7.23.6
666 | '@vue/compiler-core': 3.3.11
667 | '@vue/shared': 3.3.11
668 | estree-walker: 2.0.2
669 | magic-string: 0.30.5
670 |
671 | /@vue/reactivity@3.3.11:
672 | resolution: {integrity: sha512-D5tcw091f0nuu+hXq5XANofD0OXnBmaRqMYl5B3fCR+mX+cXJIGNw/VNawBqkjLNWETrFW0i+xH9NvDbTPVh7g==}
673 | dependencies:
674 | '@vue/shared': 3.3.11
675 |
676 | /@vue/runtime-core@3.3.11:
677 | resolution: {integrity: sha512-g9ztHGwEbS5RyWaOpXuyIVFTschclnwhqEbdy5AwGhYOgc7m/q3NFwr50MirZwTTzX55JY8pSkeib9BX04NIpw==}
678 | dependencies:
679 | '@vue/reactivity': 3.3.11
680 | '@vue/shared': 3.3.11
681 |
682 | /@vue/runtime-dom@3.3.11:
683 | resolution: {integrity: sha512-OlhtV1PVpbgk+I2zl+Y5rQtDNcCDs12rsRg71XwaA2/Rbllw6mBLMi57VOn8G0AjOJ4Mdb4k56V37+g8ukShpQ==}
684 | dependencies:
685 | '@vue/runtime-core': 3.3.11
686 | '@vue/shared': 3.3.11
687 | csstype: 3.1.3
688 |
689 | /@vue/server-renderer@3.3.11(vue@3.3.11):
690 | resolution: {integrity: sha512-AIWk0VwwxCAm4wqtJyxBylRTXSy1wCLOKbWxHaHiu14wjsNYtiRCSgVuqEPVuDpErOlRdNnuRgipQfXRLjLN5A==}
691 | peerDependencies:
692 | vue: 3.3.11
693 | dependencies:
694 | '@vue/compiler-ssr': 3.3.11
695 | '@vue/shared': 3.3.11
696 | vue: 3.3.11(typescript@5.3.3)
697 |
698 | /@vue/shared@3.3.11:
699 | resolution: {integrity: sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==}
700 |
701 | /@vue/tsconfig@0.4.0:
702 | resolution: {integrity: sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==}
703 | dev: true
704 |
705 | /@vueuse/core@9.13.0(vue@3.3.11):
706 | resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
707 | dependencies:
708 | '@types/web-bluetooth': 0.0.16
709 | '@vueuse/metadata': 9.13.0
710 | '@vueuse/shared': 9.13.0(vue@3.3.11)
711 | vue-demi: 0.14.6(vue@3.3.11)
712 | transitivePeerDependencies:
713 | - '@vue/composition-api'
714 | - vue
715 | dev: false
716 |
717 | /@vueuse/metadata@9.13.0:
718 | resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
719 | dev: false
720 |
721 | /@vueuse/shared@9.13.0(vue@3.3.11):
722 | resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
723 | dependencies:
724 | vue-demi: 0.14.6(vue@3.3.11)
725 | transitivePeerDependencies:
726 | - '@vue/composition-api'
727 | - vue
728 | dev: false
729 |
730 | /ansi-styles@3.2.1:
731 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
732 | engines: {node: '>=4'}
733 | dependencies:
734 | color-convert: 1.9.3
735 | dev: true
736 |
737 | /ansi-styles@6.2.1:
738 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
739 | engines: {node: '>=12'}
740 | dev: true
741 |
742 | /async-validator@4.2.5:
743 | resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
744 | dev: false
745 |
746 | /balanced-match@1.0.2:
747 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
748 | dev: true
749 |
750 | /brace-expansion@2.0.1:
751 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
752 | dependencies:
753 | balanced-match: 1.0.2
754 | dev: true
755 |
756 | /chalk@2.4.2:
757 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
758 | engines: {node: '>=4'}
759 | dependencies:
760 | ansi-styles: 3.2.1
761 | escape-string-regexp: 1.0.5
762 | supports-color: 5.5.0
763 | dev: true
764 |
765 | /codemirror@6.0.1(@lezer/common@1.1.2):
766 | resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==}
767 | dependencies:
768 | '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.3)(@codemirror/view@6.22.3)(@lezer/common@1.1.2)
769 | '@codemirror/commands': 6.3.2
770 | '@codemirror/language': 6.9.3
771 | '@codemirror/lint': 6.4.2
772 | '@codemirror/search': 6.5.5
773 | '@codemirror/state': 6.3.3
774 | '@codemirror/view': 6.22.3
775 | transitivePeerDependencies:
776 | - '@lezer/common'
777 | dev: false
778 |
779 | /color-convert@1.9.3:
780 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
781 | dependencies:
782 | color-name: 1.1.3
783 | dev: true
784 |
785 | /color-name@1.1.3:
786 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
787 | dev: true
788 |
789 | /computeds@0.0.1:
790 | resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==}
791 | dev: true
792 |
793 | /crelt@1.0.6:
794 | resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
795 | dev: false
796 |
797 | /cross-spawn@7.0.3:
798 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
799 | engines: {node: '>= 8'}
800 | dependencies:
801 | path-key: 3.1.1
802 | shebang-command: 2.0.0
803 | which: 2.0.2
804 | dev: true
805 |
806 | /csstype@3.1.3:
807 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
808 |
809 | /dayjs@1.11.10:
810 | resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
811 | dev: false
812 |
813 | /de-indent@1.0.2:
814 | resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
815 | dev: true
816 |
817 | /element-plus@2.4.3(vue@3.3.11):
818 | resolution: {integrity: sha512-b3q26j+lM4SBqiyzw8HybybGnP2pk4MWgrnzzzYW5qKQUgV6EG1Zg7nMCfgCVccI8tNvZoTiUHb2mFaiB9qT8w==}
819 | peerDependencies:
820 | vue: ^3.2.0
821 | dependencies:
822 | '@ctrl/tinycolor': 3.6.1
823 | '@element-plus/icons-vue': 2.3.1(vue@3.3.11)
824 | '@floating-ui/dom': 1.5.3
825 | '@popperjs/core': /@sxzz/popperjs-es@2.11.7
826 | '@types/lodash': 4.14.202
827 | '@types/lodash-es': 4.17.12
828 | '@vueuse/core': 9.13.0(vue@3.3.11)
829 | async-validator: 4.2.5
830 | dayjs: 1.11.10
831 | escape-html: 1.0.3
832 | lodash: 4.17.21
833 | lodash-es: 4.17.21
834 | lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21)
835 | memoize-one: 6.0.0
836 | normalize-wheel-es: 1.2.0
837 | vue: 3.3.11(typescript@5.3.3)
838 | transitivePeerDependencies:
839 | - '@vue/composition-api'
840 | dev: false
841 |
842 | /error-ex@1.3.2:
843 | resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
844 | dependencies:
845 | is-arrayish: 0.2.1
846 | dev: true
847 |
848 | /esbuild@0.19.9:
849 | resolution: {integrity: sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==}
850 | engines: {node: '>=12'}
851 | hasBin: true
852 | requiresBuild: true
853 | optionalDependencies:
854 | '@esbuild/android-arm': 0.19.9
855 | '@esbuild/android-arm64': 0.19.9
856 | '@esbuild/android-x64': 0.19.9
857 | '@esbuild/darwin-arm64': 0.19.9
858 | '@esbuild/darwin-x64': 0.19.9
859 | '@esbuild/freebsd-arm64': 0.19.9
860 | '@esbuild/freebsd-x64': 0.19.9
861 | '@esbuild/linux-arm': 0.19.9
862 | '@esbuild/linux-arm64': 0.19.9
863 | '@esbuild/linux-ia32': 0.19.9
864 | '@esbuild/linux-loong64': 0.19.9
865 | '@esbuild/linux-mips64el': 0.19.9
866 | '@esbuild/linux-ppc64': 0.19.9
867 | '@esbuild/linux-riscv64': 0.19.9
868 | '@esbuild/linux-s390x': 0.19.9
869 | '@esbuild/linux-x64': 0.19.9
870 | '@esbuild/netbsd-x64': 0.19.9
871 | '@esbuild/openbsd-x64': 0.19.9
872 | '@esbuild/sunos-x64': 0.19.9
873 | '@esbuild/win32-arm64': 0.19.9
874 | '@esbuild/win32-ia32': 0.19.9
875 | '@esbuild/win32-x64': 0.19.9
876 | dev: true
877 |
878 | /escape-html@1.0.3:
879 | resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
880 | dev: false
881 |
882 | /escape-string-regexp@1.0.5:
883 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
884 | engines: {node: '>=0.8.0'}
885 | dev: true
886 |
887 | /eslint-linter-browserify@8.55.0:
888 | resolution: {integrity: sha512-Nx8520Av6zEtvaqFT5WpLyWrDhRtE+Gg9zbpkIn9Q/UzPwXAuKVpNhVQFK6/VMeT4NDSKY2n9cd76mJfzgcr2g==}
889 | dev: false
890 |
891 | /estree-walker@2.0.2:
892 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
893 |
894 | /fsevents@2.3.3:
895 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
896 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
897 | os: [darwin]
898 | requiresBuild: true
899 | dev: true
900 | optional: true
901 |
902 | /function-bind@1.1.2:
903 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
904 | dev: true
905 |
906 | /has-flag@3.0.0:
907 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
908 | engines: {node: '>=4'}
909 | dev: true
910 |
911 | /hasown@2.0.0:
912 | resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
913 | engines: {node: '>= 0.4'}
914 | dependencies:
915 | function-bind: 1.1.2
916 | dev: true
917 |
918 | /he@1.2.0:
919 | resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
920 | hasBin: true
921 | dev: true
922 |
923 | /hosted-git-info@7.0.1:
924 | resolution: {integrity: sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==}
925 | engines: {node: ^16.14.0 || >=18.0.0}
926 | dependencies:
927 | lru-cache: 10.1.0
928 | dev: true
929 |
930 | /is-arrayish@0.2.1:
931 | resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
932 | dev: true
933 |
934 | /is-core-module@2.13.1:
935 | resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
936 | dependencies:
937 | hasown: 2.0.0
938 | dev: true
939 |
940 | /isexe@2.0.0:
941 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
942 | dev: true
943 |
944 | /js-tokens@4.0.0:
945 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
946 | dev: true
947 |
948 | /json-parse-even-better-errors@3.0.1:
949 | resolution: {integrity: sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==}
950 | engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
951 | dev: true
952 |
953 | /lines-and-columns@2.0.4:
954 | resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==}
955 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
956 | dev: true
957 |
958 | /lodash-es@4.17.21:
959 | resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
960 | dev: false
961 |
962 | /lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21):
963 | resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==}
964 | peerDependencies:
965 | '@types/lodash-es': '*'
966 | lodash: '*'
967 | lodash-es: '*'
968 | dependencies:
969 | '@types/lodash-es': 4.17.12
970 | lodash: 4.17.21
971 | lodash-es: 4.17.21
972 | dev: false
973 |
974 | /lodash@4.17.21:
975 | resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
976 | dev: false
977 |
978 | /lru-cache@10.1.0:
979 | resolution: {integrity: sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==}
980 | engines: {node: 14 || >=16.14}
981 | dev: true
982 |
983 | /lru-cache@6.0.0:
984 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
985 | engines: {node: '>=10'}
986 | dependencies:
987 | yallist: 4.0.0
988 | dev: true
989 |
990 | /magic-string@0.30.5:
991 | resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==}
992 | engines: {node: '>=12'}
993 | dependencies:
994 | '@jridgewell/sourcemap-codec': 1.4.15
995 |
996 | /memoize-one@6.0.0:
997 | resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
998 | dev: false
999 |
1000 | /memorystream@0.3.1:
1001 | resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
1002 | engines: {node: '>= 0.10.0'}
1003 | dev: true
1004 |
1005 | /minimatch@9.0.3:
1006 | resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
1007 | engines: {node: '>=16 || 14 >=14.17'}
1008 | dependencies:
1009 | brace-expansion: 2.0.1
1010 | dev: true
1011 |
1012 | /muggle-string@0.3.1:
1013 | resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==}
1014 | dev: true
1015 |
1016 | /nanoid@3.3.7:
1017 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
1018 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
1019 | hasBin: true
1020 |
1021 | /normalize-package-data@6.0.0:
1022 | resolution: {integrity: sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==}
1023 | engines: {node: ^16.14.0 || >=18.0.0}
1024 | dependencies:
1025 | hosted-git-info: 7.0.1
1026 | is-core-module: 2.13.1
1027 | semver: 7.5.4
1028 | validate-npm-package-license: 3.0.4
1029 | dev: true
1030 |
1031 | /normalize-wheel-es@1.2.0:
1032 | resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==}
1033 | dev: false
1034 |
1035 | /npm-run-all2@6.1.1:
1036 | resolution: {integrity: sha512-lWLbkPZ5BSdXtN8lR+0rc8caKoPdymycpZksyDEC9MOBvfdwTXZ0uVhb7bMcGeXv2/BKtfQuo6Zn3zfc8rxNXA==}
1037 | engines: {node: ^14.18.0 || >=16.0.0, npm: '>= 8'}
1038 | hasBin: true
1039 | dependencies:
1040 | ansi-styles: 6.2.1
1041 | cross-spawn: 7.0.3
1042 | memorystream: 0.3.1
1043 | minimatch: 9.0.3
1044 | pidtree: 0.6.0
1045 | read-pkg: 8.1.0
1046 | shell-quote: 1.8.1
1047 | dev: true
1048 |
1049 | /octicons-css@19.8.0:
1050 | resolution: {integrity: sha512-19a2ZfmPNQMLjQ8S2Q4s0ZYsctJpFQDxiI56H1eU4tFnb0FN6KD6iHS89D2k9YAbeVSsx0JpEfjSc4AZTEi5Og==}
1051 | dev: false
1052 |
1053 | /parse-json@7.1.1:
1054 | resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==}
1055 | engines: {node: '>=16'}
1056 | dependencies:
1057 | '@babel/code-frame': 7.23.5
1058 | error-ex: 1.3.2
1059 | json-parse-even-better-errors: 3.0.1
1060 | lines-and-columns: 2.0.4
1061 | type-fest: 3.13.1
1062 | dev: true
1063 |
1064 | /path-browserify@1.0.1:
1065 | resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
1066 | dev: true
1067 |
1068 | /path-key@3.1.1:
1069 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
1070 | engines: {node: '>=8'}
1071 | dev: true
1072 |
1073 | /picocolors@1.0.0:
1074 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
1075 |
1076 | /pidtree@0.6.0:
1077 | resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
1078 | engines: {node: '>=0.10'}
1079 | hasBin: true
1080 | dev: true
1081 |
1082 | /postcss@8.4.32:
1083 | resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==}
1084 | engines: {node: ^10 || ^12 || >=14}
1085 | dependencies:
1086 | nanoid: 3.3.7
1087 | picocolors: 1.0.0
1088 | source-map-js: 1.0.2
1089 |
1090 | /read-pkg@8.1.0:
1091 | resolution: {integrity: sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==}
1092 | engines: {node: '>=16'}
1093 | dependencies:
1094 | '@types/normalize-package-data': 2.4.4
1095 | normalize-package-data: 6.0.0
1096 | parse-json: 7.1.1
1097 | type-fest: 4.8.3
1098 | dev: true
1099 |
1100 | /rollup@4.9.0:
1101 | resolution: {integrity: sha512-bUHW/9N21z64gw8s6tP4c88P382Bq/L5uZDowHlHx6s/QWpjJXivIAbEw6LZthgSvlEizZBfLC4OAvWe7aoF7A==}
1102 | engines: {node: '>=18.0.0', npm: '>=8.0.0'}
1103 | hasBin: true
1104 | optionalDependencies:
1105 | '@rollup/rollup-android-arm-eabi': 4.9.0
1106 | '@rollup/rollup-android-arm64': 4.9.0
1107 | '@rollup/rollup-darwin-arm64': 4.9.0
1108 | '@rollup/rollup-darwin-x64': 4.9.0
1109 | '@rollup/rollup-linux-arm-gnueabihf': 4.9.0
1110 | '@rollup/rollup-linux-arm64-gnu': 4.9.0
1111 | '@rollup/rollup-linux-arm64-musl': 4.9.0
1112 | '@rollup/rollup-linux-riscv64-gnu': 4.9.0
1113 | '@rollup/rollup-linux-x64-gnu': 4.9.0
1114 | '@rollup/rollup-linux-x64-musl': 4.9.0
1115 | '@rollup/rollup-win32-arm64-msvc': 4.9.0
1116 | '@rollup/rollup-win32-ia32-msvc': 4.9.0
1117 | '@rollup/rollup-win32-x64-msvc': 4.9.0
1118 | fsevents: 2.3.3
1119 | dev: true
1120 |
1121 | /semver@7.5.4:
1122 | resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
1123 | engines: {node: '>=10'}
1124 | hasBin: true
1125 | dependencies:
1126 | lru-cache: 6.0.0
1127 | dev: true
1128 |
1129 | /shebang-command@2.0.0:
1130 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
1131 | engines: {node: '>=8'}
1132 | dependencies:
1133 | shebang-regex: 3.0.0
1134 | dev: true
1135 |
1136 | /shebang-regex@3.0.0:
1137 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
1138 | engines: {node: '>=8'}
1139 | dev: true
1140 |
1141 | /shell-quote@1.8.1:
1142 | resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==}
1143 | dev: true
1144 |
1145 | /source-map-js@1.0.2:
1146 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
1147 | engines: {node: '>=0.10.0'}
1148 |
1149 | /spdx-correct@3.2.0:
1150 | resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
1151 | dependencies:
1152 | spdx-expression-parse: 3.0.1
1153 | spdx-license-ids: 3.0.16
1154 | dev: true
1155 |
1156 | /spdx-exceptions@2.3.0:
1157 | resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
1158 | dev: true
1159 |
1160 | /spdx-expression-parse@3.0.1:
1161 | resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
1162 | dependencies:
1163 | spdx-exceptions: 2.3.0
1164 | spdx-license-ids: 3.0.16
1165 | dev: true
1166 |
1167 | /spdx-license-ids@3.0.16:
1168 | resolution: {integrity: sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==}
1169 | dev: true
1170 |
1171 | /style-mod@4.1.0:
1172 | resolution: {integrity: sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==}
1173 | dev: false
1174 |
1175 | /supports-color@5.5.0:
1176 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
1177 | engines: {node: '>=4'}
1178 | dependencies:
1179 | has-flag: 3.0.0
1180 | dev: true
1181 |
1182 | /to-fast-properties@2.0.0:
1183 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
1184 | engines: {node: '>=4'}
1185 |
1186 | /type-fest@3.13.1:
1187 | resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==}
1188 | engines: {node: '>=14.16'}
1189 | dev: true
1190 |
1191 | /type-fest@4.8.3:
1192 | resolution: {integrity: sha512-//BaTm14Q/gHBn09xlnKNqfI8t6bmdzx2DXYfPBNofN0WUybCEUDcbCWcTa0oF09lzLjZgPphXAsvRiMK0V6Bw==}
1193 | engines: {node: '>=16'}
1194 | dev: true
1195 |
1196 | /typescript@5.3.3:
1197 | resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
1198 | engines: {node: '>=14.17'}
1199 | hasBin: true
1200 |
1201 | /undici-types@5.26.5:
1202 | resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
1203 | dev: true
1204 |
1205 | /validate-npm-package-license@3.0.4:
1206 | resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
1207 | dependencies:
1208 | spdx-correct: 3.2.0
1209 | spdx-expression-parse: 3.0.1
1210 | dev: true
1211 |
1212 | /vite@5.0.8(@types/node@20.10.4):
1213 | resolution: {integrity: sha512-jYMALd8aeqR3yS9xlHd0OzQJndS9fH5ylVgWdB+pxTwxLKdO1pgC5Dlb398BUxpfaBxa4M9oT7j1g503Gaj5IQ==}
1214 | engines: {node: ^18.0.0 || >=20.0.0}
1215 | hasBin: true
1216 | peerDependencies:
1217 | '@types/node': ^18.0.0 || >=20.0.0
1218 | less: '*'
1219 | lightningcss: ^1.21.0
1220 | sass: '*'
1221 | stylus: '*'
1222 | sugarss: '*'
1223 | terser: ^5.4.0
1224 | peerDependenciesMeta:
1225 | '@types/node':
1226 | optional: true
1227 | less:
1228 | optional: true
1229 | lightningcss:
1230 | optional: true
1231 | sass:
1232 | optional: true
1233 | stylus:
1234 | optional: true
1235 | sugarss:
1236 | optional: true
1237 | terser:
1238 | optional: true
1239 | dependencies:
1240 | '@types/node': 20.10.4
1241 | esbuild: 0.19.9
1242 | postcss: 8.4.32
1243 | rollup: 4.9.0
1244 | optionalDependencies:
1245 | fsevents: 2.3.3
1246 | dev: true
1247 |
1248 | /vue-codemirror6@1.1.32(@lezer/common@1.1.2)(vue@3.3.11):
1249 | resolution: {integrity: sha512-n1iaG1yw6sRAWT0ur77q2vBYsewfOPItT+a7pwH+9B5/XDPKO4buX+yP2H2lDiPyhpmsRNEjrUDuYtBKYOuZFQ==}
1250 | engines: {yarn: '>=1.22.19'}
1251 | peerDependencies:
1252 | vue: ^2.7.14 || ^3.2
1253 | dependencies:
1254 | codemirror: 6.0.1(@lezer/common@1.1.2)
1255 | vue: 3.3.11(typescript@5.3.3)
1256 | vue-demi: 0.14.6(vue@3.3.11)
1257 | transitivePeerDependencies:
1258 | - '@lezer/common'
1259 | - '@vue/composition-api'
1260 | dev: false
1261 |
1262 | /vue-demi@0.14.6(vue@3.3.11):
1263 | resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==}
1264 | engines: {node: '>=12'}
1265 | hasBin: true
1266 | requiresBuild: true
1267 | peerDependencies:
1268 | '@vue/composition-api': ^1.0.0-rc.1
1269 | vue: ^3.0.0-0 || ^2.6.0
1270 | peerDependenciesMeta:
1271 | '@vue/composition-api':
1272 | optional: true
1273 | dependencies:
1274 | vue: 3.3.11(typescript@5.3.3)
1275 | dev: false
1276 |
1277 | /vue-i18n@9.8.0(vue@3.3.11):
1278 | resolution: {integrity: sha512-Izho+6PYjejsTq2mzjcRdBZ5VLRQoSuuexvR8029h5CpN03FYqiqBrShMyf2I1DKkN6kw/xmujcbvC+4QybpsQ==}
1279 | engines: {node: '>= 16'}
1280 | peerDependencies:
1281 | vue: ^3.0.0
1282 | dependencies:
1283 | '@intlify/core-base': 9.8.0
1284 | '@intlify/shared': 9.8.0
1285 | '@vue/devtools-api': 6.5.1
1286 | vue: 3.3.11(typescript@5.3.3)
1287 | dev: false
1288 |
1289 | /vue-template-compiler@2.7.15:
1290 | resolution: {integrity: sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==}
1291 | dependencies:
1292 | de-indent: 1.0.2
1293 | he: 1.2.0
1294 | dev: true
1295 |
1296 | /vue-tsc@1.8.25(typescript@5.3.3):
1297 | resolution: {integrity: sha512-lHsRhDc/Y7LINvYhZ3pv4elflFADoEOo67vfClAfF2heVHpHmVquLSjojgCSIwzA4F0Pc4vowT/psXCYcfk+iQ==}
1298 | hasBin: true
1299 | peerDependencies:
1300 | typescript: '*'
1301 | dependencies:
1302 | '@volar/typescript': 1.11.1
1303 | '@vue/language-core': 1.8.25(typescript@5.3.3)
1304 | semver: 7.5.4
1305 | typescript: 5.3.3
1306 | dev: true
1307 |
1308 | /vue@3.3.11(typescript@5.3.3):
1309 | resolution: {integrity: sha512-d4oBctG92CRO1cQfVBZp6WJAs0n8AK4Xf5fNjQCBeKCvMI1efGQ5E3Alt1slFJS9fZuPcFoiAiqFvQlv1X7t/w==}
1310 | peerDependencies:
1311 | typescript: '*'
1312 | peerDependenciesMeta:
1313 | typescript:
1314 | optional: true
1315 | dependencies:
1316 | '@vue/compiler-dom': 3.3.11
1317 | '@vue/compiler-sfc': 3.3.11
1318 | '@vue/runtime-dom': 3.3.11
1319 | '@vue/server-renderer': 3.3.11(vue@3.3.11)
1320 | '@vue/shared': 3.3.11
1321 | typescript: 5.3.3
1322 |
1323 | /w3c-keyname@2.2.8:
1324 | resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
1325 | dev: false
1326 |
1327 | /which@2.0.2:
1328 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
1329 | engines: {node: '>= 8'}
1330 | hasBin: true
1331 | dependencies:
1332 | isexe: 2.0.0
1333 | dev: true
1334 |
1335 | /yallist@4.0.0:
1336 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
1337 | dev: true
1338 |
1339 | file:..(vue@3.3.11):
1340 | resolution: {directory: .., type: directory}
1341 | id: file:..
1342 | name: '@idealworld/formula-editor'
1343 | peerDependencies:
1344 | vue: ^3.3.4
1345 | dependencies:
1346 | '@codemirror/autocomplete': 6.11.1(@codemirror/language@6.9.3)(@codemirror/state@6.3.3)(@codemirror/view@6.22.3)(@lezer/common@1.1.2)
1347 | '@codemirror/commands': 6.3.2
1348 | '@codemirror/lang-javascript': 6.2.1
1349 | '@codemirror/language': 6.9.3
1350 | '@codemirror/lint': 6.4.2
1351 | '@codemirror/search': 6.5.5
1352 | '@codemirror/state': 6.3.3
1353 | '@codemirror/view': 6.22.3
1354 | '@lezer/common': 1.1.2
1355 | codemirror: 6.0.1(@lezer/common@1.1.2)
1356 | eslint-linter-browserify: 8.55.0
1357 | octicons-css: 19.8.0
1358 | vue: 3.3.11(typescript@5.3.3)
1359 | vue-codemirror6: 1.1.32(@lezer/common@1.1.2)(vue@3.3.11)
1360 | vue-i18n: 9.8.0(vue@3.3.11)
1361 | transitivePeerDependencies:
1362 | - '@vue/composition-api'
1363 | dev: false
1364 |
--------------------------------------------------------------------------------