From 1c4a700d92f7c5e13f958fa5cfa4dc98b94f8edd Mon Sep 17 00:00:00 2001 From: RockChinQ <1010553892@qq.com> Date: Thu, 26 Sep 2024 00:23:03 +0800 Subject: [PATCH 01/71] =?UTF-8?q?refactor:=20=E5=B0=86=20yirimirai=20?= =?UTF-8?q?=E7=9A=84=E7=BB=84=E4=BB=B6=E9=9B=86=E6=88=90=E8=BF=9B=20platfo?= =?UTF-8?q?rm=20=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/command/entities.py | 5 +- pkg/core/entities.py | 13 +- pkg/pipeline/cntfilter/cntfilter.py | 15 +- pkg/pipeline/controller.py | 9 +- pkg/pipeline/entities.py | 6 +- pkg/pipeline/longtext/longtext.py | 7 +- pkg/pipeline/longtext/strategies/forward.py | 20 +- pkg/pipeline/longtext/strategies/image.py | 9 +- pkg/pipeline/longtext/strategy.py | 9 +- pkg/pipeline/pool.py | 8 +- pkg/pipeline/preproc/preproc.py | 7 +- pkg/pipeline/process/handlers/chat.py | 6 +- pkg/pipeline/process/handlers/command.py | 9 +- pkg/pipeline/respback/respback.py | 2 +- pkg/pipeline/resprule/entities.py | 6 +- pkg/pipeline/resprule/resprule.py | 2 +- pkg/pipeline/resprule/rule.py | 6 +- pkg/pipeline/resprule/rules/atbot.py | 13 +- pkg/pipeline/resprule/rules/prefix.py | 7 +- pkg/pipeline/resprule/rules/random.py | 5 +- pkg/pipeline/resprule/rules/regexp.py | 5 +- pkg/pipeline/wrapper/wrapper.py | 13 +- pkg/platform/adapter.py | 26 +- pkg/platform/manager.py | 28 +- pkg/platform/sources/aiocqhttp.py | 71 +- pkg/platform/sources/nakuru.py | 78 +- pkg/platform/sources/qqbotpy.py | 91 +-- pkg/platform/sources/yirimirai.py | 212 ++--- pkg/platform/types/__init__.py | 0 pkg/platform/types/base.py | 105 +++ pkg/platform/types/entities.py | 143 ++++ pkg/platform/types/events.py | 124 +++ pkg/platform/types/message.py | 826 ++++++++++++++++++++ pkg/plugin/context.py | 7 +- pkg/plugin/events.py | 7 +- pkg/provider/entities.py | 22 +- 36 files changed, 1580 insertions(+), 342 deletions(-) create mode 100644 pkg/platform/types/__init__.py create mode 100644 pkg/platform/types/base.py create mode 100644 pkg/platform/types/entities.py create mode 100644 pkg/platform/types/events.py create mode 100644 pkg/platform/types/message.py diff --git a/pkg/command/entities.py b/pkg/command/entities.py index 86975510..3ea660f1 100644 --- a/pkg/command/entities.py +++ b/pkg/command/entities.py @@ -3,10 +3,11 @@ import typing import pydantic -import mirai +# import mirai from ..core import app, entities as core_entities from . import errors, operator +from ..platform.types import message as platform_message class CommandReturn(pydantic.BaseModel): @@ -17,7 +18,7 @@ class CommandReturn(pydantic.BaseModel): """文本 """ - image: typing.Optional[mirai.Image] = None + image: typing.Optional[platform_message.Image] = None """弃用""" image_url: typing.Optional[str] = None diff --git a/pkg/core/entities.py b/pkg/core/entities.py index 6305a0ec..744c6d03 100644 --- a/pkg/core/entities.py +++ b/pkg/core/entities.py @@ -6,13 +6,16 @@ import asyncio import pydantic -import mirai +# import mirai from ..provider import entities as llm_entities from ..provider.modelmgr import entities from ..provider.sysprompt import entities as sysprompt_entities from ..provider.tools import entities as tools_entities from ..platform import adapter as msadapter +from ..platform.types import message as platform_message +from ..platform.types import events as platform_events +from ..platform.types import entities as platform_entities class LauncherTypes(enum.Enum): @@ -40,10 +43,10 @@ class Query(pydantic.BaseModel): sender_id: int """发送者ID,platform处理阶段设置""" - message_event: mirai.MessageEvent + message_event: platform_events.MessageEvent """事件,platform收到的原始事件""" - message_chain: mirai.MessageChain + message_chain: platform_message.MessageChain """消息链,platform收到的原始消息链""" adapter: msadapter.MessageSourceAdapter @@ -67,10 +70,10 @@ class Query(pydantic.BaseModel): use_funcs: typing.Optional[list[tools_entities.LLMFunction]] = None """使用的函数,由前置处理器阶段设置""" - resp_messages: typing.Optional[list[llm_entities.Message]] | typing.Optional[list[mirai.MessageChain]] = [] + resp_messages: typing.Optional[list[llm_entities.Message]] | typing.Optional[list[platform_message.MessageChain]] = [] """由Process阶段生成的回复消息对象列表""" - resp_message_chain: typing.Optional[list[mirai.MessageChain]] = None + resp_message_chain: typing.Optional[list[platform_message.MessageChain]] = None """回复消息链,从resp_messages包装而得""" # ======= 内部保留 ======= diff --git a/pkg/pipeline/cntfilter/cntfilter.py b/pkg/pipeline/cntfilter/cntfilter.py index 29e66cc4..f0c5428e 100644 --- a/pkg/pipeline/cntfilter/cntfilter.py +++ b/pkg/pipeline/cntfilter/cntfilter.py @@ -1,8 +1,8 @@ from __future__ import annotations -import mirai -import mirai.models -import mirai.models.message +# import mirai +# import mirai.models +# import mirai.models.message from ...core import app @@ -12,6 +12,9 @@ from . import filter as filter_model, entities as filter_entities from .filters import cntignore, banwords, baiduexamine from ...provider import entities as llm_entities +from ...platform.types import message as platform_message +from ...platform.types import events as platform_events +from ...platform.types import entities as platform_entities @stage.stage_class('PostContentFilterStage') @@ -89,8 +92,8 @@ async def _pre_process( elif result.level == filter_entities.ResultLevel.PASS: # 传到下一个 message = result.replacement - query.message_chain = mirai.MessageChain( - mirai.Plain(message) + query.message_chain = platform_message.MessageChain( + platform_message.Plain(message) ) return entities.StageProcessResult( @@ -148,7 +151,7 @@ async def process( contain_non_text = False - text_components = [mirai.Plain, mirai.models.message.Source] + text_components = [platform_message.Plain, platform_message.Source] for me in query.message_chain: if type(me) not in text_components: diff --git a/pkg/pipeline/controller.py b/pkg/pipeline/controller.py index 677db31d..27b96dee 100644 --- a/pkg/pipeline/controller.py +++ b/pkg/pipeline/controller.py @@ -4,11 +4,12 @@ import typing import traceback -import mirai +# import mirai from ..core import app, entities from . import entities as pipeline_entities from ..plugin import events +from ..platform.types import message as platform_message class Controller: @@ -73,11 +74,11 @@ async def _check_output(self, query: entities.Query, result: pipeline_entities.S # 处理str类型 if isinstance(result.user_notice, str): - result.user_notice = mirai.MessageChain( - mirai.Plain(result.user_notice) + result.user_notice = platform_message.MessageChain( + platform_message.Plain(result.user_notice) ) elif isinstance(result.user_notice, list): - result.user_notice = mirai.MessageChain( + result.user_notice = platform_message.MessageChain( *result.user_notice ) diff --git a/pkg/pipeline/entities.py b/pkg/pipeline/entities.py index e8cfc427..347f5db7 100644 --- a/pkg/pipeline/entities.py +++ b/pkg/pipeline/entities.py @@ -4,8 +4,8 @@ import typing import pydantic -import mirai -import mirai.models.message as mirai_message +# import mirai +from ..platform.types import message as platform_message from ..core import entities @@ -25,7 +25,7 @@ class StageProcessResult(pydantic.BaseModel): new_query: entities.Query - user_notice: typing.Optional[typing.Union[str, list[mirai_message.MessageComponent], mirai.MessageChain, None]] = [] + user_notice: typing.Optional[typing.Union[str, list[platform_message.MessageComponent], platform_message.MessageChain, None]] = [] """只要设置了就会发送给用户""" # TODO delete diff --git a/pkg/pipeline/longtext/longtext.py b/pkg/pipeline/longtext/longtext.py index 0ab34abc..d3715b7e 100644 --- a/pkg/pipeline/longtext/longtext.py +++ b/pkg/pipeline/longtext/longtext.py @@ -3,7 +3,7 @@ import traceback from PIL import Image, ImageDraw, ImageFont -from mirai.models.message import MessageComponent, Plain, MessageChain +# from mirai.models.message import MessageComponent, Plain, MessageChain from ...core import app from . import strategy @@ -11,6 +11,7 @@ from .. import stage, entities, stagemgr from ...core import entities as core_entities from ...config import manager as cfg_mgr +from ...platform.types import message as platform_message @stage.stage_class("LongTextProcessStage") @@ -63,14 +64,14 @@ async def process(self, query: core_entities.Query, stage_inst_name: str) -> ent contains_non_plain = False for msg in query.resp_message_chain[-1]: - if not isinstance(msg, Plain): + if not isinstance(msg, platform_message.Plain): contains_non_plain = True break if contains_non_plain: self.ap.logger.debug("消息中包含非 Plain 组件,跳过长消息处理。") elif len(str(query.resp_message_chain[-1])) > self.ap.platform_cfg.data['long-text-process']['threshold']: - query.resp_message_chain[-1] = MessageChain(await self.strategy_impl.process(str(query.resp_message_chain[-1]), query)) + query.resp_message_chain[-1] = platform_message.MessageChain(await self.strategy_impl.process(str(query.resp_message_chain[-1]), query)) return entities.StageProcessResult( result_type=entities.ResultType.CONTINUE, diff --git a/pkg/pipeline/longtext/strategies/forward.py b/pkg/pipeline/longtext/strategies/forward.py index 4a790313..206bf08c 100644 --- a/pkg/pipeline/longtext/strategies/forward.py +++ b/pkg/pipeline/longtext/strategies/forward.py @@ -2,15 +2,17 @@ from __future__ import annotations import typing -from mirai.models import MessageChain -from mirai.models.message import MessageComponent, ForwardMessageNode -from mirai.models.base import MiraiBaseModel +# from mirai.models import MessageChain +# from mirai.models.message import MessageComponent, ForwardMessageNode +# from mirai.models.base import MiraiBaseModel +import pydantic from .. import strategy as strategy_model from ....core import entities as core_entities +from ....platform.types import message as platform_message -class ForwardMessageDiaplay(MiraiBaseModel): +class ForwardMessageDiaplay(pydantic.BaseModel): title: str = "群聊的聊天记录" brief: str = "[聊天记录]" source: str = "聊天记录" @@ -18,13 +20,13 @@ class ForwardMessageDiaplay(MiraiBaseModel): summary: str = "查看x条转发消息" -class Forward(MessageComponent): +class Forward(platform_message.MessageComponent): """合并转发。""" type: str = "Forward" """消息组件类型。""" display: ForwardMessageDiaplay """显示信息""" - node_list: typing.List[ForwardMessageNode] + node_list: typing.List[platform_message.ForwardMessageNode] """转发消息节点列表。""" def __init__(self, *args, **kwargs): if len(args) == 1: @@ -39,7 +41,7 @@ def __str__(self): @strategy_model.strategy_class("forward") class ForwardComponentStrategy(strategy_model.LongTextStrategy): - async def process(self, message: str, query: core_entities.Query) -> list[MessageComponent]: + async def process(self, message: str, query: core_entities.Query) -> list[platform_message.MessageComponent]: display = ForwardMessageDiaplay( title="群聊的聊天记录", brief="[聊天记录]", @@ -49,10 +51,10 @@ async def process(self, message: str, query: core_entities.Query) -> list[Messag ) node_list = [ - ForwardMessageNode( + platform_message.ForwardMessageNode( sender_id=query.adapter.bot_account_id, sender_name='QQ用户', - message_chain=MessageChain([message]) + message_chain=platform_message.MessageChain([message]) ) ] diff --git a/pkg/pipeline/longtext/strategies/image.py b/pkg/pipeline/longtext/strategies/image.py index f96f03c5..3b1805c3 100644 --- a/pkg/pipeline/longtext/strategies/image.py +++ b/pkg/pipeline/longtext/strategies/image.py @@ -8,8 +8,9 @@ from PIL import Image, ImageDraw, ImageFont -from mirai.models import MessageChain, Image as ImageComponent -from mirai.models.message import MessageComponent +# from mirai.models import MessageChain, Image as ImageComponent +# from mirai.models.message import MessageComponent +from ....platform.types import message as platform_message from .. import strategy as strategy_model from ....core import entities as core_entities @@ -23,7 +24,7 @@ class Text2ImageStrategy(strategy_model.LongTextStrategy): async def initialize(self): self.text_render_font = ImageFont.truetype(self.ap.platform_cfg.data['long-text-process']['font-path'], 32, encoding="utf-8") - async def process(self, message: str, query: core_entities.Query) -> list[MessageComponent]: + async def process(self, message: str, query: core_entities.Query) -> list[platform_message.MessageComponent]: img_path = self.text_to_image( text_str=message, save_as='temp/{}.png'.format(int(time.time())) @@ -46,7 +47,7 @@ async def process(self, message: str, query: core_entities.Query) -> list[Messag os.remove(compressed_path) return [ - ImageComponent( + platform_message.Image( base64=b64.decode('utf-8'), ) ] diff --git a/pkg/pipeline/longtext/strategy.py b/pkg/pipeline/longtext/strategy.py index 5d7e24fb..5bc46a39 100644 --- a/pkg/pipeline/longtext/strategy.py +++ b/pkg/pipeline/longtext/strategy.py @@ -2,11 +2,12 @@ import abc import typing -import mirai -from mirai.models.message import MessageComponent +# import mirai +# from mirai.models.message import MessageComponent from ...core import app from ...core import entities as core_entities +from ...platform.types import message as platform_message preregistered_strategies: list[typing.Type[LongTextStrategy]] = [] @@ -51,7 +52,7 @@ async def initialize(self): pass @abc.abstractmethod - async def process(self, message: str, query: core_entities.Query) -> list[MessageComponent]: + async def process(self, message: str, query: core_entities.Query) -> list[platform_message.MessageComponent]: """处理长文本 在 platform.json 中配置 long-text-process 字段,只要 文本长度超过了 threshold 就会调用此方法 @@ -61,6 +62,6 @@ async def process(self, message: str, query: core_entities.Query) -> list[Messag query (core_entities.Query): 此次请求的上下文对象 Returns: - list[mirai.models.messages.MessageComponent]: 转换后的 YiriMirai 消息组件列表 + list[platform_message.MessageComponent]: 转换后的 平台 消息组件列表 """ return [] diff --git a/pkg/pipeline/pool.py b/pkg/pipeline/pool.py index ba7f9991..b7f79510 100644 --- a/pkg/pipeline/pool.py +++ b/pkg/pipeline/pool.py @@ -2,10 +2,12 @@ import asyncio -import mirai +# import mirai from ..core import entities from ..platform import adapter as msadapter +from ..platform.types import message as platform_message +from ..platform.types import events as platform_events class QueryPool: @@ -30,8 +32,8 @@ async def add_query( launcher_type: entities.LauncherTypes, launcher_id: int, sender_id: int, - message_event: mirai.MessageEvent, - message_chain: mirai.MessageChain, + message_event: platform_events.MessageEvent, + message_chain: platform_message.MessageChain, adapter: msadapter.MessageSourceAdapter ) -> entities.Query: async with self.condition: diff --git a/pkg/pipeline/preproc/preproc.py b/pkg/pipeline/preproc/preproc.py index ebe4d311..976c8f7c 100644 --- a/pkg/pipeline/preproc/preproc.py +++ b/pkg/pipeline/preproc/preproc.py @@ -1,11 +1,12 @@ from __future__ import annotations -import mirai +# import mirai from .. import stage, entities, stagemgr from ...core import entities as core_entities from ...provider import entities as llm_entities from ...plugin import events +from ...platform.types import message as platform_message @stage.stage_class("PreProcessor") @@ -55,11 +56,11 @@ async def process( content_list = [] for me in query.message_chain: - if isinstance(me, mirai.Plain): + if isinstance(me, platform_message.Plain): content_list.append( llm_entities.ContentElement.from_text(me.text) ) - elif isinstance(me, mirai.Image): + elif isinstance(me, platform_message.Image): if self.ap.provider_cfg.data['enable-vision'] and query.use_model.vision_supported: if me.url is not None: content_list.append( diff --git a/pkg/pipeline/process/handlers/chat.py b/pkg/pipeline/process/handlers/chat.py index cb8899bd..d87e2853 100644 --- a/pkg/pipeline/process/handlers/chat.py +++ b/pkg/pipeline/process/handlers/chat.py @@ -5,7 +5,7 @@ import traceback import json -import mirai +# import mirai from .. import handler from ... import entities @@ -13,6 +13,8 @@ from ....provider import entities as llm_entities, runnermgr from ....plugin import events +from ....platform.types import message as platform_message + class ChatMessageHandler(handler.MessageHandler): @@ -40,7 +42,7 @@ async def handle( if event_ctx.is_prevented_default(): if event_ctx.event.reply is not None: - mc = mirai.MessageChain(event_ctx.event.reply) + mc = platform_message.MessageChain(event_ctx.event.reply) query.resp_messages.append(mc) diff --git a/pkg/pipeline/process/handlers/command.py b/pkg/pipeline/process/handlers/command.py index 8c8fb8ba..5f6ed6ee 100644 --- a/pkg/pipeline/process/handlers/command.py +++ b/pkg/pipeline/process/handlers/command.py @@ -1,13 +1,14 @@ from __future__ import annotations import typing -import mirai +# import mirai from .. import handler from ... import entities from ....core import entities as core_entities from ....provider import entities as llm_entities from ....plugin import events +from ....platform.types import message as platform_message class CommandHandler(handler.MessageHandler): @@ -46,7 +47,7 @@ async def handle( if event_ctx.is_prevented_default(): if event_ctx.event.reply is not None: - mc = mirai.MessageChain(event_ctx.event.reply) + mc = platform_message.MessageChain(event_ctx.event.reply) query.resp_messages.append(mc) @@ -63,8 +64,8 @@ async def handle( else: if event_ctx.event.alter is not None: - query.message_chain = mirai.MessageChain([ - mirai.Plain(event_ctx.event.alter) + query.message_chain = platform_message.MessageChain([ + platform_message.Plain(event_ctx.event.alter) ]) session = await self.ap.sess_mgr.get_session(query) diff --git a/pkg/pipeline/respback/respback.py b/pkg/pipeline/respback/respback.py index d3af14e5..1b0cd3ac 100644 --- a/pkg/pipeline/respback/respback.py +++ b/pkg/pipeline/respback/respback.py @@ -3,7 +3,7 @@ import random import asyncio -import mirai +# import mirai from ...core import app diff --git a/pkg/pipeline/resprule/entities.py b/pkg/pipeline/resprule/entities.py index ffee3081..42c2092e 100644 --- a/pkg/pipeline/resprule/entities.py +++ b/pkg/pipeline/resprule/entities.py @@ -1,9 +1,11 @@ import pydantic -import mirai +# import mirai + +from ...platform.types import message as platform_message class RuleJudgeResult(pydantic.BaseModel): matching: bool = False - replacement: mirai.MessageChain = None + replacement: platform_message.MessageChain = None diff --git a/pkg/pipeline/resprule/resprule.py b/pkg/pipeline/resprule/resprule.py index b7fdb372..aa07bb7c 100644 --- a/pkg/pipeline/resprule/resprule.py +++ b/pkg/pipeline/resprule/resprule.py @@ -1,6 +1,6 @@ from __future__ import annotations -import mirai +# import mirai from ...core import app from . import entities as rule_entities, rule diff --git a/pkg/pipeline/resprule/rule.py b/pkg/pipeline/resprule/rule.py index bfab4152..b40e5ac5 100644 --- a/pkg/pipeline/resprule/rule.py +++ b/pkg/pipeline/resprule/rule.py @@ -2,11 +2,13 @@ import abc import typing -import mirai +# import mirai from ...core import app, entities as core_entities from . import entities +from ...platform.types import message as platform_message + preregisetered_rules: list[typing.Type[GroupRespondRule]] = [] @@ -35,7 +37,7 @@ async def initialize(self): async def match( self, message_text: str, - message_chain: mirai.MessageChain, + message_chain: platform_message.MessageChain, rule_dict: dict, query: core_entities.Query ) -> entities.RuleJudgeResult: diff --git a/pkg/pipeline/resprule/rules/atbot.py b/pkg/pipeline/resprule/rules/atbot.py index 4b39409c..d767f95a 100644 --- a/pkg/pipeline/resprule/rules/atbot.py +++ b/pkg/pipeline/resprule/rules/atbot.py @@ -1,10 +1,11 @@ from __future__ import annotations -import mirai +# import mirai from .. import rule as rule_model from .. import entities from ....core import entities as core_entities +from ....platform.types import message as platform_message @rule_model.rule_class("at-bot") @@ -13,16 +14,16 @@ class AtBotRule(rule_model.GroupRespondRule): async def match( self, message_text: str, - message_chain: mirai.MessageChain, + message_chain: platform_message.MessageChain, rule_dict: dict, query: core_entities.Query ) -> entities.RuleJudgeResult: - if message_chain.has(mirai.At(query.adapter.bot_account_id)) and rule_dict['at']: - message_chain.remove(mirai.At(query.adapter.bot_account_id)) + if message_chain.has(platform_message.At(query.adapter.bot_account_id)) and rule_dict['at']: + message_chain.remove(platform_message.At(query.adapter.bot_account_id)) - if message_chain.has(mirai.At(query.adapter.bot_account_id)): # 回复消息时会at两次,检查并删除重复的 - message_chain.remove(mirai.At(query.adapter.bot_account_id)) + if message_chain.has(platform_message.At(query.adapter.bot_account_id)): # 回复消息时会at两次,检查并删除重复的 + message_chain.remove(platform_message.At(query.adapter.bot_account_id)) return entities.RuleJudgeResult( matching=True, diff --git a/pkg/pipeline/resprule/rules/prefix.py b/pkg/pipeline/resprule/rules/prefix.py index 98b50321..c010c0f8 100644 --- a/pkg/pipeline/resprule/rules/prefix.py +++ b/pkg/pipeline/resprule/rules/prefix.py @@ -1,8 +1,9 @@ -import mirai +# import mirai from .. import rule as rule_model from .. import entities from ....core import entities as core_entities +from ....platform.types import message as platform_message @rule_model.rule_class("prefix") @@ -11,7 +12,7 @@ class PrefixRule(rule_model.GroupRespondRule): async def match( self, message_text: str, - message_chain: mirai.MessageChain, + message_chain: platform_message.MessageChain, rule_dict: dict, query: core_entities.Query ) -> entities.RuleJudgeResult: @@ -22,7 +23,7 @@ async def match( # 查找第一个plain元素 for me in message_chain: - if isinstance(me, mirai.Plain): + if isinstance(me, platform_message.Plain): me.text = me.text[len(prefix):] return entities.RuleJudgeResult( diff --git a/pkg/pipeline/resprule/rules/random.py b/pkg/pipeline/resprule/rules/random.py index 80acf6a5..bf8603a9 100644 --- a/pkg/pipeline/resprule/rules/random.py +++ b/pkg/pipeline/resprule/rules/random.py @@ -1,10 +1,11 @@ import random -import mirai +# import mirai from .. import rule as rule_model from .. import entities from ....core import entities as core_entities +from ....platform.types import message as platform_message @rule_model.rule_class("random") @@ -13,7 +14,7 @@ class RandomRespRule(rule_model.GroupRespondRule): async def match( self, message_text: str, - message_chain: mirai.MessageChain, + message_chain: platform_message.MessageChain, rule_dict: dict, query: core_entities.Query ) -> entities.RuleJudgeResult: diff --git a/pkg/pipeline/resprule/rules/regexp.py b/pkg/pipeline/resprule/rules/regexp.py index aaa46449..a6a3ecd2 100644 --- a/pkg/pipeline/resprule/rules/regexp.py +++ b/pkg/pipeline/resprule/rules/regexp.py @@ -1,10 +1,11 @@ import re -import mirai +# import mirai from .. import rule as rule_model from .. import entities from ....core import entities as core_entities +from ....platform.types import message as platform_message @rule_model.rule_class("regexp") @@ -13,7 +14,7 @@ class RegExpRule(rule_model.GroupRespondRule): async def match( self, message_text: str, - message_chain: mirai.MessageChain, + message_chain: platform_message.MessageChain, rule_dict: dict, query: core_entities.Query ) -> entities.RuleJudgeResult: diff --git a/pkg/pipeline/wrapper/wrapper.py b/pkg/pipeline/wrapper/wrapper.py index e6ce99aa..0e093826 100644 --- a/pkg/pipeline/wrapper/wrapper.py +++ b/pkg/pipeline/wrapper/wrapper.py @@ -2,7 +2,7 @@ import typing -import mirai +# import mirai from ...core import app, entities as core_entities from .. import entities @@ -10,6 +10,7 @@ from ...core import entities as core_entities from ...config import manager as cfg_mgr from ...plugin import events +from ...platform.types import message as platform_message @stage.stage_class("ResponseWrapper") @@ -34,7 +35,7 @@ async def process( """ # 如果 resp_messages[-1] 已经是 MessageChain 了 - if isinstance(query.resp_messages[-1], mirai.MessageChain): + if isinstance(query.resp_messages[-1], platform_message.MessageChain): query.resp_message_chain.append(query.resp_messages[-1]) yield entities.StageProcessResult( @@ -96,7 +97,7 @@ async def process( else: if event_ctx.event.reply is not None: - query.resp_message_chain.append(mirai.MessageChain(event_ctx.event.reply)) + query.resp_message_chain.append(platform_message.MessageChain(event_ctx.event.reply)) else: @@ -113,7 +114,7 @@ async def process( reply_text = f'调用函数 {".".join(function_names)}...' - query.resp_message_chain.append(mirai.MessageChain([mirai.Plain(reply_text)])) + query.resp_message_chain.append(platform_message.MessageChain([platform_message.Plain(reply_text)])) if self.ap.platform_cfg.data['track-function-calls']: @@ -139,11 +140,11 @@ async def process( else: if event_ctx.event.reply is not None: - query.resp_message_chain.append(mirai.MessageChain(event_ctx.event.reply)) + query.resp_message_chain.append(platform_message.MessageChain(event_ctx.event.reply)) else: - query.resp_message_chain.append(mirai.MessageChain([mirai.Plain(reply_text)])) + query.resp_message_chain.append(platform_message.MessageChain([platform_message.Plain(reply_text)])) yield entities.StageProcessResult( result_type=entities.ResultType.CONTINUE, diff --git a/pkg/platform/adapter.py b/pkg/platform/adapter.py index 4b159b79..c6d97b3a 100644 --- a/pkg/platform/adapter.py +++ b/pkg/platform/adapter.py @@ -4,9 +4,11 @@ import typing import abc -import mirai +# import mirai from ..core import app +from .types import message as platform_message +from .types import events as platform_events preregistered_adapters: list[typing.Type[MessageSourceAdapter]] = [] @@ -55,7 +57,7 @@ async def send_message( self, target_type: str, target_id: str, - message: mirai.MessageChain + message: platform_message.MessageChain ): """主动发送消息 @@ -68,8 +70,8 @@ async def send_message( async def reply_message( self, - message_source: mirai.MessageEvent, - message: mirai.MessageChain, + message_source: platform_events.MessageEvent, + message: platform_message.MessageChain, quote_origin: bool = False ): """回复消息 @@ -87,8 +89,8 @@ async def is_muted(self, group_id: int) -> bool: def register_listener( self, - event_type: typing.Type[mirai.Event], - callback: typing.Callable[[mirai.Event, MessageSourceAdapter], None] + event_type: typing.Type[platform_message.Event], + callback: typing.Callable[[platform_message.Event, MessageSourceAdapter], None] ): """注册事件监听器 @@ -100,8 +102,8 @@ def register_listener( def unregister_listener( self, - event_type: typing.Type[mirai.Event], - callback: typing.Callable[[mirai.Event, MessageSourceAdapter], None] + event_type: typing.Type[platform_message.Event], + callback: typing.Callable[[platform_message.Event, MessageSourceAdapter], None] ): """注销事件监听器 @@ -127,7 +129,7 @@ async def kill(self) -> bool: class MessageConverter: """消息链转换器基类""" @staticmethod - def yiri2target(message_chain: mirai.MessageChain): + def yiri2target(message_chain: platform_message.MessageChain): """将YiriMirai消息链转换为目标消息链 Args: @@ -139,7 +141,7 @@ def yiri2target(message_chain: mirai.MessageChain): raise NotImplementedError @staticmethod - def target2yiri(message_chain: typing.Any) -> mirai.MessageChain: + def target2yiri(message_chain: typing.Any) -> platform_message.MessageChain: """将目标消息链转换为YiriMirai消息链 Args: @@ -155,7 +157,7 @@ class EventConverter: """事件转换器基类""" @staticmethod - def yiri2target(event: typing.Type[mirai.Event]): + def yiri2target(event: typing.Type[platform_message.Event]): """将YiriMirai事件转换为目标事件 Args: @@ -167,7 +169,7 @@ def yiri2target(event: typing.Type[mirai.Event]): raise NotImplementedError @staticmethod - def target2yiri(event: typing.Any) -> mirai.Event: + def target2yiri(event: typing.Any) -> platform_message.Event: """将目标事件的调用参数转换为YiriMirai的事件参数对象 Args: diff --git a/pkg/platform/manager.py b/pkg/platform/manager.py index b9696426..4e5131d5 100644 --- a/pkg/platform/manager.py +++ b/pkg/platform/manager.py @@ -6,13 +6,17 @@ import asyncio import traceback -from mirai import At, GroupMessage, MessageEvent, StrangerMessage, \ - FriendMessage, Image, MessageChain, Plain -import mirai +# from mirai import At, GroupMessage, MessageEvent, StrangerMessage, \ +# FriendMessage, Image, MessageChain, Plain +# import mirai from ..platform import adapter as msadapter from ..core import app, entities as core_entities from ..plugin import events +from .types import message as platform_message +from .types import events as platform_events +from .types import entities as platform_entities + # 控制QQ消息输入输出的类 class PlatformManager: @@ -32,7 +36,7 @@ async def initialize(self): from .sources import yirimirai, nakuru, aiocqhttp, qqbotpy - async def on_friend_message(event: FriendMessage, adapter: msadapter.MessageSourceAdapter): + async def on_friend_message(event: platform_events.FriendMessage, adapter: msadapter.MessageSourceAdapter): event_ctx = await self.ap.plugin_mgr.emit_event( event=events.PersonMessageReceived( @@ -55,7 +59,7 @@ async def on_friend_message(event: FriendMessage, adapter: msadapter.MessageSour adapter=adapter ) - async def on_stranger_message(event: StrangerMessage, adapter: msadapter.MessageSourceAdapter): + async def on_stranger_message(event: platform_events.StrangerMessage, adapter: msadapter.MessageSourceAdapter): event_ctx = await self.ap.plugin_mgr.emit_event( event=events.PersonMessageReceived( @@ -78,7 +82,7 @@ async def on_stranger_message(event: StrangerMessage, adapter: msadapter.Message adapter=adapter ) - async def on_group_message(event: GroupMessage, adapter: msadapter.MessageSourceAdapter): + async def on_group_message(event: platform_events.GroupMessage, adapter: msadapter.MessageSourceAdapter): event_ctx = await self.ap.plugin_mgr.emit_event( event=events.GroupMessageReceived( @@ -127,16 +131,16 @@ async def on_group_message(event: GroupMessage, adapter: msadapter.MessageSource if adapter_name == 'yiri-mirai': adapter_inst.register_listener( - StrangerMessage, + platform_events.StrangerMessage, on_stranger_message ) adapter_inst.register_listener( - FriendMessage, + platform_events.FriendMessage, on_friend_message ) adapter_inst.register_listener( - GroupMessage, + platform_events.GroupMessage, on_group_message ) @@ -146,13 +150,13 @@ async def on_group_message(event: GroupMessage, adapter: msadapter.MessageSource if len(self.adapters) == 0: self.ap.logger.warning('未运行平台适配器,请根据文档配置并启用平台适配器。') - async def send(self, event: mirai.MessageEvent, msg: mirai.MessageChain, adapter: msadapter.MessageSourceAdapter): + async def send(self, event: platform_events.MessageEvent, msg: platform_message.MessageChain, adapter: msadapter.MessageSourceAdapter): - if self.ap.platform_cfg.data['at-sender'] and isinstance(event, GroupMessage): + if self.ap.platform_cfg.data['at-sender'] and isinstance(event, platform_events.GroupMessage): msg.insert( 0, - At( + platform_message.At( event.sender.id ) ) diff --git a/pkg/platform/sources/aiocqhttp.py b/pkg/platform/sources/aiocqhttp.py index ebdd56e5..d4278768 100644 --- a/pkg/platform/sources/aiocqhttp.py +++ b/pkg/platform/sources/aiocqhttp.py @@ -5,31 +5,34 @@ import time import datetime -import mirai -import mirai.models.message as yiri_message +# import mirai +# import mirai.models.message as yiri_message import aiocqhttp from .. import adapter from ...pipeline.longtext.strategies import forward from ...core import app +from ..types import message as platform_message +from ..types import events as platform_events +from ..types import entities as platform_entities class AiocqhttpMessageConverter(adapter.MessageConverter): @staticmethod - def yiri2target(message_chain: mirai.MessageChain) -> typing.Tuple[list, int, datetime.datetime]: + def yiri2target(message_chain: platform_message.MessageChain) -> typing.Tuple[list, int, datetime.datetime]: msg_list = aiocqhttp.Message() msg_id = 0 msg_time = None for msg in message_chain: - if type(msg) is mirai.Plain: + if type(msg) is platform_message.Plain: msg_list.append(aiocqhttp.MessageSegment.text(msg.text)) - elif type(msg) is yiri_message.Source: + elif type(msg) is platform_message.Source: msg_id = msg.id msg_time = msg.time - elif type(msg) is mirai.Image: + elif type(msg) is platform_message.Image: arg = '' if msg.base64: arg = msg.base64 @@ -40,13 +43,11 @@ def yiri2target(message_chain: mirai.MessageChain) -> typing.Tuple[list, int, da elif msg.path: arg = msg.path msg_list.append(aiocqhttp.MessageSegment.image(arg)) - elif type(msg) is mirai.At: + elif type(msg) is platform_message.At: msg_list.append(aiocqhttp.MessageSegment.at(msg.target)) - elif type(msg) is mirai.AtAll: + elif type(msg) is platform_message.AtAll: msg_list.append(aiocqhttp.MessageSegment.at("all")) - elif type(msg) is mirai.Face: - msg_list.append(aiocqhttp.MessageSegment.face(msg.face_id)) - elif type(msg) is mirai.Voice: + elif type(msg) is platform_message.Voice: arg = '' if msg.base64: arg = msg.base64 @@ -74,25 +75,25 @@ def target2yiri(message: str, message_id: int = -1): yiri_msg_list = [] yiri_msg_list.append( - yiri_message.Source(id=message_id, time=datetime.datetime.now()) + platform_message.Source(id=message_id, time=datetime.datetime.now()) ) for msg in message: if msg.type == "at": if msg.data["qq"] == "all": - yiri_msg_list.append(yiri_message.AtAll()) + yiri_msg_list.append(platform_message.AtAll()) else: yiri_msg_list.append( - yiri_message.At( + platform_message.At( target=msg.data["qq"], ) ) elif msg.type == "text": - yiri_msg_list.append(yiri_message.Plain(text=msg.data["text"])) + yiri_msg_list.append(platform_message.Plain(text=msg.data["text"])) elif msg.type == "image": - yiri_msg_list.append(yiri_message.Image(url=msg.data["url"])) + yiri_msg_list.append(platform_message.Image(url=msg.data["url"])) - chain = mirai.MessageChain(yiri_msg_list) + chain = platform_message.MessageChain(yiri_msg_list) return chain @@ -100,11 +101,11 @@ def target2yiri(message: str, message_id: int = -1): class AiocqhttpEventConverter(adapter.EventConverter): @staticmethod - def yiri2target(event: mirai.Event, bot_account_id: int): + def yiri2target(event: platform_events.Event, bot_account_id: int): msg, msg_id, msg_time = AiocqhttpMessageConverter.yiri2target(event.message_chain) - if type(event) is mirai.GroupMessage: + if type(event) is platform_events.GroupMessage: role = "member" if event.sender.permission == "ADMINISTRATOR": @@ -140,7 +141,7 @@ def yiri2target(event: mirai.Event, bot_account_id: int): } return aiocqhttp.Event.from_payload(payload) - elif type(event) is mirai.FriendMessage: + elif type(event) is platform_events.FriendMessage: payload = { "post_type": "message", @@ -177,15 +178,15 @@ def target2yiri(event: aiocqhttp.Event): permission = "ADMINISTRATOR" elif event.sender["role"] == "owner": permission = "OWNER" - converted_event = mirai.GroupMessage( - sender=mirai.models.entities.GroupMember( + converted_event = platform_events.GroupMessage( + sender=platform_entities.GroupMember( id=event.sender["user_id"], # message_seq 放哪? member_name=event.sender["nickname"], permission=permission, - group=mirai.models.entities.Group( + group=platform_entities.Group( id=event.group_id, name=event.sender["nickname"], - permission=mirai.models.entities.Permission.Member, + permission=platform_entities.Permission.Member, ), special_title=event.sender["title"] if "title" in event.sender else "", join_timestamp=0, @@ -197,8 +198,8 @@ def target2yiri(event: aiocqhttp.Event): ) return converted_event elif event.message_type == "private": - return mirai.FriendMessage( - sender=mirai.models.entities.Friend( + return platform_events.FriendMessage( + sender=platform_entities.Friend( id=event.sender["user_id"], nickname=event.sender["nickname"], remark="", @@ -240,7 +241,7 @@ async def shutdown_trigger_placeholder(): self.bot = aiocqhttp.CQHttp() async def send_message( - self, target_type: str, target_id: str, message: mirai.MessageChain + self, target_type: str, target_id: str, message: platform_message.MessageChain ): aiocq_msg = AiocqhttpMessageConverter.yiri2target(message)[0] @@ -251,8 +252,8 @@ async def send_message( async def reply_message( self, - message_source: mirai.MessageEvent, - message: mirai.MessageChain, + message_source: platform_events.MessageEvent, + message: platform_message.MessageChain, quote_origin: bool = False, ): aiocq_event = AiocqhttpEventConverter.yiri2target(message_source, self.bot_account_id) @@ -270,8 +271,8 @@ async def is_muted(self, group_id: int) -> bool: def register_listener( self, - event_type: typing.Type[mirai.Event], - callback: typing.Callable[[mirai.Event, adapter.MessageSourceAdapter], None], + event_type: typing.Type[platform_events.Event], + callback: typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None], ): async def on_message(event: aiocqhttp.Event): self.bot_account_id = event.self_id @@ -280,15 +281,15 @@ async def on_message(event: aiocqhttp.Event): except: traceback.print_exc() - if event_type == mirai.GroupMessage: + if event_type == platform_events.GroupMessage: self.bot.on_message("group")(on_message) - elif event_type == mirai.FriendMessage: + elif event_type == platform_events.FriendMessage: self.bot.on_message("private")(on_message) def unregister_listener( self, - event_type: typing.Type[mirai.Event], - callback: typing.Callable[[mirai.Event, adapter.MessageSourceAdapter], None], + event_type: typing.Type[platform_events.Event], + callback: typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None], ): return super().unregister_listener(event_type, callback) diff --git a/pkg/platform/sources/nakuru.py b/pkg/platform/sources/nakuru.py index 94c29812..5c05f9d4 100644 --- a/pkg/platform/sources/nakuru.py +++ b/pkg/platform/sources/nakuru.py @@ -6,26 +6,29 @@ import traceback import logging -import mirai +# import mirai import nakuru import nakuru.entities.components as nkc from .. import adapter as adapter_model from ...pipeline.longtext.strategies import forward +from ...platform.types import message as platform_message +from ...platform.types import entities as platform_entities +from ...platform.types import events as platform_events class NakuruProjectMessageConverter(adapter_model.MessageConverter): """消息转换器""" @staticmethod - def yiri2target(message_chain: mirai.MessageChain) -> list: + def yiri2target(message_chain: platform_message.MessageChain) -> list: msg_list = [] - if type(message_chain) is mirai.MessageChain: + if type(message_chain) is platform_message.MessageChain: msg_list = message_chain.__root__ elif type(message_chain) is list: msg_list = message_chain elif type(message_chain) is str: - msg_list = [mirai.Plain(message_chain)] + msg_list = [platform_message.Plain(message_chain)] else: raise Exception("Unknown message type: " + str(message_chain) + str(type(message_chain))) @@ -33,22 +36,20 @@ def yiri2target(message_chain: mirai.MessageChain) -> list: # 遍历并转换 for component in msg_list: - if type(component) is mirai.Plain: + if type(component) is platform_message.Plain: nakuru_msg_list.append(nkc.Plain(component.text, False)) - elif type(component) is mirai.Image: + elif type(component) is platform_message.Image: if component.url is not None: nakuru_msg_list.append(nkc.Image.fromURL(component.url)) elif component.base64 is not None: nakuru_msg_list.append(nkc.Image.fromBase64(component.base64)) elif component.path is not None: nakuru_msg_list.append(nkc.Image.fromFileSystem(component.path)) - elif type(component) is mirai.Face: - nakuru_msg_list.append(nkc.Face(id=component.face_id)) - elif type(component) is mirai.At: + elif type(component) is platform_message.At: nakuru_msg_list.append(nkc.At(qq=component.target)) - elif type(component) is mirai.AtAll: + elif type(component) is platform_message.AtAll: nakuru_msg_list.append(nkc.AtAll()) - elif type(component) is mirai.Voice: + elif type(component) is platform_message.Voice: if component.url is not None: nakuru_msg_list.append(nkc.Record.fromURL(component.url)) elif component.path is not None: @@ -80,49 +81,47 @@ def yiri2target(message_chain: mirai.MessageChain) -> list: return nakuru_msg_list @staticmethod - def target2yiri(message_chain: typing.Any, message_id: int = -1) -> mirai.MessageChain: + def target2yiri(message_chain: typing.Any, message_id: int = -1) -> platform_message.MessageChain: """将Yiri的消息链转换为YiriMirai的消息链""" assert type(message_chain) is list yiri_msg_list = [] import datetime # 添加Source组件以标记message_id等信息 - yiri_msg_list.append(mirai.models.message.Source(id=message_id, time=datetime.datetime.now())) + yiri_msg_list.append(platform_message.Source(id=message_id, time=datetime.datetime.now())) for component in message_chain: if type(component) is nkc.Plain: - yiri_msg_list.append(mirai.Plain(text=component.text)) + yiri_msg_list.append(platform_message.Plain(text=component.text)) elif type(component) is nkc.Image: - yiri_msg_list.append(mirai.Image(url=component.url)) - elif type(component) is nkc.Face: - yiri_msg_list.append(mirai.Face(face_id=component.id)) + yiri_msg_list.append(platform_message.Image(url=component.url)) elif type(component) is nkc.At: - yiri_msg_list.append(mirai.At(target=component.qq)) + yiri_msg_list.append(platform_message.At(target=component.qq)) elif type(component) is nkc.AtAll: - yiri_msg_list.append(mirai.AtAll()) + yiri_msg_list.append(platform_message.AtAll()) else: pass # logging.debug("转换后的消息链: " + str(yiri_msg_list)) - chain = mirai.MessageChain(yiri_msg_list) + chain = platform_message.MessageChain(yiri_msg_list) return chain class NakuruProjectEventConverter(adapter_model.EventConverter): """事件转换器""" @staticmethod - def yiri2target(event: typing.Type[mirai.Event]): - if event is mirai.GroupMessage: + def yiri2target(event: typing.Type[platform_events.Event]): + if event is platform_events.GroupMessage: return nakuru.GroupMessage - elif event is mirai.FriendMessage: + elif event is platform_events.FriendMessage: return nakuru.FriendMessage else: raise Exception("未支持转换的事件类型: " + str(event)) @staticmethod - def target2yiri(event: typing.Any) -> mirai.Event: + def target2yiri(event: typing.Any) -> platform_events.Event: yiri_chain = NakuruProjectMessageConverter.target2yiri(event.message, event.message_id) if type(event) is nakuru.FriendMessage: # 私聊消息事件 - return mirai.FriendMessage( - sender=mirai.models.entities.Friend( + return platform_events.FriendMessage( + sender=platform_entities.Friend( id=event.sender.user_id, nickname=event.sender.nickname, remark=event.sender.nickname @@ -138,16 +137,15 @@ def target2yiri(event: typing.Any) -> mirai.Event: elif event.sender.role == "owner": permission = "OWNER" - import mirai.models.entities as entities - return mirai.GroupMessage( - sender=mirai.models.entities.GroupMember( + return platform_events.GroupMessage( + sender=platform_entities.GroupMember( id=event.sender.user_id, member_name=event.sender.nickname, permission=permission, - group=mirai.models.entities.Group( + group=platform_entities.Group( id=event.group_id, name=event.sender.nickname, - permission=entities.Permission.Member + permission=platform_entities.Permission.Member ), special_title=event.sender.title, join_timestamp=0, @@ -189,7 +187,7 @@ async def send_message( self, target_type: str, target_id: str, - message: typing.Union[mirai.MessageChain, list], + message: typing.Union[platform_message.MessageChain, list], converted: bool = False ): task = None @@ -222,8 +220,8 @@ async def send_message( async def reply_message( self, - message_source: mirai.MessageEvent, - message: mirai.MessageChain, + message_source: platform_events.MessageEvent, + message: platform_message.MessageChain, quote_origin: bool = False ): message = self.message_converter.yiri2target(message) @@ -233,14 +231,14 @@ async def reply_message( id=message_source.message_chain.message_id, ) ) - if type(message_source) is mirai.GroupMessage: + if type(message_source) is platform_events.GroupMessage: await self.send_message( "group", message_source.sender.group.id, message, converted=True ) - elif type(message_source) is mirai.FriendMessage: + elif type(message_source) is platform_events.FriendMessage: await self.send_message( "person", message_source.sender.id, @@ -258,8 +256,8 @@ def is_muted(self, group_id: int) -> bool: def register_listener( self, - event_type: typing.Type[mirai.Event], - callback: typing.Callable[[mirai.Event, adapter_model.MessageSourceAdapter], None] + event_type: typing.Type[platform_events.Event], + callback: typing.Callable[[platform_events.Event, adapter_model.MessageSourceAdapter], None] ): try: @@ -286,8 +284,8 @@ async def listener_wrapper(app: nakuru.CQHTTP, source: source_cls): def unregister_listener( self, - event_type: typing.Type[mirai.Event], - callback: typing.Callable[[mirai.Event, adapter_model.MessageSourceAdapter], None] + event_type: typing.Type[platform_events.Event], + callback: typing.Callable[[platform_events.Event, adapter_model.MessageSourceAdapter], None] ): nakuru_event_name = self.event_converter.yiri2target(event_type).__name__ diff --git a/pkg/platform/sources/qqbotpy.py b/pkg/platform/sources/qqbotpy.py index a79013c3..5a882c93 100644 --- a/pkg/platform/sources/qqbotpy.py +++ b/pkg/platform/sources/qqbotpy.py @@ -6,7 +6,7 @@ import re import traceback -import mirai +# import mirai import botpy import botpy.message as botpy_message import botpy.types.message as botpy_message_type @@ -17,17 +17,21 @@ from ...pipeline.longtext.strategies import forward from ...core import app from ...config import manager as cfg_mgr +from ...platform.types import entities as platform_entities +from ...platform.types import events as platform_events +from ...platform.types import message as platform_message -class OfficialGroupMessage(mirai.GroupMessage): + +class OfficialGroupMessage(platform_events.GroupMessage): pass -class OfficialFriendMessage(mirai.FriendMessage): +class OfficialFriendMessage(platform_events.FriendMessage): pass event_handler_mapping = { - mirai.GroupMessage: ["on_at_message_create", "on_group_at_message_create"], - mirai.FriendMessage: ["on_direct_message_create", "on_c2c_message_create"], + platform_events.GroupMessage: ["on_at_message_create", "on_group_at_message_create"], + platform_events.FriendMessage: ["on_direct_message_create", "on_c2c_message_create"], } @@ -123,16 +127,16 @@ class OfficialMessageConverter(adapter_model.MessageConverter): """QQ 官方消息转换器""" @staticmethod - def yiri2target(message_chain: mirai.MessageChain): + def yiri2target(message_chain: platform_message.MessageChain): """将 YiriMirai 的消息链转换为 QQ 官方消息""" msg_list = [] - if type(message_chain) is mirai.MessageChain: + if type(message_chain) is platform_message.MessageChain: msg_list = message_chain.__root__ elif type(message_chain) is list: msg_list = message_chain elif type(message_chain) is str: - msg_list = [mirai.Plain(text=message_chain)] + msg_list = [platform_message.Plain(text=message_chain)] else: raise Exception( "Unknown message type: " + str(message_chain) + str(type(message_chain)) @@ -153,22 +157,22 @@ def yiri2target(message_chain: mirai.MessageChain): # 遍历并转换 for component in msg_list: - if type(component) is mirai.Plain: + if type(component) is platform_message.Plain: offcial_messages.append({"type": "text", "content": component.text}) - elif type(component) is mirai.Image: + elif type(component) is platform_message.Image: if component.url is not None: offcial_messages.append({"type": "image", "content": component.url}) elif component.path is not None: offcial_messages.append( {"type": "file_image", "content": component.path} ) - elif type(component) is mirai.At: + elif type(component) is platform_message.At: offcial_messages.append({"type": "at", "content": ""}) - elif type(component) is mirai.AtAll: + elif type(component) is platform_message.AtAll: print( "上层组件要求发送 AtAll 消息,但 QQ 官方 API 不支持此消息类型,忽略此消息。" ) - elif type(component) is mirai.Voice: + elif type(component) is platform_message.Voice: print( "上层组件要求发送 Voice 消息,但 QQ 官方 API 不支持此消息类型,忽略此消息。" ) @@ -197,29 +201,29 @@ def extract_message_chain_from_obj( message: typing.Union[botpy_message.Message, botpy_message.DirectMessage, botpy_message.GroupMessage, botpy_message.C2CMessage], message_id: str = None, bot_account_id: int = 0, - ) -> mirai.MessageChain: + ) -> platform_message.MessageChain: yiri_msg_list = [] # 存id yiri_msg_list.append( - mirai.models.message.Source( + platform_message.Source( id=save_msg_id(message_id), time=datetime.datetime.now() ) ) if type(message) not in [botpy_message.DirectMessage, botpy_message.C2CMessage]: - yiri_msg_list.append(mirai.At(target=bot_account_id)) + yiri_msg_list.append(platform_message.At(target=bot_account_id)) if hasattr(message, "mentions"): for mention in message.mentions: if mention.bot: continue - yiri_msg_list.append(mirai.At(target=mention.id)) + yiri_msg_list.append(platform_message.At(target=mention.id)) for attachment in message.attachments: if attachment.content_type.startswith("image"): - yiri_msg_list.append(mirai.Image(url=attachment.url)) + yiri_msg_list.append(platform_message.Image(url=attachment.url)) else: logging.warning( "不支持的附件类型:" + attachment.content_type + ",忽略此附件。" @@ -227,9 +231,9 @@ def extract_message_chain_from_obj( content = re.sub(r"<@!\d+>", "", str(message.content)) if content.strip() != "": - yiri_msg_list.append(mirai.Plain(text=content)) + yiri_msg_list.append(platform_message.Plain(text=content)) - chain = mirai.MessageChain(yiri_msg_list) + chain = platform_message.MessageChain(yiri_msg_list) return chain @@ -244,10 +248,10 @@ def __init__(self, member_openid_mapping: OpenIDMapping[str, int], group_openid_ self.member_openid_mapping = member_openid_mapping self.group_openid_mapping = group_openid_mapping - def yiri2target(self, event: typing.Type[mirai.Event]): - if event == mirai.GroupMessage: + def yiri2target(self, event: typing.Type[platform_events.Event]): + if event == platform_events.GroupMessage: return botpy_message.Message - elif event == mirai.FriendMessage: + elif event == platform_events.FriendMessage: return botpy_message.DirectMessage else: raise Exception( @@ -257,8 +261,7 @@ def yiri2target(self, event: typing.Type[mirai.Event]): def target2yiri( self, event: typing.Union[botpy_message.Message, botpy_message.DirectMessage, botpy_message.GroupMessage, botpy_message.C2CMessage], - ) -> mirai.Event: - import mirai.models.entities as mirai_entities + ) -> platform_events.Event: if type(event) == botpy_message.Message: # 频道内,转群聊事件 permission = "MEMBER" @@ -268,15 +271,15 @@ def target2yiri( elif "4" in event.member.roles: permission = "OWNER" - return mirai.GroupMessage( - sender=mirai_entities.GroupMember( + return platform_events.GroupMessage( + sender=platform_entities.GroupMember( id=event.author.id, member_name=event.author.username, permission=permission, - group=mirai_entities.Group( + group=platform_entities.Group( id=event.channel_id, name=event.author.username, - permission=mirai_entities.Permission.Member, + permission=platform_entities.Permission.Member, ), special_title="", join_timestamp=int( @@ -297,8 +300,8 @@ def target2yiri( ), ) elif type(event) == botpy_message.DirectMessage: # 频道私聊,转私聊事件 - return mirai.FriendMessage( - sender=mirai_entities.Friend( + return platform_events.FriendMessage( + sender=platform_entities.Friend( id=event.guild_id, nickname=event.author.username, remark=event.author.username, @@ -317,14 +320,14 @@ def target2yiri( replacing_member_id = self.member_openid_mapping.save_openid(event.author.member_openid) return OfficialGroupMessage( - sender=mirai_entities.GroupMember( + sender=platform_entities.GroupMember( id=replacing_member_id, member_name=replacing_member_id, permission="MEMBER", - group=mirai_entities.Group( + group=platform_entities.Group( id=self.group_openid_mapping.save_openid(event.group_openid), name=replacing_member_id, - permission=mirai_entities.Permission.Member, + permission=platform_entities.Permission.Member, ), special_title="", join_timestamp=int(0), @@ -345,7 +348,7 @@ def target2yiri( user_id_alter = self.member_openid_mapping.save_openid(event.author.user_openid) # 实测这里的user_openid与group的member_openid是一样的 return OfficialFriendMessage( - sender=mirai_entities.Friend( + sender=platform_entities.Friend( id=user_id_alter, nickname=user_id_alter, remark=user_id_alter, @@ -410,7 +413,7 @@ def __init__(self, cfg: dict, ap: app.Application): self.bot = botpy.Client(intents=intents) async def send_message( - self, target_type: str, target_id: str, message: mirai.MessageChain + self, target_type: str, target_id: str, message: platform_message.MessageChain ): message_list = self.message_converter.yiri2target(message) @@ -437,8 +440,8 @@ async def send_message( async def reply_message( self, - message_source: mirai.MessageEvent, - message: mirai.MessageChain, + message_source: platform_events.MessageEvent, + message: platform_message.MessageChain, quote_origin: bool = False, ): @@ -463,13 +466,13 @@ async def reply_message( ] ) - if type(message_source) == mirai.GroupMessage: + if type(message_source) == platform_events.GroupMessage: args["channel_id"] = str(message_source.sender.group.id) args["msg_id"] = cached_message_ids[ str(message_source.message_chain.message_id) ] await self.bot.api.post_message(**args) - elif type(message_source) == mirai.FriendMessage: + elif type(message_source) == platform_events.FriendMessage: args["guild_id"] = str(message_source.sender.id) args["msg_id"] = cached_message_ids[ str(message_source.message_chain.message_id) @@ -534,9 +537,9 @@ async def is_muted(self, group_id: int) -> bool: def register_listener( self, - event_type: typing.Type[mirai.Event], + event_type: typing.Type[platform_events.Event], callback: typing.Callable[ - [mirai.Event, adapter_model.MessageSourceAdapter], None + [platform_events.Event, adapter_model.MessageSourceAdapter], None ], ): @@ -560,9 +563,9 @@ async def wrapper( def unregister_listener( self, - event_type: typing.Type[mirai.Event], + event_type: typing.Type[platform_events.Event], callback: typing.Callable[ - [mirai.Event, adapter_model.MessageSourceAdapter], None + [platform_events.Event, adapter_model.MessageSourceAdapter], None ], ): delattr(self.bot, event_handler_mapping[event_type]) diff --git a/pkg/platform/sources/yirimirai.py b/pkg/platform/sources/yirimirai.py index 7768dcf0..4082e90a 100644 --- a/pkg/platform/sources/yirimirai.py +++ b/pkg/platform/sources/yirimirai.py @@ -1,124 +1,124 @@ -import asyncio -import typing +# import asyncio +# import typing -import mirai -import mirai.models.bus -from mirai.bot import MiraiRunner +# import mirai +# import mirai.models.bus +# from mirai.bot import MiraiRunner -from .. import adapter as adapter_model -from ...core import app +# from .. import adapter as adapter_model +# from ...core import app -@adapter_model.adapter_class("yiri-mirai") -class YiriMiraiAdapter(adapter_model.MessageSourceAdapter): - """YiriMirai适配器""" - bot: mirai.Mirai +# @adapter_model.adapter_class("yiri-mirai") +# class YiriMiraiAdapter(adapter_model.MessageSourceAdapter): +# """YiriMirai适配器""" +# bot: mirai.Mirai - def __init__(self, config: dict, ap: app.Application): - """初始化YiriMirai的对象""" - self.ap = ap - self.config = config - if 'adapter' not in config or \ - config['adapter'] == 'WebSocketAdapter': - self.bot = mirai.Mirai( - qq=config['qq'], - adapter=mirai.WebSocketAdapter( - host=config['host'], - port=config['port'], - verify_key=config['verifyKey'] - ) - ) - elif config['adapter'] == 'HTTPAdapter': - self.bot = mirai.Mirai( - qq=config['qq'], - adapter=mirai.HTTPAdapter( - host=config['host'], - port=config['port'], - verify_key=config['verifyKey'] - ) - ) - else: - raise Exception('Unknown adapter for YiriMirai: ' + config['adapter']) +# def __init__(self, config: dict, ap: app.Application): +# """初始化YiriMirai的对象""" +# self.ap = ap +# self.config = config +# if 'adapter' not in config or \ +# config['adapter'] == 'WebSocketAdapter': +# self.bot = mirai.Mirai( +# qq=config['qq'], +# adapter=mirai.WebSocketAdapter( +# host=config['host'], +# port=config['port'], +# verify_key=config['verifyKey'] +# ) +# ) +# elif config['adapter'] == 'HTTPAdapter': +# self.bot = mirai.Mirai( +# qq=config['qq'], +# adapter=mirai.HTTPAdapter( +# host=config['host'], +# port=config['port'], +# verify_key=config['verifyKey'] +# ) +# ) +# else: +# raise Exception('Unknown adapter for YiriMirai: ' + config['adapter']) - async def send_message( - self, - target_type: str, - target_id: str, - message: mirai.MessageChain - ): - """发送消息 +# async def send_message( +# self, +# target_type: str, +# target_id: str, +# message: mirai.MessageChain +# ): +# """发送消息 - Args: - target_type (str): 目标类型,`person`或`group` - target_id (str): 目标ID - message (mirai.MessageChain): YiriMirai库的消息链 - """ - task = None - if target_type == 'person': - task = self.bot.send_friend_message(int(target_id), message) - elif target_type == 'group': - task = self.bot.send_group_message(int(target_id), message) - else: - raise Exception('Unknown target type: ' + target_type) +# Args: +# target_type (str): 目标类型,`person`或`group` +# target_id (str): 目标ID +# message (mirai.MessageChain): YiriMirai库的消息链 +# """ +# task = None +# if target_type == 'person': +# task = self.bot.send_friend_message(int(target_id), message) +# elif target_type == 'group': +# task = self.bot.send_group_message(int(target_id), message) +# else: +# raise Exception('Unknown target type: ' + target_type) - await task +# await task - async def reply_message( - self, - message_source: mirai.MessageEvent, - message: mirai.MessageChain, - quote_origin: bool = False - ): - """回复消息 +# async def reply_message( +# self, +# message_source: mirai.MessageEvent, +# message: mirai.MessageChain, +# quote_origin: bool = False +# ): +# """回复消息 - Args: - message_source (mirai.MessageEvent): YiriMirai消息源事件 - message (mirai.MessageChain): YiriMirai库的消息链 - quote_origin (bool, optional): 是否引用原消息. Defaults to False. - """ - await self.bot.send(message_source, message, quote_origin) +# Args: +# message_source (mirai.MessageEvent): YiriMirai消息源事件 +# message (mirai.MessageChain): YiriMirai库的消息链 +# quote_origin (bool, optional): 是否引用原消息. Defaults to False. +# """ +# await self.bot.send(message_source, message, quote_origin) - async def is_muted(self, group_id: int) -> bool: - result = await self.bot.member_info(target=group_id, member_id=self.bot.qq).get() - if result.mute_time_remaining > 0: - return True - return False +# async def is_muted(self, group_id: int) -> bool: +# result = await self.bot.member_info(target=group_id, member_id=self.bot.qq).get() +# if result.mute_time_remaining > 0: +# return True +# return False - def register_listener( - self, - event_type: typing.Type[mirai.Event], - callback: typing.Callable[[mirai.Event, adapter_model.MessageSourceAdapter], None] - ): - """注册事件监听器 +# def register_listener( +# self, +# event_type: typing.Type[mirai.Event], +# callback: typing.Callable[[mirai.Event, adapter_model.MessageSourceAdapter], None] +# ): +# """注册事件监听器 - Args: - event_type (typing.Type[mirai.Event]): YiriMirai事件类型 - callback (typing.Callable[[mirai.Event], None]): 回调函数,接收一个参数,为YiriMirai事件 - """ - async def wrapper(event: mirai.Event): - await callback(event, self) - self.bot.on(event_type)(wrapper) +# Args: +# event_type (typing.Type[mirai.Event]): YiriMirai事件类型 +# callback (typing.Callable[[mirai.Event], None]): 回调函数,接收一个参数,为YiriMirai事件 +# """ +# async def wrapper(event: mirai.Event): +# await callback(event, self) +# self.bot.on(event_type)(wrapper) - def unregister_listener( - self, - event_type: typing.Type[mirai.Event], - callback: typing.Callable[[mirai.Event, adapter_model.MessageSourceAdapter], None] - ): - """注销事件监听器 +# def unregister_listener( +# self, +# event_type: typing.Type[mirai.Event], +# callback: typing.Callable[[mirai.Event, adapter_model.MessageSourceAdapter], None] +# ): +# """注销事件监听器 - Args: - event_type (typing.Type[mirai.Event]): YiriMirai事件类型 - callback (typing.Callable[[mirai.Event], None]): 回调函数,接收一个参数,为YiriMirai事件 - """ - assert isinstance(self.bot, mirai.Mirai) - bus = self.bot.bus - assert isinstance(bus, mirai.models.bus.ModelEventBus) +# Args: +# event_type (typing.Type[mirai.Event]): YiriMirai事件类型 +# callback (typing.Callable[[mirai.Event], None]): 回调函数,接收一个参数,为YiriMirai事件 +# """ +# assert isinstance(self.bot, mirai.Mirai) +# bus = self.bot.bus +# assert isinstance(bus, mirai.models.bus.ModelEventBus) - bus.unsubscribe(event_type, callback) +# bus.unsubscribe(event_type, callback) - async def run_async(self): - self.bot_account_id = self.bot.qq - return await MiraiRunner(self.bot)._run() +# async def run_async(self): +# self.bot_account_id = self.bot.qq +# return await MiraiRunner(self.bot)._run() - async def kill(self) -> bool: - return False +# async def kill(self) -> bool: +# return False diff --git a/pkg/platform/types/__init__.py b/pkg/platform/types/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/platform/types/base.py b/pkg/platform/types/base.py new file mode 100644 index 00000000..964e9c6f --- /dev/null +++ b/pkg/platform/types/base.py @@ -0,0 +1,105 @@ + +from typing import Dict, List, Type + +import pydantic.main as pdm +from pydantic import BaseModel + + +class PlatformMetaclass(pdm.ModelMetaclass): + """此类是 YiriMirai 中使用的 pydantic 模型的元类的基类。""" + + +def to_camel(name: str) -> str: + """将下划线命名风格转换为小驼峰命名。""" + if name[:2] == '__': # 不处理双下划线开头的特殊命名。 + return name + name_parts = name.split('_') + return ''.join(name_parts[:1] + [x.title() for x in name_parts[1:]]) + + +class PlatformBaseModel(BaseModel, metaclass=PlatformMetaclass): + """模型基类。 + + 启用了三项配置: + 1. 允许解析时传入额外的值,并将额外值保存在模型中。 + 2. 允许通过别名访问字段。 + 3. 自动生成小驼峰风格的别名,以符合 mirai-api-http 的命名。 + """ + def __init__(self, *args, **kwargs): + """""" + super().__init__(*args, **kwargs) + + def __repr__(self) -> str: + return self.__class__.__name__ + '(' + ', '.join( + (f'{k}={repr(v)}' for k, v in self.__dict__.items() if v) + ) + ')' + + class Config: + extra = 'allow' + allow_population_by_field_name = True + alias_generator = to_camel + + +class PlatformIndexedMetaclass(PlatformMetaclass): + """可以通过子类名获取子类的类的元类。""" + __indexedbases__: List[Type['PlatformIndexedModel']] = [] + __indexedmodel__ = None + + def __new__(cls, name, bases, attrs, **kwargs): + new_cls = super().__new__(cls, name, bases, attrs, **kwargs) + # 第一类:MiraiIndexedModel + if name == 'PlatformIndexedModel': + cls.__indexedmodel__ = new_cls + new_cls.__indexes__ = {} + return new_cls + # 第二类:MiraiIndexedModel 的直接子类,这些是可以通过子类名获取子类的类。 + if cls.__indexedmodel__ in bases: + cls.__indexedbases__.append(new_cls) + new_cls.__indexes__ = {} + return new_cls + # 第三类:MiraiIndexedModel 的直接子类的子类,这些添加到直接子类的索引中。 + for base in cls.__indexedbases__: + if issubclass(new_cls, base): + base.__indexes__[name] = new_cls + return new_cls + + def __getitem__(cls, name): + return cls.get_subtype(name) + + +class PlatformIndexedModel(PlatformBaseModel, metaclass=PlatformIndexedMetaclass): + """可以通过子类名获取子类的类。""" + __indexes__: Dict[str, Type['PlatformIndexedModel']] + + @classmethod + def get_subtype(cls, name: str) -> Type['PlatformIndexedModel']: + """根据类名称,获取相应的子类类型。 + + Args: + name: 类名称。 + + Returns: + Type['MiraiIndexedModel']: 子类类型。 + """ + try: + type_ = cls.__indexes__.get(name) + if not (type_ and issubclass(type_, cls)): + raise ValueError(f'`{name}` 不是 `{cls.__name__}` 的子类!') + return type_ + except AttributeError as e: + raise ValueError(f'`{name}` 不是 `{cls.__name__}` 的子类!') from None + + @classmethod + def parse_subtype(cls, obj: dict) -> 'PlatformIndexedModel': + """通过字典,构造对应的模型对象。 + + Args: + obj: 一个字典,包含了模型对象的属性。 + + Returns: + MiraiIndexedModel: 构造的对象。 + """ + if cls in PlatformIndexedModel.__subclasses__(): + ModelType = cls.get_subtype(obj['type']) + return ModelType.parse_obj(obj) + return super().parse_obj(obj) diff --git a/pkg/platform/types/entities.py b/pkg/platform/types/entities.py new file mode 100644 index 00000000..8077bd15 --- /dev/null +++ b/pkg/platform/types/entities.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +""" +此模块提供实体和配置项模型。 +""" +import abc +from datetime import datetime +from enum import Enum +import typing + +import pydantic + + +class Entity(pydantic.BaseModel): + """实体,表示一个用户或群。""" + id: int + """QQ 号或群号。""" + @abc.abstractmethod + def get_avatar_url(self) -> str: + """头像图片链接。""" + + @abc.abstractmethod + def get_name(self) -> str: + """名称。""" + + +class Friend(Entity): + """好友。""" + id: int + """QQ 号。""" + nickname: typing.Optional[str] + """昵称。""" + remark: typing.Optional[str] + """备注。""" + def get_avatar_url(self) -> str: + return f'http://q4.qlogo.cn/g?b=qq&nk={self.id}&s=140' + + def get_name(self) -> str: + return self.nickname or self.remark or '' + + +class Permission(str, Enum): + """群成员身份权限。""" + Member = "MEMBER" + """成员。""" + Administrator = "ADMINISTRATOR" + """管理员。""" + Owner = "OWNER" + """群主。""" + def __repr__(self) -> str: + return repr(self.value) + + +class Group(Entity): + """群。""" + id: int + """群号。""" + name: str + """群名称。""" + permission: Permission + """Bot 在群中的权限。""" + def get_avatar_url(self) -> str: + return f'https://p.qlogo.cn/gh/{self.id}/{self.id}/' + + def get_name(self) -> str: + return self.name + + +class GroupMember(Entity): + """群成员。""" + id: int + """QQ 号。""" + member_name: str + """群成员名称。""" + permission: Permission + """Bot 在群中的权限。""" + group: Group + """群。""" + special_title: str = '' + """群头衔。""" + join_timestamp: datetime = datetime.utcfromtimestamp(0) + """加入群的时间。""" + last_speak_timestamp: datetime = datetime.utcfromtimestamp(0) + """最后一次发言的时间。""" + mute_time_remaining: int = 0 + """禁言剩余时间。""" + def get_avatar_url(self) -> str: + return f'http://q4.qlogo.cn/g?b=qq&nk={self.id}&s=140' + + def get_name(self) -> str: + return self.member_name + + +class Client(Entity): + """来自其他客户端的用户。""" + id: int + """识别 id。""" + platform: str + """来源平台。""" + def get_avatar_url(self) -> str: + raise NotImplementedError + + def get_name(self) -> str: + return self.platform + + +class Subject(pydantic.BaseModel): + """另一种实体类型表示。""" + id: int + """QQ 号或群号。""" + kind: typing.Literal['Friend', 'Group', 'Stranger'] + """类型。""" + + +class Config(pydantic.BaseModel): + """配置项类型。""" + def modify(self, **kwargs) -> 'Config': + """修改部分设置。""" + for k, v in kwargs.items(): + if k in self.__fields__: + setattr(self, k, v) + else: + raise ValueError(f'未知配置项: {k}') + return self + + +class GroupConfigModel(Config): + """群配置。""" + name: str + """群名称。""" + confess_talk: bool + """是否允许坦白说。""" + allow_member_invite: bool + """是否允许成员邀请好友入群。""" + auto_approve: bool + """是否开启自动审批入群。""" + anonymous_chat: bool + """是否开启匿名聊天。""" + announcement: str = '' + """群公告。""" + + +class MemberInfoModel(Config, GroupMember): + """群成员信息。""" diff --git a/pkg/platform/types/events.py b/pkg/platform/types/events.py new file mode 100644 index 00000000..1b008cf4 --- /dev/null +++ b/pkg/platform/types/events.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +""" +此模块提供事件模型。 +""" +from datetime import datetime +from enum import Enum +import typing + +import pydantic + +from . import entities as platform_entities +from . import message as platform_message + + +class Event(pydantic.BaseModel): + """事件基类。 + + Args: + type: 事件名。 + """ + type: str + """事件名。""" + def __repr__(self): + return self.__class__.__name__ + '(' + ', '.join( + ( + f'{k}={repr(v)}' + for k, v in self.__dict__.items() if k != 'type' and v + ) + ) + ')' + + @classmethod + def parse_subtype(cls, obj: dict) -> 'Event': + try: + return typing.cast(Event, super().parse_subtype(obj)) + except ValueError: + return Event(type=obj['type']) + + @classmethod + def get_subtype(cls, name: str) -> typing.Type['Event']: + try: + return typing.cast(typing.Type[Event], super().get_subtype(name)) + except ValueError: + return Event + + +############################### +# Bot Event +class BotEvent(Event): + """Bot 自身事件。 + + Args: + type: 事件名。 + qq: Bot 的 QQ 号。 + """ + type: str + """事件名。""" + qq: int + """Bot 的 QQ 号。""" + + +############################### +# Message Event +class MessageEvent(Event): + """消息事件。 + + Args: + type: 事件名。 + message_chain: 消息内容。 + """ + type: str + """事件名。""" + message_chain: platform_message.MessageChain + """消息内容。""" + + +class FriendMessage(MessageEvent): + """好友消息。 + + Args: + type: 事件名。 + sender: 发送消息的好友。 + message_chain: 消息内容。 + """ + type: str = 'FriendMessage' + """事件名。""" + sender: platform_entities.Friend + """发送消息的好友。""" + message_chain: platform_message.MessageChain + """消息内容。""" + + +class GroupMessage(MessageEvent): + """群消息。 + + Args: + type: 事件名。 + sender: 发送消息的群成员。 + message_chain: 消息内容。 + """ + type: str = 'GroupMessage' + """事件名。""" + sender: platform_entities.GroupMember + """发送消息的群成员。""" + message_chain: platform_message.MessageChain + """消息内容。""" + @property + def group(self) -> platform_entities.Group: + return self.sender.group + + +class StrangerMessage(MessageEvent): + """陌生人消息。 + + Args: + type: 事件名。 + sender: 发送消息的人。 + message_chain: 消息内容。 + """ + type: str = 'StrangerMessage' + """事件名。""" + sender: platform_entities.Friend + """发送消息的人。""" + message_chain: platform_message.MessageChain + """消息内容。""" diff --git a/pkg/platform/types/message.py b/pkg/platform/types/message.py new file mode 100644 index 00000000..00e420b1 --- /dev/null +++ b/pkg/platform/types/message.py @@ -0,0 +1,826 @@ +import itertools +import logging +from datetime import datetime +from enum import Enum +from pathlib import Path +import typing + +import pydantic +import pydantic.main + +from . import entities as platform_entities +from .base import PlatformBaseModel, PlatformIndexedMetaclass, PlatformIndexedModel + + +logger = logging.getLogger(__name__) + + +class MessageComponentMetaclass(PlatformIndexedMetaclass): + """消息组件元类。""" + __message_component__ = None + + def __new__(cls, name, bases, attrs, **kwargs): + new_cls = super().__new__(cls, name, bases, attrs, **kwargs) + if name == 'MessageComponent': + cls.__message_component__ = new_cls + + if not cls.__message_component__: + return new_cls + + for base in bases: + if issubclass(base, cls.__message_component__): + # 获取字段名 + if hasattr(new_cls, '__fields__'): + # 忽略 type 字段 + new_cls.__parameter_names__ = list(new_cls.__fields__)[1:] + else: + new_cls.__parameter_names__ = [] + break + + return new_cls + + +class MessageComponent(PlatformIndexedModel, metaclass=MessageComponentMetaclass): + """消息组件。""" + type: str + """消息组件类型。""" + def __str__(self): + return '' + + def __repr__(self): + return self.__class__.__name__ + '(' + ', '.join( + ( + f'{k}={repr(v)}' + for k, v in self.__dict__.items() if k != 'type' and v + ) + ) + ')' + + def __init__(self, *args, **kwargs): + # 解析参数列表,将位置参数转化为具名参数 + parameter_names = self.__parameter_names__ + if len(args) > len(parameter_names): + raise TypeError( + f'`{self.type}`需要{len(parameter_names)}个参数,但传入了{len(args)}个。' + ) + for name, value in zip(parameter_names, args): + if name in kwargs: + raise TypeError(f'在 `{self.type}` 中,具名参数 `{name}` 与位置参数重复。') + kwargs[name] = value + + super().__init__(**kwargs) + + +TMessageComponent = typing.TypeVar('TMessageComponent', bound=MessageComponent) + + +class MessageChain(PlatformBaseModel): + """消息链。 + + 一个构造消息链的例子: + ```py + message_chain = MessageChain([ + AtAll(), + Plain("Hello World!"), + ]) + ``` + + `Plain` 可以省略。 + ```py + message_chain = MessageChain([ + AtAll(), + "Hello World!", + ]) + ``` + + 在调用 API 时,参数中需要 MessageChain 的,也可以使用 `List[MessageComponent]` 代替。 + 例如,以下两种写法是等价的: + ```py + await bot.send_friend_message(12345678, [ + Plain("Hello World!") + ]) + ``` + ```py + await bot.send_friend_message(12345678, MessageChain([ + Plain("Hello World!") + ])) + ``` + + 可以使用 `in` 运算检查消息链中: + 1. 是否有某个消息组件。 + 2. 是否有某个类型的消息组件。 + + ```py + if AtAll in message_chain: + print('AtAll') + + if At(bot.qq) in message_chain: + print('At Me') + ``` + + 消息链对索引操作进行了增强。以消息组件类型为索引,获取消息链中的全部该类型的消息组件。 + ```py + plain_list = message_chain[Plain] + '[Plain("Hello World!")]' + ``` + + 可以用加号连接两个消息链。 + ```py + MessageChain(['Hello World!']) + MessageChain(['Goodbye World!']) + # 返回 MessageChain([Plain("Hello World!"), Plain("Goodbye World!")]) + ``` + + """ + __root__: typing.List[MessageComponent] + + @staticmethod + def _parse_message_chain(msg_chain: typing.Iterable): + result = [] + for msg in msg_chain: + if isinstance(msg, dict): + result.append(MessageComponent.parse_subtype(msg)) + elif isinstance(msg, MessageComponent): + result.append(msg) + elif isinstance(msg, str): + result.append(Plain(msg)) + else: + raise TypeError( + f"消息链中元素需为 dict 或 str 或 MessageComponent,当前类型:{type(msg)}" + ) + return result + + @pydantic.validator('__root__', always=True, pre=True) + def _parse_component(cls, msg_chain): + if isinstance(msg_chain, (str, MessageComponent)): + msg_chain = [msg_chain] + if not msg_chain: + msg_chain = [] + return cls._parse_message_chain(msg_chain) + + @classmethod + def parse_obj(cls, msg_chain: typing.Iterable): + """通过列表形式的消息链,构造对应的 `MessageChain` 对象。 + + Args: + msg_chain: 列表形式的消息链。 + """ + result = cls._parse_message_chain(msg_chain) + return cls(__root__=result) + + def __init__(self, __root__: typing.Iterable[MessageComponent] = None): + super().__init__(__root__=__root__) + + def __str__(self): + return "".join(str(component) for component in self.__root__) + + def __repr__(self): + return f'{self.__class__.__name__}({self.__root__!r})' + + def __iter__(self): + yield from self.__root__ + + def get_first(self, + t: typing.Type[TMessageComponent]) -> typing.Optional[TMessageComponent]: + """获取消息链中第一个符合类型的消息组件。""" + for component in self: + if isinstance(component, t): + return component + return None + + @typing.overload + def __getitem__(self, index: int) -> MessageComponent: + ... + + @typing.overload + def __getitem__(self, index: slice) -> typing.List[MessageComponent]: + ... + + @typing.overload + def __getitem__(self, + index: typing.Type[TMessageComponent]) -> typing.List[TMessageComponent]: + ... + + @typing.overload + def __getitem__( + self, index: typing.Tuple[typing.Type[TMessageComponent], int] + ) -> typing.List[TMessageComponent]: + ... + + def __getitem__( + self, index: typing.Union[int, slice, typing.Type[TMessageComponent], + typing.Tuple[typing.Type[TMessageComponent], int]] + ) -> typing.Union[MessageComponent, typing.List[MessageComponent], + typing.List[TMessageComponent]]: + return self.get(index) + + def __setitem__( + self, key: typing.Union[int, slice], + value: typing.Union[MessageComponent, str, typing.Iterable[typing.Union[MessageComponent, + str]]] + ): + if isinstance(value, str): + value = Plain(value) + if isinstance(value, typing.Iterable): + value = (Plain(c) if isinstance(c, str) else c for c in value) + self.__root__[key] = value # type: ignore + + def __delitem__(self, key: typing.Union[int, slice]): + del self.__root__[key] + + def __reversed__(self) -> typing.Iterable[MessageComponent]: + return reversed(self.__root__) + + def has( + self, sub: typing.Union[MessageComponent, typing.Type[MessageComponent], + 'MessageChain', str] + ) -> bool: + """判断消息链中: + 1. 是否有某个消息组件。 + 2. 是否有某个类型的消息组件。 + + Args: + sub (`Union[MessageComponent, Type[MessageComponent], 'MessageChain', str]`): + 若为 `MessageComponent`,则判断该组件是否在消息链中。 + 若为 `Type[MessageComponent]`,则判断该组件类型是否在消息链中。 + + Returns: + bool: 是否找到。 + """ + if isinstance(sub, type): # 检测消息链中是否有某种类型的对象 + for i in self: + if type(i) is sub: + return True + return False + if isinstance(sub, MessageComponent): # 检查消息链中是否有某个组件 + for i in self: + if i == sub: + return True + return False + raise TypeError(f"类型不匹配,当前类型:{type(sub)}") + + def __contains__(self, sub) -> bool: + return self.has(sub) + + def __ge__(self, other): + return other in self + + def __len__(self) -> int: + return len(self.__root__) + + def __add__( + self, other: typing.Union['MessageChain', MessageComponent, str] + ) -> 'MessageChain': + if isinstance(other, MessageChain): + return self.__class__(self.__root__ + other.__root__) + if isinstance(other, str): + return self.__class__(self.__root__ + [Plain(other)]) + if isinstance(other, MessageComponent): + return self.__class__(self.__root__ + [other]) + return NotImplemented + + def __radd__(self, other: typing.Union[MessageComponent, str]) -> 'MessageChain': + if isinstance(other, MessageComponent): + return self.__class__([other] + self.__root__) + if isinstance(other, str): + return self.__class__( + [typing.cast(MessageComponent, Plain(other))] + self.__root__ + ) + return NotImplemented + + def __mul__(self, other: int): + if isinstance(other, int): + return self.__class__(self.__root__ * other) + return NotImplemented + + def __rmul__(self, other: int): + return self.__mul__(other) + + def __iadd__(self, other: typing.Iterable[typing.Union[MessageComponent, str]]): + self.extend(other) + + def __imul__(self, other: int): + if isinstance(other, int): + self.__root__ *= other + return NotImplemented + + def index( + self, + x: typing.Union[MessageComponent, typing.Type[MessageComponent]], + i: int = 0, + j: int = -1 + ) -> int: + """返回 x 在消息链中首次出现项的索引号(索引号在 i 或其后且在 j 之前)。 + + Args: + x (`Union[MessageComponent, Type[MessageComponent]]`): + 要查找的消息元素或消息元素类型。 + i: 从哪个位置开始查找。 + j: 查找到哪个位置结束。 + + Returns: + int: 如果找到,则返回索引号。 + + Raises: + ValueError: 没有找到。 + TypeError: 类型不匹配。 + """ + if isinstance(x, type): + l = len(self) + if i < 0: + i += l + if i < 0: + i = 0 + if j < 0: + j += l + if j > l: + j = l + for index in range(i, j): + if type(self[index]) is x: + return index + raise ValueError("消息链中不存在该类型的组件。") + if isinstance(x, MessageComponent): + return self.__root__.index(x, i, j) + raise TypeError(f"类型不匹配,当前类型:{type(x)}") + + def count(self, x: typing.Union[MessageComponent, typing.Type[MessageComponent]]) -> int: + """返回消息链中 x 出现的次数。 + + Args: + x (`Union[MessageComponent, Type[MessageComponent]]`): + 要查找的消息元素或消息元素类型。 + + Returns: + int: 次数。 + """ + if isinstance(x, type): + return sum(1 for i in self if type(i) is x) + if isinstance(x, MessageComponent): + return self.__root__.count(x) + raise TypeError(f"类型不匹配,当前类型:{type(x)}") + + def extend(self, x: typing.Iterable[typing.Union[MessageComponent, str]]): + """将另一个消息链中的元素添加到消息链末尾。 + + Args: + x: 另一个消息链,也可为消息元素或字符串元素的序列。 + """ + self.__root__.extend(Plain(c) if isinstance(c, str) else c for c in x) + + def append(self, x: typing.Union[MessageComponent, str]): + """将一个消息元素或字符串元素添加到消息链末尾。 + + Args: + x: 消息元素或字符串元素。 + """ + self.__root__.append(Plain(x) if isinstance(x, str) else x) + + def insert(self, i: int, x: typing.Union[MessageComponent, str]): + """将一个消息元素或字符串添加到消息链中指定位置。 + + Args: + i: 插入位置。 + x: 消息元素或字符串元素。 + """ + self.__root__.insert(i, Plain(x) if isinstance(x, str) else x) + + def pop(self, i: int = -1) -> MessageComponent: + """从消息链中移除并返回指定位置的元素。 + + Args: + i: 移除位置。默认为末尾。 + + Returns: + MessageComponent: 移除的元素。 + """ + return self.__root__.pop(i) + + def remove(self, x: typing.Union[MessageComponent, typing.Type[MessageComponent]]): + """从消息链中移除指定元素或指定类型的一个元素。 + + Args: + x: 指定的元素或元素类型。 + """ + if isinstance(x, type): + self.pop(self.index(x)) + if isinstance(x, MessageComponent): + self.__root__.remove(x) + + def exclude( + self, + x: typing.Union[MessageComponent, typing.Type[MessageComponent]], + count: int = -1 + ) -> 'MessageChain': + """返回移除指定元素或指定类型的元素后剩余的消息链。 + + Args: + x: 指定的元素或元素类型。 + count: 至多移除的数量。默认为全部移除。 + + Returns: + MessageChain: 剩余的消息链。 + """ + def _exclude(): + nonlocal count + x_is_type = isinstance(x, type) + for c in self: + if count > 0 and ((x_is_type and type(c) is x) or c == x): + count -= 1 + continue + yield c + + return self.__class__(_exclude()) + + def reverse(self): + """将消息链原地翻转。""" + self.__root__.reverse() + + @classmethod + def join(cls, *args: typing.Iterable[typing.Union[str, MessageComponent]]): + return cls( + Plain(c) if isinstance(c, str) else c + for c in itertools.chain(*args) + ) + + @property + def source(self) -> typing.Optional['Source']: + """获取消息链中的 `Source` 对象。""" + return self.get_first(Source) + + @property + def message_id(self) -> int: + """获取消息链的 message_id,若无法获取,返回 -1。""" + source = self.source + return source.id if source else -1 + + +TMessage = typing.Union[MessageChain, typing.Iterable[typing.Union[MessageComponent, str]], + MessageComponent, str] +"""可以转化为 MessageChain 的类型。""" + + +class Source(MessageComponent): + """源。包含消息的基本信息。""" + type: str = "Source" + """消息组件类型。""" + id: int + """消息的识别号,用于引用回复(Source 类型永远为 MessageChain 的第一个元素)。""" + time: datetime + """消息时间。""" + + +class Plain(MessageComponent): + """纯文本。""" + type: str = "Plain" + """消息组件类型。""" + text: str + """文字消息。""" + def __str__(self): + return self.text + + def __repr__(self): + return f'Plain({self.text!r})' + + +class Quote(MessageComponent): + """引用。""" + type: str = "Quote" + """消息组件类型。""" + id: typing.Optional[int] = None + """被引用回复的原消息的 message_id。""" + group_id: typing.Optional[int] = None + """被引用回复的原消息所接收的群号,当为好友消息时为0。""" + sender_id: typing.Optional[int] = None + """被引用回复的原消息的发送者的QQ号。""" + target_id: typing.Optional[int] = None + """被引用回复的原消息的接收者者的QQ号(或群号)。""" + origin: MessageChain + """被引用回复的原消息的消息链对象。""" + + @pydantic.validator("origin", always=True, pre=True) + def origin_formater(cls, v): + return MessageChain.parse_obj(v) + + +class At(MessageComponent): + """At某人。""" + type: str = "At" + """消息组件类型。""" + target: int + """群员 QQ 号。""" + display: typing.Optional[str] = None + """At时显示的文字,发送消息时无效,自动使用群名片。""" + def __eq__(self, other): + return isinstance(other, At) and self.target == other.target + + def __str__(self): + return f"@{self.display or self.target}" + + def as_mirai_code(self) -> str: + return f"[mirai:at:{self.target}]" + + +class AtAll(MessageComponent): + """At全体。""" + type: str = "AtAll" + """消息组件类型。""" + def __str__(self): + return "@全体成员" + + def as_mirai_code(self) -> str: + return f"[mirai:atall]" + + +class Image(MessageComponent): + """图片。""" + type: str = "Image" + """消息组件类型。""" + image_id: typing.Optional[str] = None + """图片的 image_id,群图片与好友图片格式不同。不为空时将忽略 url 属性。""" + url: typing.Optional[pydantic.HttpUrl] = None + """图片的 URL,发送时可作网络图片的链接;接收时为腾讯图片服务器的链接,可用于图片下载。""" + path: typing.Union[str, Path, None] = None + """图片的路径,发送本地图片。""" + base64: typing.Optional[str] = None + """图片的 Base64 编码。""" + def __eq__(self, other): + return isinstance( + other, Image + ) and self.type == other.type and self.uuid == other.uuid + + def __str__(self): + return '[图片]' + + def as_mirai_code(self) -> str: + return f"[mirai:image:{self.image_id}]" + + @pydantic.validator('path') + def validate_path(cls, path: typing.Union[str, Path, None]): + """修复 path 参数的行为,使之相对于 YiriMirai 的启动路径。""" + if path: + try: + return str(Path(path).resolve(strict=True)) + except FileNotFoundError: + raise ValueError(f"无效路径:{path}") + else: + return path + + @property + def uuid(self): + image_id = self.image_id + if image_id[0] == '{': # 群图片 + image_id = image_id[1:37] + elif image_id[0] == '/': # 好友图片 + image_id = image_id[1:] + return image_id + + async def download( + self, + filename: typing.Union[str, Path, None] = None, + directory: typing.Union[str, Path, None] = None, + determine_type: bool = True + ): + """下载图片到本地。 + + Args: + filename: 下载到本地的文件路径。与 `directory` 二选一。 + directory: 下载到本地的文件夹路径。与 `filename` 二选一。 + determine_type: 是否自动根据图片类型确定拓展名,默认为 True。 + """ + if not self.url: + logger.warning(f'图片 `{self.uuid}` 无 url 参数,下载失败。') + return + + import httpx + async with httpx.AsyncClient() as client: + response = await client.get(self.url) + response.raise_for_status() + content = response.content + + if filename: + path = Path(filename) + if determine_type: + import imghdr + path = path.with_suffix( + '.' + str(imghdr.what(None, content)) + ) + path.parent.mkdir(parents=True, exist_ok=True) + elif directory: + import imghdr + path = Path(directory) + path.mkdir(parents=True, exist_ok=True) + path = path / f'{self.uuid}.{imghdr.what(None, content)}' + else: + raise ValueError("请指定文件路径或文件夹路径!") + + import aiofiles + async with aiofiles.open(path, 'wb') as f: + await f.write(content) + + return path + + @classmethod + async def from_local( + cls, + filename: typing.Union[str, Path, None] = None, + content: typing.Optional[bytes] = None, + ) -> "Image": + """从本地文件路径加载图片,以 base64 的形式传递。 + + Args: + filename: 从本地文件路径加载图片,与 `content` 二选一。 + content: 从本地文件内容加载图片,与 `filename` 二选一。 + + Returns: + Image: 图片对象。 + """ + if content: + pass + elif filename: + path = Path(filename) + import aiofiles + async with aiofiles.open(path, 'rb') as f: + content = await f.read() + else: + raise ValueError("请指定图片路径或图片内容!") + import base64 + img = cls(base64=base64.b64encode(content).decode()) + return img + + @classmethod + def from_unsafe_path(cls, path: typing.Union[str, Path]) -> "Image": + """从不安全的路径加载图片。 + + Args: + path: 从不安全的路径加载图片。 + + Returns: + Image: 图片对象。 + """ + return cls.construct(path=str(path)) + + +class Unknown(MessageComponent): + """未知。""" + type: str = "Unknown" + """消息组件类型。""" + text: str + """文本。""" + + +class Voice(MessageComponent): + """语音。""" + type: str = "Voice" + """消息组件类型。""" + voice_id: typing.Optional[str] = None + """语音的 voice_id,不为空时将忽略 url 属性。""" + url: typing.Optional[str] = None + """语音的 URL,发送时可作网络语音的链接;接收时为腾讯语音服务器的链接,可用于语音下载。""" + path: typing.Optional[str] = None + """语音的路径,发送本地语音。""" + base64: typing.Optional[str] = None + """语音的 Base64 编码。""" + length: typing.Optional[int] = None + """语音的长度,单位为秒。""" + @pydantic.validator('path') + def validate_path(cls, path: typing.Optional[str]): + """修复 path 参数的行为,使之相对于 YiriMirai 的启动路径。""" + if path: + try: + return str(Path(path).resolve(strict=True)) + except FileNotFoundError: + raise ValueError(f"无效路径:{path}") + else: + return path + + def __str__(self): + return '[语音]' + + async def download( + self, + filename: typing.Union[str, Path, None] = None, + directory: typing.Union[str, Path, None] = None + ): + """下载语音到本地。 + + 语音采用 silk v3 格式,silk 格式的编码解码请使用 [graiax-silkcoder](https://pypi.org/project/graiax-silkcoder/)。 + + Args: + filename: 下载到本地的文件路径。与 `directory` 二选一。 + directory: 下载到本地的文件夹路径。与 `filename` 二选一。 + """ + if not self.url: + logger.warning(f'语音 `{self.voice_id}` 无 url 参数,下载失败。') + return + + import httpx + async with httpx.AsyncClient() as client: + response = await client.get(self.url) + response.raise_for_status() + content = response.content + + if filename: + path = Path(filename) + path.parent.mkdir(parents=True, exist_ok=True) + elif directory: + path = Path(directory) + path.mkdir(parents=True, exist_ok=True) + path = path / f'{self.voice_id}.silk' + else: + raise ValueError("请指定文件路径或文件夹路径!") + + import aiofiles + async with aiofiles.open(path, 'wb') as f: + await f.write(content) + + @classmethod + async def from_local( + cls, + filename: typing.Union[str, Path, None] = None, + content: typing.Optional[bytes] = None, + ) -> "Voice": + """从本地文件路径加载语音,以 base64 的形式传递。 + + Args: + filename: 从本地文件路径加载语音,与 `content` 二选一。 + content: 从本地文件内容加载语音,与 `filename` 二选一。 + """ + if content: + pass + if filename: + path = Path(filename) + import aiofiles + async with aiofiles.open(path, 'rb') as f: + content = await f.read() + else: + raise ValueError("请指定语音路径或语音内容!") + import base64 + img = cls(base64=base64.b64encode(content).decode()) + return img + + +class ForwardMessageNode(pydantic.BaseModel): + """合并转发中的一条消息。""" + sender_id: typing.Optional[int] = None + """发送人QQ号。""" + sender_name: typing.Optional[str] = None + """显示名称。""" + message_chain: typing.Optional[MessageChain] = None + """消息内容。""" + message_id: typing.Optional[int] = None + """消息的 message_id,可以只使用此属性,从缓存中读取消息内容。""" + time: typing.Optional[datetime] = None + """发送时间。""" + @pydantic.validator('message_chain', check_fields=False) + def _validate_message_chain(cls, value: typing.Union[MessageChain, list]): + if isinstance(value, list): + return MessageChain.parse_obj(value) + return value + + @classmethod + def create( + cls, sender: typing.Union[platform_entities.Friend, platform_entities.GroupMember], message: MessageChain + ) -> 'ForwardMessageNode': + """从消息链生成转发消息。 + + Args: + sender: 发送人。 + message: 消息内容。 + + Returns: + ForwardMessageNode: 生成的一条消息。 + """ + return ForwardMessageNode( + sender_id=sender.id, + sender_name=sender.get_name(), + message_chain=message + ) + + +class Forward(MessageComponent): + """合并转发。""" + type: str = "Forward" + """消息组件类型。""" + node_list: typing.List[ForwardMessageNode] + """转发消息节点列表。""" + def __init__(self, *args, **kwargs): + if len(args) == 1: + self.node_list = args[0] + super().__init__(**kwargs) + super().__init__(*args, **kwargs) + + def __str__(self): + return '[聊天记录]' + + +class File(MessageComponent): + """文件。""" + type: str = "File" + """消息组件类型。""" + id: str + """文件识别 ID。""" + name: str + """文件名称。""" + size: int + """文件大小。""" + def __str__(self): + return f'[文件]{self.name}' + diff --git a/pkg/plugin/context.py b/pkg/plugin/context.py index 42cb6be2..9e4d8caf 100644 --- a/pkg/plugin/context.py +++ b/pkg/plugin/context.py @@ -3,11 +3,12 @@ import typing import abc import pydantic -import mirai +# import mirai from . import events from ..provider.tools import entities as tools_entities from ..core import app +from ..platform.types import message as platform_message def register( @@ -174,7 +175,7 @@ def add_return(self, key: str, ret): self.__return_value__[key] = [] self.__return_value__[key].append(ret) - async def reply(self, message_chain: mirai.MessageChain): + async def reply(self, message_chain: platform_message.MessageChain): """回复此次消息请求 Args: @@ -190,7 +191,7 @@ async def send_message( self, target_type: str, target_id: str, - message: mirai.MessageChain + message: platform_message.MessageChain ): """主动发送消息 diff --git a/pkg/plugin/events.py b/pkg/plugin/events.py index b5919762..3fc2ea73 100644 --- a/pkg/plugin/events.py +++ b/pkg/plugin/events.py @@ -3,10 +3,11 @@ import typing import pydantic -import mirai +# import mirai from ..core import entities as core_entities from ..provider import entities as llm_entities +from ..platform.types import message as platform_message class BaseEventModel(pydantic.BaseModel): @@ -31,7 +32,7 @@ class PersonMessageReceived(BaseEventModel): sender_id: int """发送者ID(QQ号)""" - message_chain: mirai.MessageChain + message_chain: platform_message.MessageChain class GroupMessageReceived(BaseEventModel): @@ -43,7 +44,7 @@ class GroupMessageReceived(BaseEventModel): sender_id: int - message_chain: mirai.MessageChain + message_chain: platform_message.MessageChain class PersonNormalMessageReceived(BaseEventModel): diff --git a/pkg/provider/entities.py b/pkg/provider/entities.py index 50000722..950cbb17 100644 --- a/pkg/provider/entities.py +++ b/pkg/provider/entities.py @@ -4,7 +4,9 @@ import enum import pydantic -import mirai +# import mirai + +from ..platform.types import message as platform_message class FunctionCall(pydantic.BaseModel): @@ -79,7 +81,7 @@ def readable_str(self) -> str: else: return '未知消息' - def get_content_mirai_message_chain(self, prefix_text: str="") -> mirai.MessageChain | None: + def get_content_mirai_message_chain(self, prefix_text: str="") -> platform_message.MessageChain | None: """将内容转换为 Mirai MessageChain 对象 Args: @@ -89,15 +91,15 @@ def get_content_mirai_message_chain(self, prefix_text: str="") -> mirai.MessageC if self.content is None: return None elif isinstance(self.content, str): - return mirai.MessageChain([mirai.Plain(prefix_text+self.content)]) + return platform_message.MessageChain([platform_message.Plain(prefix_text+self.content)]) elif isinstance(self.content, list): mc = [] for ce in self.content: if ce.type == 'text': - mc.append(mirai.Plain(ce.text)) + mc.append(platform_message.Plain(ce.text)) elif ce.type == 'image_url': if ce.image_url.url.startswith("http"): - mc.append(mirai.Image(url=ce.image_url.url)) + mc.append(platform_message.Image(url=ce.image_url.url)) else: # base64 b64_str = ce.image_url.url @@ -105,15 +107,15 @@ def get_content_mirai_message_chain(self, prefix_text: str="") -> mirai.MessageC if b64_str.startswith("data:"): b64_str = b64_str.split(",")[1] - mc.append(mirai.Image(base64=b64_str)) + mc.append(platform_message.Image(base64=b64_str)) # 找第一个文字组件 if prefix_text: for i, c in enumerate(mc): - if isinstance(c, mirai.Plain): - mc[i] = mirai.Plain(prefix_text+c.text) + if isinstance(c, platform_message.Plain): + mc[i] = platform_message.Plain(prefix_text+c.text) break else: - mc.insert(0, mirai.Plain(prefix_text)) + mc.insert(0, platform_message.Plain(prefix_text)) - return mirai.MessageChain(mc) + return platform_message.MessageChain(mc) From fdba470e9a7d82dad9f30922267e0b440aea7185 Mon Sep 17 00:00:00 2001 From: RockChinQ <1010553892@qq.com> Date: Thu, 26 Sep 2024 00:28:57 +0800 Subject: [PATCH 02/71] =?UTF-8?q?perf:=20=E5=B0=86=20platform=20=E7=9A=84?= =?UTF-8?q?=20=E7=BB=84=E4=BB=B6=E5=AF=BC=E5=85=A5=E5=8C=85=20=5F=5Finit?= =?UTF-8?q?=5F=5F=20=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/platform/types/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/platform/types/__init__.py b/pkg/platform/types/__init__.py index e69de29b..998b0fb8 100644 --- a/pkg/platform/types/__init__.py +++ b/pkg/platform/types/__init__.py @@ -0,0 +1,3 @@ +from .entities import * +from .events import * +from .message import * From e8da26cb8a8f0583e6143db26d4ec20eff3f6975 Mon Sep 17 00:00:00 2001 From: RockChinQ <1010553892@qq.com> Date: Thu, 26 Sep 2024 11:23:37 +0800 Subject: [PATCH 03/71] fix: missing break --- pkg/plugin/manager.py | 2 ++ pkg/plugin/setting.py | 1 + 2 files changed, 3 insertions(+) diff --git a/pkg/plugin/manager.py b/pkg/plugin/manager.py index 4e0784aa..b1241eaf 100644 --- a/pkg/plugin/manager.py +++ b/pkg/plugin/manager.py @@ -48,6 +48,8 @@ async def load_plugins(self): # 按优先级倒序 self.plugins.sort(key=lambda x: x.priority, reverse=True) + self.ap.logger.debug(f'优先级排序后的插件列表 {self.plugins}') + async def initialize_plugins(self): for plugin in self.plugins: try: diff --git a/pkg/plugin/setting.py b/pkg/plugin/setting.py index 7e715af1..bd50603f 100644 --- a/pkg/plugin/setting.py +++ b/pkg/plugin/setting.py @@ -45,6 +45,7 @@ async def sync_setting( for plugin_container in plugin_containers: if plugin_container.plugin_name == value['name']: plugin_container.set_from_setting_dict(value) + break self.settings.data = { 'plugins': [ From 3469515e044f9546bb0b2b58c1b2f5127add3772 Mon Sep 17 00:00:00 2001 From: RockChinQ <1010553892@qq.com> Date: Thu, 26 Sep 2024 13:01:45 +0800 Subject: [PATCH 04/71] =?UTF-8?q?feat:=20=E5=88=A0=E9=99=A4=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E4=B8=AD=E5=AF=B9=20mirai=20=E7=9A=84=E5=BC=95?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- .github/dependabot.yml | 1 - .github/pull_request_template.md | 3 ++ pkg/command/entities.py | 1 - pkg/core/bootutils/deps.py | 1 - pkg/core/entities.py | 1 - pkg/pipeline/cntfilter/cntfilter.py | 4 --- pkg/pipeline/controller.py | 1 - pkg/pipeline/entities.py | 5 --- pkg/pipeline/longtext/longtext.py | 1 - pkg/pipeline/longtext/strategies/forward.py | 3 -- pkg/pipeline/longtext/strategies/image.py | 2 -- pkg/pipeline/longtext/strategy.py | 2 -- pkg/pipeline/pool.py | 1 - pkg/pipeline/preproc/preproc.py | 1 - pkg/pipeline/process/handlers/chat.py | 1 - pkg/pipeline/process/handlers/command.py | 1 - pkg/pipeline/respback/respback.py | 1 - pkg/pipeline/resprule/entities.py | 1 - pkg/pipeline/resprule/resprule.py | 1 - pkg/pipeline/resprule/rule.py | 2 -- pkg/pipeline/resprule/rules/atbot.py | 1 - pkg/pipeline/resprule/rules/prefix.py | 1 - pkg/pipeline/resprule/rules/random.py | 1 - pkg/pipeline/resprule/rules/regexp.py | 1 - pkg/pipeline/wrapper/wrapper.py | 14 +++----- pkg/platform/adapter.py | 39 ++++++++++----------- pkg/platform/manager.py | 2 -- pkg/platform/sources/aiocqhttp.py | 2 -- pkg/platform/sources/nakuru.py | 1 - pkg/platform/sources/qqbotpy.py | 1 - pkg/platform/sources/yirimirai.py | 3 -- pkg/platform/types/base.py | 14 ++++---- pkg/platform/types/message.py | 13 ++----- pkg/plugin/context.py | 5 ++- pkg/plugin/events.py | 1 - pkg/provider/entities.py | 9 +++-- requirements.txt | 1 - 38 files changed, 42 insertions(+), 103 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 12f9b83e..a5947b06 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -8,10 +8,10 @@ body: label: 消息平台适配器 description: "连接QQ使用的框架" options: - - yiri-mirai(Mirai) - Nakuru(go-cqhttp) - aiocqhttp(使用 OneBot 协议接入的) - qq-botpy(QQ官方API) + - yiri-mirai(Mirai) validations: required: false - type: input diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d1973eb3..53c58493 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,5 +10,4 @@ updates: schedule: interval: "weekly" allow: - - dependency-name: "yiri-mirai-rc" - dependency-name: "openai" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6b636c25..a4b3a1a4 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -6,8 +6,11 @@ ### PR 作者完成 +*请在方括号间写`x`以打勾 + - [ ] 阅读仓库[贡献指引](https://github.com/RockChinQ/QChatGPT/blob/master/CONTRIBUTING.md)了吗? - [ ] 与项目所有者沟通过了吗? +- [ ] 我确定已自行测试所作的更改,确保功能符合预期。 ### 项目所有者完成 diff --git a/pkg/command/entities.py b/pkg/command/entities.py index 3ea660f1..463dfe38 100644 --- a/pkg/command/entities.py +++ b/pkg/command/entities.py @@ -3,7 +3,6 @@ import typing import pydantic -# import mirai from ..core import app, entities as core_entities from . import errors, operator diff --git a/pkg/core/bootutils/deps.py b/pkg/core/bootutils/deps.py index e7df3e82..c392f800 100644 --- a/pkg/core/bootutils/deps.py +++ b/pkg/core/bootutils/deps.py @@ -5,7 +5,6 @@ "openai": "openai", "anthropic": "anthropic", "colorlog": "colorlog", - "mirai": "yiri-mirai-rc", "aiocqhttp": "aiocqhttp", "botpy": "qq-botpy", "PIL": "pillow", diff --git a/pkg/core/entities.py b/pkg/core/entities.py index 744c6d03..dbaa5ffd 100644 --- a/pkg/core/entities.py +++ b/pkg/core/entities.py @@ -6,7 +6,6 @@ import asyncio import pydantic -# import mirai from ..provider import entities as llm_entities from ..provider.modelmgr import entities diff --git a/pkg/pipeline/cntfilter/cntfilter.py b/pkg/pipeline/cntfilter/cntfilter.py index f0c5428e..f7376b61 100644 --- a/pkg/pipeline/cntfilter/cntfilter.py +++ b/pkg/pipeline/cntfilter/cntfilter.py @@ -1,9 +1,5 @@ from __future__ import annotations -# import mirai -# import mirai.models -# import mirai.models.message - from ...core import app from .. import stage, entities, stagemgr diff --git a/pkg/pipeline/controller.py b/pkg/pipeline/controller.py index 27b96dee..0f07e068 100644 --- a/pkg/pipeline/controller.py +++ b/pkg/pipeline/controller.py @@ -4,7 +4,6 @@ import typing import traceback -# import mirai from ..core import app, entities from . import entities as pipeline_entities diff --git a/pkg/pipeline/entities.py b/pkg/pipeline/entities.py index 347f5db7..cbeb3d0e 100644 --- a/pkg/pipeline/entities.py +++ b/pkg/pipeline/entities.py @@ -4,7 +4,6 @@ import typing import pydantic -# import mirai from ..platform.types import message as platform_message from ..core import entities @@ -28,10 +27,6 @@ class StageProcessResult(pydantic.BaseModel): user_notice: typing.Optional[typing.Union[str, list[platform_message.MessageComponent], platform_message.MessageChain, None]] = [] """只要设置了就会发送给用户""" - # TODO delete - # admin_notice: typing.Optional[typing.Union[str, list[mirai_message.MessageComponent], mirai.MessageChain, None]] = [] - """只要设置了就会发送给管理员""" - console_notice: typing.Optional[str] = '' """只要设置了就会输出到控制台""" diff --git a/pkg/pipeline/longtext/longtext.py b/pkg/pipeline/longtext/longtext.py index d3715b7e..ecb745d0 100644 --- a/pkg/pipeline/longtext/longtext.py +++ b/pkg/pipeline/longtext/longtext.py @@ -3,7 +3,6 @@ import traceback from PIL import Image, ImageDraw, ImageFont -# from mirai.models.message import MessageComponent, Plain, MessageChain from ...core import app from . import strategy diff --git a/pkg/pipeline/longtext/strategies/forward.py b/pkg/pipeline/longtext/strategies/forward.py index 206bf08c..c39c9208 100644 --- a/pkg/pipeline/longtext/strategies/forward.py +++ b/pkg/pipeline/longtext/strategies/forward.py @@ -2,9 +2,6 @@ from __future__ import annotations import typing -# from mirai.models import MessageChain -# from mirai.models.message import MessageComponent, ForwardMessageNode -# from mirai.models.base import MiraiBaseModel import pydantic from .. import strategy as strategy_model diff --git a/pkg/pipeline/longtext/strategies/image.py b/pkg/pipeline/longtext/strategies/image.py index 3b1805c3..9e32e598 100644 --- a/pkg/pipeline/longtext/strategies/image.py +++ b/pkg/pipeline/longtext/strategies/image.py @@ -8,8 +8,6 @@ from PIL import Image, ImageDraw, ImageFont -# from mirai.models import MessageChain, Image as ImageComponent -# from mirai.models.message import MessageComponent from ....platform.types import message as platform_message from .. import strategy as strategy_model diff --git a/pkg/pipeline/longtext/strategy.py b/pkg/pipeline/longtext/strategy.py index 5bc46a39..6f66bbff 100644 --- a/pkg/pipeline/longtext/strategy.py +++ b/pkg/pipeline/longtext/strategy.py @@ -2,8 +2,6 @@ import abc import typing -# import mirai -# from mirai.models.message import MessageComponent from ...core import app from ...core import entities as core_entities diff --git a/pkg/pipeline/pool.py b/pkg/pipeline/pool.py index b7f79510..45f16e66 100644 --- a/pkg/pipeline/pool.py +++ b/pkg/pipeline/pool.py @@ -2,7 +2,6 @@ import asyncio -# import mirai from ..core import entities from ..platform import adapter as msadapter diff --git a/pkg/pipeline/preproc/preproc.py b/pkg/pipeline/preproc/preproc.py index 976c8f7c..3a71a841 100644 --- a/pkg/pipeline/preproc/preproc.py +++ b/pkg/pipeline/preproc/preproc.py @@ -1,6 +1,5 @@ from __future__ import annotations -# import mirai from .. import stage, entities, stagemgr from ...core import entities as core_entities diff --git a/pkg/pipeline/process/handlers/chat.py b/pkg/pipeline/process/handlers/chat.py index d87e2853..6e192b78 100644 --- a/pkg/pipeline/process/handlers/chat.py +++ b/pkg/pipeline/process/handlers/chat.py @@ -5,7 +5,6 @@ import traceback import json -# import mirai from .. import handler from ... import entities diff --git a/pkg/pipeline/process/handlers/command.py b/pkg/pipeline/process/handlers/command.py index 5f6ed6ee..cec64a45 100644 --- a/pkg/pipeline/process/handlers/command.py +++ b/pkg/pipeline/process/handlers/command.py @@ -1,7 +1,6 @@ from __future__ import annotations import typing -# import mirai from .. import handler from ... import entities diff --git a/pkg/pipeline/respback/respback.py b/pkg/pipeline/respback/respback.py index 1b0cd3ac..d3dd83fe 100644 --- a/pkg/pipeline/respback/respback.py +++ b/pkg/pipeline/respback/respback.py @@ -3,7 +3,6 @@ import random import asyncio -# import mirai from ...core import app diff --git a/pkg/pipeline/resprule/entities.py b/pkg/pipeline/resprule/entities.py index 42c2092e..22927154 100644 --- a/pkg/pipeline/resprule/entities.py +++ b/pkg/pipeline/resprule/entities.py @@ -1,5 +1,4 @@ import pydantic -# import mirai from ...platform.types import message as platform_message diff --git a/pkg/pipeline/resprule/resprule.py b/pkg/pipeline/resprule/resprule.py index aa07bb7c..77858f0d 100644 --- a/pkg/pipeline/resprule/resprule.py +++ b/pkg/pipeline/resprule/resprule.py @@ -1,6 +1,5 @@ from __future__ import annotations -# import mirai from ...core import app from . import entities as rule_entities, rule diff --git a/pkg/pipeline/resprule/rule.py b/pkg/pipeline/resprule/rule.py index b40e5ac5..ad69d8a0 100644 --- a/pkg/pipeline/resprule/rule.py +++ b/pkg/pipeline/resprule/rule.py @@ -2,8 +2,6 @@ import abc import typing -# import mirai - from ...core import app, entities as core_entities from . import entities diff --git a/pkg/pipeline/resprule/rules/atbot.py b/pkg/pipeline/resprule/rules/atbot.py index d767f95a..a0b7a7c8 100644 --- a/pkg/pipeline/resprule/rules/atbot.py +++ b/pkg/pipeline/resprule/rules/atbot.py @@ -1,6 +1,5 @@ from __future__ import annotations -# import mirai from .. import rule as rule_model from .. import entities diff --git a/pkg/pipeline/resprule/rules/prefix.py b/pkg/pipeline/resprule/rules/prefix.py index c010c0f8..fb7bbcfc 100644 --- a/pkg/pipeline/resprule/rules/prefix.py +++ b/pkg/pipeline/resprule/rules/prefix.py @@ -1,4 +1,3 @@ -# import mirai from .. import rule as rule_model from .. import entities diff --git a/pkg/pipeline/resprule/rules/random.py b/pkg/pipeline/resprule/rules/random.py index bf8603a9..0178f2c4 100644 --- a/pkg/pipeline/resprule/rules/random.py +++ b/pkg/pipeline/resprule/rules/random.py @@ -1,6 +1,5 @@ import random -# import mirai from .. import rule as rule_model from .. import entities diff --git a/pkg/pipeline/resprule/rules/regexp.py b/pkg/pipeline/resprule/rules/regexp.py index a6a3ecd2..f5f5b3f6 100644 --- a/pkg/pipeline/resprule/rules/regexp.py +++ b/pkg/pipeline/resprule/rules/regexp.py @@ -1,6 +1,5 @@ import re -# import mirai from .. import rule as rule_model from .. import entities diff --git a/pkg/pipeline/wrapper/wrapper.py b/pkg/pipeline/wrapper/wrapper.py index 0e093826..1ffb3147 100644 --- a/pkg/pipeline/wrapper/wrapper.py +++ b/pkg/pipeline/wrapper/wrapper.py @@ -2,7 +2,6 @@ import typing -# import mirai from ...core import app, entities as core_entities from .. import entities @@ -46,19 +45,14 @@ async def process( else: if query.resp_messages[-1].role == 'command': - # query.resp_message_chain.append(mirai.MessageChain("[bot] "+query.resp_messages[-1].content)) - query.resp_message_chain.append(query.resp_messages[-1].get_content_mirai_message_chain(prefix_text='[bot] ')) + query.resp_message_chain.append(query.resp_messages[-1].get_content_platform_message_chain(prefix_text='[bot] ')) yield entities.StageProcessResult( result_type=entities.ResultType.CONTINUE, new_query=query ) elif query.resp_messages[-1].role == 'plugin': - # if not isinstance(query.resp_messages[-1].content, mirai.MessageChain): - # query.resp_message_chain.append(mirai.MessageChain(query.resp_messages[-1].content)) - # else: - # query.resp_message_chain.append(query.resp_messages[-1].content) - query.resp_message_chain.append(query.resp_messages[-1].get_content_mirai_message_chain()) + query.resp_message_chain.append(query.resp_messages[-1].get_content_platform_message_chain()) yield entities.StageProcessResult( result_type=entities.ResultType.CONTINUE, @@ -73,7 +67,7 @@ async def process( reply_text = '' if result.content: # 有内容 - reply_text = str(result.get_content_mirai_message_chain()) + reply_text = str(result.get_content_platform_message_chain()) # ============= 触发插件事件 =============== event_ctx = await self.ap.plugin_mgr.emit_event( @@ -101,7 +95,7 @@ async def process( else: - query.resp_message_chain.append(result.get_content_mirai_message_chain()) + query.resp_message_chain.append(result.get_content_platform_message_chain()) yield entities.StageProcessResult( result_type=entities.ResultType.CONTINUE, diff --git a/pkg/platform/adapter.py b/pkg/platform/adapter.py index c6d97b3a..7cf64a12 100644 --- a/pkg/platform/adapter.py +++ b/pkg/platform/adapter.py @@ -4,7 +4,6 @@ import typing import abc -# import mirai from ..core import app from .types import message as platform_message @@ -64,7 +63,7 @@ async def send_message( Args: target_type (str): 目标类型,`person`或`group` target_id (str): 目标ID - message (mirai.MessageChain): YiriMirai库的消息链 + message (platform.types.MessageChain): 消息链 """ raise NotImplementedError @@ -77,8 +76,8 @@ async def reply_message( """回复消息 Args: - message_source (mirai.MessageEvent): YiriMirai消息源事件 - message (mirai.MessageChain): YiriMirai库的消息链 + message_source (platform.types.MessageEvent): 消息源事件 + message (platform.types.MessageChain): 消息链 quote_origin (bool, optional): 是否引用原消息. Defaults to False. """ raise NotImplementedError @@ -95,8 +94,8 @@ def register_listener( """注册事件监听器 Args: - event_type (typing.Type[mirai.Event]): YiriMirai事件类型 - callback (typing.Callable[[mirai.Event], None]): 回调函数,接收一个参数,为YiriMirai事件 + event_type (typing.Type[platform.types.Event]): 事件类型 + callback (typing.Callable[[platform.types.Event], None]): 回调函数,接收一个参数,为事件 """ raise NotImplementedError @@ -108,8 +107,8 @@ def unregister_listener( """注销事件监听器 Args: - event_type (typing.Type[mirai.Event]): YiriMirai事件类型 - callback (typing.Callable[[mirai.Event], None]): 回调函数,接收一个参数,为YiriMirai事件 + event_type (typing.Type[platform.types.Event]): 事件类型 + callback (typing.Callable[[platform.types.Event], None]): 回调函数,接收一个参数,为事件 """ raise NotImplementedError @@ -130,25 +129,25 @@ class MessageConverter: """消息链转换器基类""" @staticmethod def yiri2target(message_chain: platform_message.MessageChain): - """将YiriMirai消息链转换为目标消息链 + """将源平台消息链转换为目标平台消息链 Args: - message_chain (mirai.MessageChain): YiriMirai消息链 + message_chain (platform.types.MessageChain): 源平台消息链 Returns: - typing.Any: 目标消息链 + typing.Any: 目标平台消息链 """ raise NotImplementedError @staticmethod def target2yiri(message_chain: typing.Any) -> platform_message.MessageChain: - """将目标消息链转换为YiriMirai消息链 + """将目标平台消息链转换为源平台消息链 Args: - message_chain (typing.Any): 目标消息链 + message_chain (typing.Any): 目标平台消息链 Returns: - mirai.MessageChain: YiriMirai消息链 + platform.types.MessageChain: 源平台消息链 """ raise NotImplementedError @@ -158,24 +157,24 @@ class EventConverter: @staticmethod def yiri2target(event: typing.Type[platform_message.Event]): - """将YiriMirai事件转换为目标事件 + """将源平台事件转换为目标平台事件 Args: - event (typing.Type[mirai.Event]): YiriMirai事件 + event (typing.Type[platform.types.Event]): 源平台事件 Returns: - typing.Any: 目标事件 + typing.Any: 目标平台事件 """ raise NotImplementedError @staticmethod def target2yiri(event: typing.Any) -> platform_message.Event: - """将目标事件的调用参数转换为YiriMirai的事件参数对象 + """将目标平台事件的调用参数转换为源平台的事件参数对象 Args: - event (typing.Any): 目标事件 + event (typing.Any): 目标平台事件 Returns: - typing.Type[mirai.Event]: YiriMirai事件 + typing.Type[platform.types.Event]: 源平台事件 """ raise NotImplementedError diff --git a/pkg/platform/manager.py b/pkg/platform/manager.py index 4e5131d5..d46e5b80 100644 --- a/pkg/platform/manager.py +++ b/pkg/platform/manager.py @@ -6,9 +6,7 @@ import asyncio import traceback -# from mirai import At, GroupMessage, MessageEvent, StrangerMessage, \ # FriendMessage, Image, MessageChain, Plain -# import mirai from ..platform import adapter as msadapter from ..core import app, entities as core_entities diff --git a/pkg/platform/sources/aiocqhttp.py b/pkg/platform/sources/aiocqhttp.py index adf9c4e1..25d197e3 100644 --- a/pkg/platform/sources/aiocqhttp.py +++ b/pkg/platform/sources/aiocqhttp.py @@ -5,8 +5,6 @@ import time import datetime -# import mirai -# import mirai.models.message as yiri_message import aiocqhttp from .. import adapter diff --git a/pkg/platform/sources/nakuru.py b/pkg/platform/sources/nakuru.py index 5c05f9d4..2fbe8be4 100644 --- a/pkg/platform/sources/nakuru.py +++ b/pkg/platform/sources/nakuru.py @@ -6,7 +6,6 @@ import traceback import logging -# import mirai import nakuru import nakuru.entities.components as nkc diff --git a/pkg/platform/sources/qqbotpy.py b/pkg/platform/sources/qqbotpy.py index 5a882c93..cbc86f44 100644 --- a/pkg/platform/sources/qqbotpy.py +++ b/pkg/platform/sources/qqbotpy.py @@ -6,7 +6,6 @@ import re import traceback -# import mirai import botpy import botpy.message as botpy_message import botpy.types.message as botpy_message_type diff --git a/pkg/platform/sources/yirimirai.py b/pkg/platform/sources/yirimirai.py index 4082e90a..aa0823fd 100644 --- a/pkg/platform/sources/yirimirai.py +++ b/pkg/platform/sources/yirimirai.py @@ -1,9 +1,6 @@ # import asyncio # import typing -# import mirai -# import mirai.models.bus -# from mirai.bot import MiraiRunner # from .. import adapter as adapter_model # from ...core import app diff --git a/pkg/platform/types/base.py b/pkg/platform/types/base.py index 964e9c6f..d3c0be49 100644 --- a/pkg/platform/types/base.py +++ b/pkg/platform/types/base.py @@ -6,7 +6,7 @@ class PlatformMetaclass(pdm.ModelMetaclass): - """此类是 YiriMirai 中使用的 pydantic 模型的元类的基类。""" + """此类是平台中使用的 pydantic 模型的元类的基类。""" def to_camel(name: str) -> str: @@ -23,7 +23,7 @@ class PlatformBaseModel(BaseModel, metaclass=PlatformMetaclass): 启用了三项配置: 1. 允许解析时传入额外的值,并将额外值保存在模型中。 2. 允许通过别名访问字段。 - 3. 自动生成小驼峰风格的别名,以符合 mirai-api-http 的命名。 + 3. 自动生成小驼峰风格的别名。 """ def __init__(self, *args, **kwargs): """""" @@ -47,17 +47,17 @@ class PlatformIndexedMetaclass(PlatformMetaclass): def __new__(cls, name, bases, attrs, **kwargs): new_cls = super().__new__(cls, name, bases, attrs, **kwargs) - # 第一类:MiraiIndexedModel + # 第一类:PlatformIndexedModel if name == 'PlatformIndexedModel': cls.__indexedmodel__ = new_cls new_cls.__indexes__ = {} return new_cls - # 第二类:MiraiIndexedModel 的直接子类,这些是可以通过子类名获取子类的类。 + # 第二类:PlatformIndexedModel 的直接子类,这些是可以通过子类名获取子类的类。 if cls.__indexedmodel__ in bases: cls.__indexedbases__.append(new_cls) new_cls.__indexes__ = {} return new_cls - # 第三类:MiraiIndexedModel 的直接子类的子类,这些添加到直接子类的索引中。 + # 第三类:PlatformIndexedModel 的直接子类的子类,这些添加到直接子类的索引中。 for base in cls.__indexedbases__: if issubclass(new_cls, base): base.__indexes__[name] = new_cls @@ -79,7 +79,7 @@ def get_subtype(cls, name: str) -> Type['PlatformIndexedModel']: name: 类名称。 Returns: - Type['MiraiIndexedModel']: 子类类型。 + Type['PlatformIndexedModel']: 子类类型。 """ try: type_ = cls.__indexes__.get(name) @@ -97,7 +97,7 @@ def parse_subtype(cls, obj: dict) -> 'PlatformIndexedModel': obj: 一个字典,包含了模型对象的属性。 Returns: - MiraiIndexedModel: 构造的对象。 + PlatformIndexedModel: 构造的对象。 """ if cls in PlatformIndexedModel.__subclasses__(): ModelType = cls.get_subtype(obj['type']) diff --git a/pkg/platform/types/message.py b/pkg/platform/types/message.py index 00e420b1..e790f881 100644 --- a/pkg/platform/types/message.py +++ b/pkg/platform/types/message.py @@ -514,9 +514,6 @@ def __eq__(self, other): def __str__(self): return f"@{self.display or self.target}" - def as_mirai_code(self) -> str: - return f"[mirai:at:{self.target}]" - class AtAll(MessageComponent): """At全体。""" @@ -525,9 +522,6 @@ class AtAll(MessageComponent): def __str__(self): return "@全体成员" - def as_mirai_code(self) -> str: - return f"[mirai:atall]" - class Image(MessageComponent): """图片。""" @@ -549,12 +543,9 @@ def __eq__(self, other): def __str__(self): return '[图片]' - def as_mirai_code(self) -> str: - return f"[mirai:image:{self.image_id}]" - @pydantic.validator('path') def validate_path(cls, path: typing.Union[str, Path, None]): - """修复 path 参数的行为,使之相对于 YiriMirai 的启动路径。""" + """修复 path 参数的行为,使之相对于 QChatGPT 的启动路径。""" if path: try: return str(Path(path).resolve(strict=True)) @@ -682,7 +673,7 @@ class Voice(MessageComponent): """语音的长度,单位为秒。""" @pydantic.validator('path') def validate_path(cls, path: typing.Optional[str]): - """修复 path 参数的行为,使之相对于 YiriMirai 的启动路径。""" + """修复 path 参数的行为,使之相对于 QChatGPT 的启动路径。""" if path: try: return str(Path(path).resolve(strict=True)) diff --git a/pkg/plugin/context.py b/pkg/plugin/context.py index 9e4d8caf..f6cc1769 100644 --- a/pkg/plugin/context.py +++ b/pkg/plugin/context.py @@ -3,7 +3,6 @@ import typing import abc import pydantic -# import mirai from . import events from ..provider.tools import entities as tools_entities @@ -179,7 +178,7 @@ async def reply(self, message_chain: platform_message.MessageChain): """回复此次消息请求 Args: - message_chain (mirai.MessageChain): YiriMirai库的消息链,若用户使用的不是 YiriMirai 适配器,程序也能自动转换为目标消息链 + message_chain (platform.types.MessageChain): 源平台的消息链,若用户使用的不是源平台适配器,程序也能自动转换为目标平台消息链 """ await self.host.ap.platform_mgr.send( event=self.event.query.message_event, @@ -198,7 +197,7 @@ async def send_message( Args: target_type (str): 目标类型,`person`或`group` target_id (str): 目标ID - message (mirai.MessageChain): YiriMirai库的消息链,若用户使用的不是 YiriMirai 适配器,程序也能自动转换为目标消息链 + message (platform.types.MessageChain): 源平台的消息链,若用户使用的不是源平台适配器,程序也能自动转换为目标平台消息链 """ await self.event.query.adapter.send_message( target_type=target_type, diff --git a/pkg/plugin/events.py b/pkg/plugin/events.py index 3fc2ea73..013dd113 100644 --- a/pkg/plugin/events.py +++ b/pkg/plugin/events.py @@ -3,7 +3,6 @@ import typing import pydantic -# import mirai from ..core import entities as core_entities from ..provider import entities as llm_entities diff --git a/pkg/provider/entities.py b/pkg/provider/entities.py index 950cbb17..803613a3 100644 --- a/pkg/provider/entities.py +++ b/pkg/provider/entities.py @@ -4,7 +4,6 @@ import enum import pydantic -# import mirai from ..platform.types import message as platform_message @@ -75,14 +74,14 @@ class Message(pydantic.BaseModel): def readable_str(self) -> str: if self.content is not None: - return str(self.role) + ": " + str(self.get_content_mirai_message_chain()) + return str(self.role) + ": " + str(self.get_content_platform_message_chain()) elif self.tool_calls is not None: return f'调用工具: {self.tool_calls[0].id}' else: return '未知消息' - def get_content_mirai_message_chain(self, prefix_text: str="") -> platform_message.MessageChain | None: - """将内容转换为 Mirai MessageChain 对象 + def get_content_platform_message_chain(self, prefix_text: str="") -> platform_message.MessageChain | None: + """将内容转换为平台消息 MessageChain 对象 Args: prefix_text (str): 首个文字组件的前缀文本 @@ -108,7 +107,7 @@ def get_content_mirai_message_chain(self, prefix_text: str="") -> platform_messa b64_str = b64_str.split(",")[1] mc.append(platform_message.Image(base64=b64_str)) - + # 找第一个文字组件 if prefix_text: for i, c in enumerate(mc): diff --git a/requirements.txt b/requirements.txt index 1b554d29..7bf257ed 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ requests openai>1.0.0 anthropic colorlog~=6.6.0 -yiri-mirai-rc aiocqhttp qq-botpy nakuru-project-idk From c53ffaca6c0f4791db90272846eb2a572b0ce95e Mon Sep 17 00:00:00 2001 From: RockChinQ <1010553892@qq.com> Date: Thu, 26 Sep 2024 14:38:18 +0800 Subject: [PATCH 05/71] =?UTF-8?q?fix:=20=E5=A4=84=E7=90=86=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=20import=20mirai=20=E6=97=B6=E7=9A=84=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/core/entities.py | 2 +- pkg/platform/manager.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/core/entities.py b/pkg/core/entities.py index dbaa5ffd..67b05666 100644 --- a/pkg/core/entities.py +++ b/pkg/core/entities.py @@ -110,7 +110,7 @@ class Session(pydantic.BaseModel): using_conversation: typing.Optional[Conversation] = None - conversations: typing.Optional[list[Conversation]] = [] + conversations: typing.Optional[list[Conversation]] = pydantic.Field(default_factory=list) create_time: typing.Optional[datetime.datetime] = pydantic.Field(default_factory=datetime.datetime.now) diff --git a/pkg/platform/manager.py b/pkg/platform/manager.py index d46e5b80..aed8deff 100644 --- a/pkg/platform/manager.py +++ b/pkg/platform/manager.py @@ -2,6 +2,7 @@ import json import os +import sys import logging import asyncio import traceback @@ -15,6 +16,10 @@ from .types import events as platform_events from .types import entities as platform_entities +# 处理 3.4 移除了 YiriMirai 之后,插件的兼容性问题 +from . import types as mirai +sys.modules['mirai'] = mirai + # 控制QQ消息输入输出的类 class PlatformManager: From 21f153e5c32a9d21c442cbf80dbad56333c492ca Mon Sep 17 00:00:00 2001 From: RockChinQ <1010553892@qq.com> Date: Fri, 11 Oct 2024 22:23:08 +0800 Subject: [PATCH 06/71] =?UTF-8?q?chore:=20webui=20=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/.browserslistrc | 4 + web/.editorconfig | 5 + web/.eslintrc.js | 10 + web/.gitignore | 22 + web/README.md | 1 + web/index.html | 16 + web/jsconfig.json | 20 + web/package-lock.json | 4867 ++++++++++++++++++++++++++++++++++ web/package.json | 34 + web/public/favicon.ico | Bin 0 -> 15406 bytes web/src/App.vue | 11 + web/src/assets/logo.png | Bin 0 -> 11955 bytes web/src/assets/logo.svg | 6 + web/src/main.js | 20 + web/src/pages/index.vue | 6 + web/src/plugins/index.js | 15 + web/src/plugins/vuetify.js | 19 + web/src/router/index.js | 36 + web/src/styles/settings.scss | 10 + web/vite.config.mjs | 54 + 20 files changed, 5156 insertions(+) create mode 100644 web/.browserslistrc create mode 100644 web/.editorconfig create mode 100644 web/.eslintrc.js create mode 100644 web/.gitignore create mode 100644 web/README.md create mode 100644 web/index.html create mode 100644 web/jsconfig.json create mode 100644 web/package-lock.json create mode 100644 web/package.json create mode 100644 web/public/favicon.ico create mode 100644 web/src/App.vue create mode 100644 web/src/assets/logo.png create mode 100644 web/src/assets/logo.svg create mode 100644 web/src/main.js create mode 100644 web/src/pages/index.vue create mode 100644 web/src/plugins/index.js create mode 100644 web/src/plugins/vuetify.js create mode 100644 web/src/router/index.js create mode 100644 web/src/styles/settings.scss create mode 100644 web/vite.config.mjs diff --git a/web/.browserslistrc b/web/.browserslistrc new file mode 100644 index 00000000..dc3bc09a --- /dev/null +++ b/web/.browserslistrc @@ -0,0 +1,4 @@ +> 1% +last 2 versions +not dead +not ie 11 diff --git a/web/.editorconfig b/web/.editorconfig new file mode 100644 index 00000000..7053c49a --- /dev/null +++ b/web/.editorconfig @@ -0,0 +1,5 @@ +[*.{js,jsx,ts,tsx,vue}] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/web/.eslintrc.js b/web/.eslintrc.js new file mode 100644 index 00000000..6e7e1b14 --- /dev/null +++ b/web/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + root: true, + env: { + node: true, + }, + extends: [ + 'plugin:vue/vue3-essential', + 'eslint:recommended', + ], +} diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 00000000..11f5d714 --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,22 @@ +.DS_Store +node_modules +/dist + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/web/README.md b/web/README.md new file mode 100644 index 00000000..280a55a2 --- /dev/null +++ b/web/README.md @@ -0,0 +1 @@ +# WebUI diff --git a/web/index.html b/web/index.html new file mode 100644 index 00000000..150c3979 --- /dev/null +++ b/web/index.html @@ -0,0 +1,16 @@ + + + + + + + + LangBot 面板 + + + +
+ + + + diff --git a/web/jsconfig.json b/web/jsconfig.json new file mode 100644 index 00000000..dad0634c --- /dev/null +++ b/web/jsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "allowJs": true, + "target": "es5", + "module": "esnext", + "baseUrl": "./", + "moduleResolution": "bundler", + "paths": { + "@/*": [ + "src/*" + ] + }, + "lib": [ + "esnext", + "dom", + "dom.iterable", + "scripthost" + ] + } +} diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 00000000..bca39b56 --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,4867 @@ +{ + "name": "web", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web", + "version": "0.0.0", + "dependencies": { + "@mdi/font": "7.4.47", + "core-js": "^3.37.1", + "roboto-fontface": "*", + "vue": "^3.4.31", + "vuetify": "^3.6.11" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.5", + "eslint": "^8.57.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.4.0", + "eslint-plugin-vue": "^9.27.0", + "sass": "1.77.6", + "unplugin-fonts": "^1.1.1", + "unplugin-vue-components": "^0.27.2", + "unplugin-vue-router": "^0.10.0", + "vite": "^5.3.3", + "vite-plugin-vuetify": "^2.0.3", + "vue-router": "^4.4.0" + } + }, + "node_modules/@antfu/utils": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", + "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@mdi/font": { + "version": "7.4.47", + "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz", + "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==", + "license": "Apache-2.0" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", + "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.5.tgz", + "integrity": "sha512-SU5cvamg0Eyu/F+kLeMXS7GoahL+OoizlclVFX3l5Ql6yNlywJJ0OuqTzUx0v+aHhPHEB/56CT06GQrRrGNYww==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.5.tgz", + "integrity": "sha512-S4pit5BP6E5R5C8S6tgU/drvgjtYW76FBuG6+ibG3tMvlD1h9LHVF9KmlmaUBQ8Obou7hEyS+0w+IR/VtxwNMQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.5.tgz", + "integrity": "sha512-250ZGg4ipTL0TGvLlfACkIxS9+KLtIbn7BCZjsZj88zSg2Lvu3Xdw6dhAhfe/FjjXPVNCtcSp+WZjVsD3a/Zlw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.5.tgz", + "integrity": "sha512-D8brJEFg5D+QxFcW6jYANu+Rr9SlKtTenmsX5hOSzNYVrK5oLAEMTUgKWYJP+wdKyCdeSwnapLsn+OVRFycuQg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.5.tgz", + "integrity": "sha512-PNqXYmdNFyWNg0ma5LdY8wP+eQfdvyaBAojAXgO7/gs0Q/6TQJVXAXe8gwW9URjbS0YAammur0fynYGiWsKlXw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.5.tgz", + "integrity": "sha512-kSSCZOKz3HqlrEuwKd9TYv7vxPYD77vHSUvM2y0YaTGnFc8AdI5TTQRrM1yIp3tXCKrSL9A7JLoILjtad5t8pQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.5.tgz", + "integrity": "sha512-oTXQeJHRbOnwRnRffb6bmqmUugz0glXaPyspp4gbQOPVApdpRrY/j7KP3lr7M8kTfQTyrBUzFjj5EuHAhqH4/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.5.tgz", + "integrity": "sha512-qnOTIIs6tIGFKCHdhYitgC2XQ2X25InIbZFor5wh+mALH84qnFHvc+vmWUpyX97B0hNvwNUL4B+MB8vJvH65Fw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.5.tgz", + "integrity": "sha512-TMYu+DUdNlgBXING13rHSfUc3Ky5nLPbWs4bFnT+R6Vu3OvXkTkixvvBKk8uO4MT5Ab6lC3U7x8S8El2q5o56w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.5.tgz", + "integrity": "sha512-PTQq1Kz22ZRvuhr3uURH+U/Q/a0pbxJoICGSprNLAoBEkyD3Sh9qP5I0Asn0y0wejXQBbsVMRZRxlbGFD9OK4A==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.5.tgz", + "integrity": "sha512-bR5nCojtpuMss6TDEmf/jnBnzlo+6n1UhgwqUvRoe4VIotC7FG1IKkyJbwsT7JDsF2jxR+NTnuOwiGv0hLyDoQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.5.tgz", + "integrity": "sha512-N0jPPhHjGShcB9/XXZQWuWBKZQnC1F36Ce3sDqWpujsGjDz/CQtOL9LgTrJ+rJC8MJeesMWrMWVLKKNR/tMOCA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.5.tgz", + "integrity": "sha512-uBa2e28ohzNNwjr6Uxm4XyaA1M/8aTgfF2T7UIlElLaeXkgpmIJ2EitVNQxjO9xLLLy60YqAgKn/AqSpCUkE9g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.5.tgz", + "integrity": "sha512-RXT8S1HP8AFN/Kr3tg4fuYrNxZ/pZf1HemC5Tsddc6HzgGnJm0+Lh5rAHJkDuW3StI0ynNXukidROMXYl6ew8w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.5.tgz", + "integrity": "sha512-ElTYOh50InL8kzyUD6XsnPit7jYCKrphmddKAe1/Ytt74apOxDq5YEcbsiKs0fR3vff3jEneMM+3I7jbqaMyBg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.5.tgz", + "integrity": "sha512-+lvL/4mQxSV8MukpkKyyvfwhH266COcWlXE/1qxwN08ajovta3459zrjLghYMgDerlzNwLAcFpvU+WWE5y6nAQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz", + "integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue-macros/common": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-1.14.0.tgz", + "integrity": "sha512-xwQhDoEXRNXobNQmdqOD20yUGdVLVLZe4zhDlT9q/E+z+mvT3wukaAoJG80XRnv/BcgOOCVpxqpkQZ3sNTgjWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.6", + "@rollup/pluginutils": "^5.1.0", + "@vue/compiler-sfc": "^3.5.4", + "ast-kit": "^1.1.0", + "local-pkg": "^0.5.0", + "magic-string-ast": "^0.6.2" + }, + "engines": { + "node": ">=16.14.0" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.2.25" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.10.tgz", + "integrity": "sha512-iXWlk+Cg/ag7gLvY0SfVucU8Kh2CjysYZjhhP70w9qI4MvSox4frrP+vDGvtQuzIcgD8+sxM6lZvCtdxGunTAA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.10", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.10.tgz", + "integrity": "sha512-DyxHC6qPcktwYGKOIy3XqnHRrrXyWR2u91AjP+nLkADko380srsC2DC3s7Y1Rk6YfOlxOlvEQKa9XXmLI+W4ZA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.10", + "@vue/shared": "3.5.10" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.10.tgz", + "integrity": "sha512-to8E1BgpakV7224ZCm8gz1ZRSyjNCAWEplwFMWKlzCdP9DkMKhRRwt0WkCjY7jkzi/Vz3xgbpeig5Pnbly4Tow==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.10", + "@vue/compiler-dom": "3.5.10", + "@vue/compiler-ssr": "3.5.10", + "@vue/shared": "3.5.10", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.47", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.10.tgz", + "integrity": "sha512-hxP4Y3KImqdtyUKXDRSxKSRkSm1H9fCvhojEYrnaoWhE4w/y8vwWhnosJoPPe2AXm5sU7CSbYYAgkt2ZPhDz+A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.10", + "@vue/shared": "3.5.10" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.10.tgz", + "integrity": "sha512-kW08v06F6xPSHhid9DJ9YjOGmwNDOsJJQk0ax21wKaUYzzuJGEuoKNU2Ujux8FLMrP7CFJJKsHhXN9l2WOVi2g==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.10" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.10.tgz", + "integrity": "sha512-9Q86I5Qq3swSkFfzrZ+iqEy7Vla325M7S7xc1NwKnRm/qoi1Dauz0rT6mTMmscqx4qz0EDJ1wjB+A36k7rl8mA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.10", + "@vue/shared": "3.5.10" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.10.tgz", + "integrity": "sha512-t3x7ht5qF8ZRi1H4fZqFzyY2j+GTMTDxRheT+i8M9Ph0oepUxoadmbwlFwMoW7RYCpNQLpP2Yx3feKs+fyBdpA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.10", + "@vue/runtime-core": "3.5.10", + "@vue/shared": "3.5.10", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.10.tgz", + "integrity": "sha512-IVE97tt2kGKwHNq9yVO0xdh1IvYfZCShvDSy46JIh5OQxP1/EXSpoDqetVmyIzL7CYOWnnmMkVqd7YK2QSWkdw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.10", + "@vue/shared": "3.5.10" + }, + "peerDependencies": { + "vue": "3.5.10" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.10.tgz", + "integrity": "sha512-VkkBhU97Ki+XJ0xvl4C9YJsIZ2uIlQ7HqPpZOS3m9VCvmROPaChZU6DexdMJqvz9tbgG+4EtFVrSuailUq5KGQ==", + "license": "MIT" + }, + "node_modules/@vuetify/loader-shared": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@vuetify/loader-shared/-/loader-shared-2.0.3.tgz", + "integrity": "sha512-Ss3GC7eJYkp2SF6xVzsT7FAruEmdihmn4OCk2+UocREerlXKWgOKKzTN5PN3ZVN5q05jHHrsNhTuWbhN61Bpdg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "upath": "^2.0.1" + }, + "peerDependencies": { + "vue": "^3.0.0", + "vuetify": "^3.0.0" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-kit": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-1.2.1.tgz", + "integrity": "sha512-h31wotR7rkFLrlmGPn0kGqOZ/n5EQFvp7dBs400chpHDhHc8BK3gpvyHDluRujuGgeoTAv3dSIMz9BI3JxAWyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.6", + "pathe": "^1.1.2" + }, + "engines": { + "node": ">=16.14.0" + } + }, + "node_modules/ast-walker-scope": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.6.2.tgz", + "integrity": "sha512-1UWOyC50xI3QZkRuDj6PqDtpm1oHWtYs+NQGwqL/2R11eN3Q81PHAHPM0SWW3BNQm53UDwS//Jv8L4CCVLM1bQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "ast-kit": "^1.0.1" + }, + "engines": { + "node": ">=16.14.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "devOptional": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-compat-utils/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-config-standard": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.9.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-n": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "builtins": "^5.0.1", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", + "minimatch": "^3.1.2", + "resolve": "^1.22.2", + "semver": "^7.5.3" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.28.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.28.0.tgz", + "integrity": "sha512-ShrihdjIhOTxs+MfWun6oJWuk+g/LAhN+CiuOl/jjkG3l0F2AuK5NMTaWqyvBgkFtpYmyks6P4603mLmhNJW8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "globals": "^13.24.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "vue-eslint-parser": "^9.4.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-vue/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/magic-string-ast": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-0.6.2.tgz", + "integrity": "sha512-oN3Bcd7ZVt+0VGEs7402qR/tjgjbM7kPlH/z7ufJnzTLVBzXJITRHOJiwMmmYMgZfdoWQsfQcY+iKlxiBppnMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.10" + }, + "engines": { + "node": ">=16.14.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mlly": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", + "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", + "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roboto-fontface": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz", + "integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g==", + "license": "Apache-2.0" + }, + "node_modules/rollup": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.5.tgz", + "integrity": "sha512-WoinX7GeQOFMGznEcWA1WrTQCd/tpEbMkc3nuMs9BT0CPjMdSjPMTVClwWd4pgSQwJdP65SK9mTCNvItlr5o7w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.22.5", + "@rollup/rollup-android-arm64": "4.22.5", + "@rollup/rollup-darwin-arm64": "4.22.5", + "@rollup/rollup-darwin-x64": "4.22.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.5", + "@rollup/rollup-linux-arm-musleabihf": "4.22.5", + "@rollup/rollup-linux-arm64-gnu": "4.22.5", + "@rollup/rollup-linux-arm64-musl": "4.22.5", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.5", + "@rollup/rollup-linux-riscv64-gnu": "4.22.5", + "@rollup/rollup-linux-s390x-gnu": "4.22.5", + "@rollup/rollup-linux-x64-gnu": "4.22.5", + "@rollup/rollup-linux-x64-musl": "4.22.5", + "@rollup/rollup-win32-arm64-msvc": "4.22.5", + "@rollup/rollup-win32-ia32-msvc": "4.22.5", + "@rollup/rollup-win32-x64-msvc": "4.22.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unplugin": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.14.1.tgz", + "integrity": "sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.12.1", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "webpack-sources": "^3" + }, + "peerDependenciesMeta": { + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/unplugin-fonts": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unplugin-fonts/-/unplugin-fonts-1.1.1.tgz", + "integrity": "sha512-/Aw/rL9D2aslGGM0vi+2R2aG508RSwawLnnBuo+JDSqYc4cHJO1R1phllhN6GysEhBp/6a4B6+vSFPVapWyAAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.2.12", + "unplugin": "^1.3.1" + }, + "peerDependencies": { + "@nuxt/kit": "^3.0.0", + "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/unplugin-vue-components": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.27.4.tgz", + "integrity": "sha512-1XVl5iXG7P1UrOMnaj2ogYa5YTq8aoh5jwDPQhemwO/OrXW+lPQKDXd1hMz15qxQPxgb/XXlbgo3HQ2rLEbmXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/utils": "^0.7.10", + "@rollup/pluginutils": "^5.1.0", + "chokidar": "^3.6.0", + "debug": "^4.3.6", + "fast-glob": "^3.3.2", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.11", + "minimatch": "^9.0.5", + "mlly": "^1.7.1", + "unplugin": "^1.12.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@babel/parser": "^7.15.8", + "@nuxt/kit": "^3.2.2", + "vue": "2 || 3" + }, + "peerDependenciesMeta": { + "@babel/parser": { + "optional": true + }, + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/unplugin-vue-components/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/unplugin-vue-components/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/unplugin-vue-router": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/unplugin-vue-router/-/unplugin-vue-router-0.10.8.tgz", + "integrity": "sha512-xi+eLweYAqolIoTRSmumbi6Yx0z5M0PLvl+NFNVWHJgmE2ByJG1SZbrn+TqyuDtIyln20KKgq8tqmL7aLoiFjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.4", + "@rollup/pluginutils": "^5.1.0", + "@vue-macros/common": "^1.12.2", + "ast-walker-scope": "^0.6.2", + "chokidar": "^3.6.0", + "fast-glob": "^3.3.2", + "json5": "^2.2.3", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.11", + "mlly": "^1.7.1", + "pathe": "^1.1.2", + "scule": "^1.3.0", + "unplugin": "^1.12.2", + "yaml": "^2.5.0" + }, + "peerDependencies": { + "vue-router": "^4.4.0" + }, + "peerDependenciesMeta": { + "vue-router": { + "optional": true + } + } + }, + "node_modules/unplugin-vue-router/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/upath": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", + "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-vuetify": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vite-plugin-vuetify/-/vite-plugin-vuetify-2.0.4.tgz", + "integrity": "sha512-A4cliYUoP/u4AWSRVRvAPKgpgR987Pss7LpFa7s1GvOe8WjgDq92Rt3eVXrvgxGCWvZsPKziVqfHHdCMqeDhfw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@vuetify/loader-shared": "^2.0.3", + "debug": "^4.3.3", + "upath": "^2.0.1" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": ">=5", + "vue": "^3.0.0", + "vuetify": "^3.0.0" + } + }, + "node_modules/vue": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.10.tgz", + "integrity": "sha512-Vy2kmJwHPlouC/tSnIgXVg03SG+9wSqT1xu1Vehc+ChsXsRd7jLkKgMltVEFOzUdBr3uFwBCG+41LJtfAcBRng==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.10", + "@vue/compiler-sfc": "3.5.10", + "@vue/runtime-dom": "3.5.10", + "@vue/server-renderer": "3.5.10", + "@vue/shared": "3.5.10" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vue-router": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.5.tgz", + "integrity": "sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vuetify": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.2.tgz", + "integrity": "sha512-q0WTcRG977+a9Dqhb8TOaPm+Xmvj0oVhnBJhAdHWFSov3HhHTTxlH2nXP/GBTXZuuMHDbBeIWFuUR2/1Fx0PPw==", + "license": "MIT", + "engines": { + "node": "^12.20 || >=14.13" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/johnleider" + }, + "peerDependencies": { + "typescript": ">=4.7", + "vite-plugin-vuetify": ">=1.0.0", + "vue": "^3.3.0", + "webpack-plugin-vuetify": ">=2.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vite-plugin-vuetify": { + "optional": true + }, + "webpack-plugin-vuetify": { + "optional": true + } + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 00000000..3225f51b --- /dev/null +++ b/web/package.json @@ -0,0 +1,34 @@ +{ + "name": "web", + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "lint": "eslint . --fix --ignore-path .gitignore" + }, + "dependencies": { + "@mdi/font": "7.4.47", + "core-js": "^3.37.1", + "roboto-fontface": "*", + "vue": "^3.4.31", + "vuetify": "^3.6.11" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.5", + "eslint": "^8.57.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.4.0", + "eslint-plugin-vue": "^9.27.0", + "sass": "1.77.6", + "unplugin-fonts": "^1.1.1", + "unplugin-vue-components": "^0.27.2", + "unplugin-vue-router": "^0.10.0", + "vite": "^5.3.3", + "vite-plugin-vuetify": "^2.0.3", + "vue-router": "^4.4.0" + } +} diff --git a/web/public/favicon.ico b/web/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8fb9f91b3aab4eec0c76ffc5342528033c61e247 GIT binary patch literal 15406 zcmeHO3v5%@8NNVarCoU?zSqezmT6npu}xxXJJhY(x=s~hQ>SSYYSncU&|(|9Y?QVX z@}TB1G8mASM;tRKYgJkrAW)&g7${B%c>oE7;)O>NNO_bu4G-IdNB({PbTkJL{ZGDJe2DLL+vq#sL?l$jZPe_*I2y@|4sBRjso zUy`aUlJo$60})6B%aMKN&yMG1Qvjth%4dDy{HM^34) z&_TyGJg3UC{J|n2-wr)LDYwhNWBH4VL-JgYz;erTEKiMF20`|$Cf~GysBU40j@&)u zboK>@(yL25%R|RmS~6@99bG>PviV`j>&k~M@~J%cn{`uCzUOyY^23rlWt6DH=aut3 zlZo^g63xIXwP(4)hgT<|I>hSGqh7YbLP$jL+%p0>vM2Su?wmOV;-uyLFww=KN5Ox{j<% zmi}mZc22bZyxbmI!x+Ch3+qsk+#YbHJ{CdQjDh~;LRJB63Pq&~p

*N){PEn7FA;EAwj=|b_AUGw z{Eb1Zt8`N8chL?vjkw~yAvg~N+W_qV|N7U7a3HGfPY0GBqUxOaLJzPO2|Qz7H&yF{ z!Y@7SbxH$-YlpxgCaImE$Fk6T4dUMU!=a+u##*PZb&m9d@{VbA<&v z;n*Hvm#Nt5zF}Ud{=9#v%+3;8${f~WV?Q`CFATe5JXp$wT(q1TOU7#0jK6PDXZ)(1 zOSF4N3hStRA?+K$*ZctH(lRF!KFSL%W20hM6%Rz+k9We?_C36J>PVG2%Y`27X;nW+ z*x`6oe7S`dXABgw#vFYvuM;-c|L9ua=7z9?9B!cL=Fyiz34xz{6kpCd?PeyG2V7mkg!!m@d$btKgDK_Ib zt|Qt#$Am-fZ&{xA0GOAnn8Ue+QQBY3NiO*vfvd{5lsp3L_K5f@hhj{}^Nk#us4?p+ ztOM)g!1L3Ff(Tk8KQe4khoQS4_&0;4zo|FQr(iM%k4L+U*z zff0J2FQWxM*K?>u4d$6_8R>l`cK8U+!w`A!D@A2^2+L%A~GSv8* z(uV!wgkD)YqY(1-;X}j zv4?n}%=LOp! z;8GgCC~I2q%*#5{$kpuKJ6-ET*}o#)Z>p>vQsxF25{?c6CQ5()gsGtn{Ul`*ykb!bDMnnlGUdfYdn+9lsY%+pJ_Z_xHs+imH? z!L`+luUT1yv*4C1Z&<#Qlui*r2~lxBI`sAm+}oX{s=OcRf9A0%Q^56>8DC==kKtgy z>D-TeE@f3uu4&Y?ZVVlM3wLg~Z>Y}xfNE|1MIAijELQN`Y2<45F5=t5>wtYuk>yuH zXEJj-vN;uZD0|537U$Eq5Oek?f#JlNGq{dN(qiDuxO185eWG@Tuk1zKUsCs)MAVm# zZTvIN+Wl0&MDXYQF#3{qXA$(Ft>uw;>&qkE2f#n|`-?1D`gl8Gqj+=j77qvaV7#CH z-mDV$x0TsBRP5B|jZ%mFQ}FDv4Rn5l$yl(|`Q7Oe;~u+P58Qvv*6uy)7U=EIpPM-5 zPv}(pkwc!Zx3$@4;Y){)0d2tO5v#db4**wJ~;d2`ShTX zw|GaKwMoD4ydIzqSN(;l&j_8}$q^eYga7tU+}ZvgxyO*m1UDkuojePFZ zG`-$PpE1`DpyrLMO}R7wCfu3t6Y|B7d%f_n+{8^32;HSdS9&hVGwt*sUYr-!%%`a~|XP)!GJa zaPfvq9vz7{ms4}`U~qpQ+@r?~G$CeSuOWUWWPj8xrxoKsi%ALkMoN~Re@yeI<`b#& zQakqh?^Bfdlv*~90r${a#kP)=j;z5r;Qua|_8~6ciamnnhpFd84r{fjgNVx%{UgRa z5XnEKlf}cZ&elidYgP}Qi}Z0^o$C^y%G~k#_Qp8=1^BDy6=xQN&GlMeiCUK;x&F$^ zjA44c!M?-)Y1kOO--Oix|DE7=F!d?WE|oc-P;ICUYO&YsIA3qS68`^+SoxzLDfxQ- z)V%&7_{XQaWqV^?8s0=b5Yxwfs(L2+kBDu`F4FIF*gxGfML8AK#-01US-npCiqalZ z?M~DOWA<^ZR|~P;79^A!*A-C1sshAC6<~Z9P|a%vs7IcNOJh792S@Vc$sCItcXG)K z1Fn?E#hswKujQD~WT#qpf3`ix0(EL{#By^?PeQ2& + + + + + + + + diff --git a/web/src/assets/logo.png b/web/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a5f23ae7bff64954cf3537377a9f99306baf083d GIT binary patch literal 11955 zcmd6Ni9eKI^#7gJGKPulg;G%xiD=PGd&*jhP(($RY!y;H&uF0{l@?o>HdMBj2_-WU zNeh(_F{45yOO~6)1Q=?HBxq?7z}sB>)eS zX_8$O02XYpTeH$Nn$X+k6gjP_RPeX^z|cl2Yy0C`%k3^z#7&pBl;8Ed>bUL7pLv7R z6W014JxNhW@JjXCeXZg}#GXB2moGgZ_(`#Ou?&nUCg$zmsv3Xoj7bwQKX%vaiyIO? z86DLj?XyWG9Ns%o7xRAn*ge^bgdgipfcE@<^A+f`+mR>sKJ-efbO9(E zUaqO#{4M!gQ>Emm+h|>u3SJs1DO?hNToWIvqpIOS`jT%Uk7n0;wKs3);uG&)iMaiW z42@3$1li}%73SD=>Gs3DkumeZ(hj})J_6P`bPG8mIUj{f#s@0AbH4E`#H@}=-la`W z*u-6V5+J4|1lMME>4E(_7k=r-a@s6UONw$AHLHXEdyf4jQ(1c*F zHX0VCGG13c_-7ZhVu;e1xZ~-O*zV`wu73WG?8he3+^K-fpOZbBGjhjX!#qkpY$}^2 z4DKV~SN+$Aj>^@wcz4v@ZyvOj2#vbRjlahsRjv5BL?s}SnD`cA&Y<(5bo6RrO`Jvx znD!<=I9mzuY3JM~`gZl}NmlSWoZubDU0tboo)y#DPzA~P3Y5;^TRwUm8zx+<*<Lup&B5dj3P;M2pm*yn?)%cf z*jta-^OmWv_5}lD61#!eu~1K9rlYlQ_q%3GH}RsGO}p3iEeCx6>e|ZecI;X;6&>VB zykbrS%t6FrbVSFgrVUEtVt>TR8qf{Wd+$-?>Wp`X>I{2#LbG~P}J-H+N z;0nz?@he1Q{Kuw>g(h-=q2ku)A9~n*3%N=Fl1p2%Eb~4P_~C3;&>lBbxzzgTs1Qex zq`Z%A?^Y*;;_JE0)|41Jb$vXMP`>b?bobG_f0sLcC$Yvg?K;_DdRPzkyYjke4!FGRLEZC_p=-#}d@k>7aa6%OS8+zy zn>xzCrbsR?JYNZPdK;S}_0F_4-_Hpq+9F+aEHx0D$onB;5~uwypWanbFKNY&1vgk7*t?{kcTWNt+PUAcde2^Cz<5|&5#cei;2B5J8-OEgrc*Y@tsfPlItbJ5t(>);7UJuPnU*TcLc zQhNR*4kP1F_8ooa9{)1c+iuHODte#&A!)wd#qtBU9}zft6b~Xhw{J|eqI|S&eQykY zuo27dSOk{VAL^X#KWItbVl4{@?n3A``TNL9G`s**Qn-Gsj6<$?0O znVV=8JT+>=ruvd#ynUn7@{V^v7|342<^TLUP?+4(BB(YT9q_*Vwc^l##+^;FSJ8nomv2*=A+c%V>FpWlpU!Ky zoELOoWUZhJERCmp-=X*F8Nnyo(}2xSD0w7(0>;~pBYp=(obXy#QCE5-?M`NonC^pUh@_-k%~bVtg9 z3j~&0g!o2sD1Pp$Y>)B>b@$GZZ`5ag-^TOM{uQKzU4EJ?b$G!*565g3>vs17;nbiG ztCi=y+WP%0+_-b=#5tm3n+TA5Ayl>&6;R0%78n@!J!2l^9airdia!&C*#0Fi0S(nP zPyV~a@!~^@{OjOyChGa0#>@zRF6YnN{< z9_xlao2Coq&l$2Ev)%}|Im`)lJTh9CMZ@TuU9=^S8YN!KkBH4K5co6mwjR8de-DN# zXN4=4-Vuj*2(YA@qkpv>z}u?HNIg6A9u@ez(`GSpU+R~36du~s>$Gp6?it*`BUJvh z$7FonU0ndvRiA=0e>SGloZI+b>A>|Q7bmncss8+!y^v4)jxz7+wCgVLf{^bGDqxSnc@K`}*9>L_Z-Snf z>$T_#mub?pR@Va9oc-7$uo)qtt4-@w8ZaH7+l33q+%|6(FJlux&KpsG{_rfYQVHy* znb#cDJEyAHj_LYzvU)=h-Q=&8$X3gn)PyZ1Em`E52?Tz}b#(t9}&AH4cz-A=^iO!jXZV4tFUS(QjRWMrDio84Hv^_8gY5XJ4xIX^` zls`flCbfjPOLM=d?h!D5F*T#J9%dCG6?8XdobvFYeNDr?Mm-nZ z;qqvsQ44bOrG%U}E6VQ=J>flqA!XA*%y^ZCk3by57zwp?82&19qi6%AldfW2%|C!; zTC|-MJ1o^7371{LeDK7GuuusYrybh>qoYC@I9|LWOPX|>=#68Aj`!-sbq5z~ppQ;y zNp#hR|4SMiZT-ma-uY9TumZW5R{d_UrmkgqjZl!wkpuqGPQVk@IqD(_mJFJ#+>tn} z-h?%@iQE*jfbZ2ibLbXh*7L?-WcWyPvCo@eR+#Y_L#Hel)0}AKsY!sa!~>Gr1OvOD zEM9YG@!0xam0h)~l(D2Uz91~(jRr@#VAN@>P;bGQ^@g>b=^s4Qw!tmaMgux660%q6 zO|oy5(^uo{r*pywoQ_&HVTlRtt=K&S5@YTsd`MjQXXC7!+F)9xErAx^hC6M*o8nsZ zg<16wb2oKBE@eUWLEI(9YyX*5dj`y2w&d0vBn~sjPJdBL zlE-G)f9Z%wh538VQv&}pG15eZyePZ}p`wQa@})aGkg10TR6ay-dB?5P+PXV_!d>)t zd$*X&|KcpYfBLe+^9rW!p@Gn;rfBM#EpUT6fzX+j2^r|@B-oyB|6@E>Jnw?gf)hQ< zcmIalH>ZPB#S~TfyaYP`+tjR4-o^oyT^7=2nSs+m<_Qm<*?k>#P#Cm+@(@32JTi;r zlgE{N@ENOKYYQ%olAUSG)q3zMTWcs_*ZoKb?1fcSo33KS=(mjUwv9V1R*^?YL%XQ>Z;h?QjUu0TU#g%59b^#Z z7Dj}*b!t$x%I9{Ge~1%Wul3~|S7d#T;A3p}g=qg`6d$KJi%D@Q?v*RURx5$kowkGq zqSIJhKOPB#vPKadD!OG>_wG$I+lWP2)`bh0nP742O=sV?g=BfBE9nO|nX8ldb89^~ zNsAa8K-l6ySuTxdxrmqPlK9&DcUVcaNbF#+*Jn=(1g&}?qHYdc&$pJWnnecpBP)1y zSJ!r@|CM3J=ClSHxcFVD;`^7tnJv>sziy??QIU%~*YiB$BA{k+lH7g<70OEB+)m-x{Yq5g|Cy@gBROfAc27MDdtKPx4EWt{XHs7oj_ zVFg4yvm202Ybyn@)5v|=0=zDJxC~dV;61uPs5frq6@OH7tp?_#Vv5#}P6hVL?+nf& z_a)Ar|5XA+@~I)r{}nL5Ro|~*0Nh`%uIiTZj9Ag4T zpL@oM31x@A0^@pOi$EfqYb25+N$V+LPrFaQ2S1BMQ4Y>K(iNxH^v0J->yimRgyMF~ ziqAy3ZRQGGUdiola{Db&u>lpOoZLqHp~kmc0Jq)DA{dmd?+p!C0yM>ZjScbxvu-^T zHL1NlM@I#T!CS(yMeeG>USmB881jn*(t9$sMm^>ba|Kh!4*y`O7=z$>25J;8fm^X>l%dd+rB)Aufz zcnt-Gl^@7WyHC-%5aJSWp7z6dEN7&bH}*WryGUyus%IgUJ~Nqz6ig%%I0|+#o}8yF z;S3VrMW$YoTf7~bL3F&(dyy8QVXn&)BiRLu(D=kdcmQAZxeX#bHX44+u!OPwCnwpKpAVD~j4Dol?OhpKkGf1fM$m z=>A^MEvDwbBSBv9Z#uRe8vHPmB%}o*bUzS~+U4Oh-)o?MXwNk+Z`VNRR+JBkQ(mVE z)E9Mxu-oN`ek(})&)Ab=yBoNRYgZ%k*ym&=RL-WKM^^%-8$J%2zUz^*&+`XR44)p) z=NwZ4*KUcLl&Pr3B!3QnJh$CF=cyCeru=`zIdY3oKi5sH_V^TmvsNaP`LZ(k z(Go6`ybpTw8NBhz?WM`-bUSTH?D}6B>lWDh{fOgCx#9$_*}eQa_3;@3XAdM$#X$Cq zNLTp!`XUbU9Bqb65+@Hl}(+GBcNFmO#v>)Pld z*lLMG-hEeN_1Hg{(%H+?RRYsx>Q~h;Hcn&=(^v^FlHKCHtVE7;@XM>Hs|WQervoag zOs9U;Xx7k_K-LyFemc>LwXC9Yw!&9KXU_;^_x`WK2$0i8HPTjadHWZF1Hm%Q+idW; zTiU%;mlk+|5J0cs^}&g}JW7NU>(&_p%YPu$uX`z%r}1|nG>N;Uf~jH-q79a~wXdzy z;3&Yh*^$=Zv+J(3VjgQjk?Hop&2fmH4;Wx=r#&VUGdQ9$BoL9mM8p1y`sJv=>Fd+L z2{JKg@189oGKlurP)pRcE0_p;=&a%szP~q5R+Ot6bpnwc5PDtXL03n8%ojk224^3 z8wM7P4W?lWOLNP%Jyyb8#68M6V6jng1{s&l-k8)9?>FGbv$U>7&1Xn@G_pYY#pw$o z+j+@aVEK&f`j>V^EIFqxRBTw3kIf+lHaZj@2yLi zJ=S#R&YqQqxzd5Q?aCCY@rxL<0LAzc#Mea4jKRAMBY9}9`6F+G{w28rP^SA@1? z;m|_2Mxy70?yo&}JnOsHh4vP@KlM^OkWAog%Wn1>6!kNyfU^JjzFdl<-=sLdDe9&x z6Ygz&f`r~N8qg!5sv$77Zx%hfvAo~A=YNE`P;M2V&$%T7SlDhHH&M@ zW?tDwK5KGna7Q3=*c=#Ny!~!J!E!nkwgcK1FLl!VUWV`nMBW*$O2yK-F94_JoynX> zF_9*r4&6W|#mb&XCbeYw+mLhTG+>W+0q4EZPAo$%%gUOl^D8J{dPjlF6u2)22VsEx zsstSS6fVm&3qW@L8iL67Y!pA~BK~xmz?$Jk3xYOqOMY(^zKl>`m znLV1sd*09ty7LjAIIX3Z5&yacDQt@R?}9 zhJ8W({JEjDJS)b}9)@!xc8Ln7W6mk@zsh63ssX3DNW;A+gk8Rk@QyQ{!pInSzHC|l zX7P}gI=^&#hMHu{IRdK`f+{7w?9capc{j26?iclws(#024`}w_F?;UXDPVJ70Zt|) z)+dsw&Sx7c9|35<+$(gx7Zf?gh*S#@2OzsXEAZe%X#y8R zJi3$y&0gWH@vd767q(X$?A`lGl5HTb1lU^b8YN){^*MC(zBEKyV;S)$Jf*f6i9?o# zQTd7X`0Hy!v2h0GfIQ3YP4WAada32X0IKu6*<(3Wb$*(&m_{S9ShcZ4jXcW4LxPr8 z=?ukEVb+uwHzBhTfAqR{P6e+B-rm*d+4&$dZ9Zpc2J2T0K06LM45ifCNnFV*l2|Bp z+&8}Kz+tWPmq}bMFKK;W2XCG;ThZrW=BWN0DhlW%_A`r|&i&RvMLla2zs^!wL2%{5 zTy76nvGL=0G1zgGX#B6%B%Kx#53A2eVTPWOGECYE(2~R`<*2A#N?i`B^D@VaEqX$LaA6 zoU1gD?SNC=U?$$J1!it;B!+NV92#Nq7qYXiAFl&MgURP2`xqpBfL3!x@dSd4~g32$(Rn8nWi{OZfGML2O%CZrK}0lv?fDxR+GfK zeGk&hUGc-mUamV$JY>Oil1=5Gl_^#;*vXrs+em!04=Hb4Mwl$-(tbG-Lm3j9Tqu6) z;Vip}xPXOV&0DDc#(xzLJm0)UnHALA|8WB96hem>x6YM9???EzsX1>_udmH+7iP^K z(^S;~uVfP-r-`U(JEceodwy$p?}J-H!94@5U~mq$*VA6DgbFOIjFPG)?`0{UJ5q)Z z*6YV19lHR-O&lB1UG{#ta$preH(T7%C=p)Z$HLU@d(1?hMn_e-%xQjrb+^pOr&i@u z43o6n8YaQCQJgja}^tp6|16%3>go-B6^0%zf^(BR}KN?@Lff)Y_8P=2FLA zGvx=@KtPD&fXZcaz`6L{LrC)khEnhHkSA*m--I9!io&OZOLykX9*f_o1)S}mJQ?({ zl>4Vo!cYHH0-BQVz4}`h0?tEEHwV1)K(rHj*@0WZ3`D|DcPpWZLjAoJ08J|!w0;=# zeuHTI;ZLtcda`65o{)=Yz438CwY3O)hrO{61?9MG%ZA(;^w_@_o0|mZ2huQ1yh#b9 z!j|z{`|p*!HN9plE)3&$rMvW$K*B0*7_pkyjOU>;O&%X(4h)pZ__wLYDCWz&y2_BZ z@dQu-n}i8NnBUZjR-UX!m_{PNnhWK~XgSuKaZSDv>iQxE6`V+F@h&6k1DU*V2P6=; zwGorY)|&jGqfks#-qZq&`SAEDE}EhP9yjVicAE}|W=)zkcUoJ0g5&06ZpOpf8V(daD~aR4M|GO#>=P zyGY>kxppkX2^i(}<1$`~@kCD*5zze&nEA}x{Wuw~x1@sHk$=g$GS|luyzQOCWm+Z? zK;Z0Z$wt&=*AZgP#aX{-_2w49`E-lEW0(r=iYHqJA^|iKXn?V07pye`ArU~&0-QQ{ zHnWP74U1cf{cR3g$`e zbiN*!c>snI2A}Y$Yp-1Ss5o{$U;9sI(;McnxfC8zHH;P zAP^aVzAGF2>~>L|x0{$*^<0|My+#2JE6c8|=Y<F3!=}Mg7=)=r1Njk24>ky0L-MncEPBLp5x7(R(tWFQbOg6n@nAv*Z#E1Iuci!)_qz~o@> zuPx#J+5W`DL7d9}6P5;1Z4Ivtfko1qlc6P{_;m!2npFJVCeaWy?ItAlx%+p;kRA5fPzPh|o3n|E4@}J7%DFLnJrS3=BXpVO4kQIOHD&5e8UyAbZ=}cnR1ZU_{P{h8JCbCOZsU@Z{4c97&3+<>Wm?A&yy1ogY(^#av>Hu``bT`_M z>wtMM1%sVTn}xt&qf=I`h9|%e3`n6fZbD}m(?e;ydwVjjSc3&)uUeVDYv~M^XJrqb z8A5gQ)NQ$B=sxq325js#Kwm^mQ~q(bF&XCuqYM}-Yx3bS^CsyVQZE%aAb4Nx6EMXO z0M$N)pI&cX;i?i6{M1z-TQ6XwcQWb#6>xsQ^C`15j+$vB{;;yM)0Dsmzy@x)nqrUR zrXIviyv%*kkv;-cE@LfNVQ6rq6HD&OmDZ)0HLNm)usy=321O3NNAT`4#Ag}__MX2i3S{T+yMg)V+j1aRLDERnZdc=La3~>p_#_LGjKVd6 zc~$2~+p8+G`*GDj1iYDpEo=8Nx}~HoMFfz&@Q_1&UUn0H`j-NFS_`0aVSSWKS)9`` zk){6Sj!4e_6#$vAPT!J_VOeK7@MXfO(3rVjZ3$I5K=lVb;0XqEm<=SuFlvM3o)rOZtZPiKR@Fhji!i1rkcD&FeEh@I?V{poWE8nDELmbv3tr|E^mi$DX0E_pxz zUF%H4_F9F4;B>)M5V;FXwr=z99NKm{b}Xm+_KN?Kp1CF!_z;{_I#;myfrCxuCI@ed ziio!4?E(#-EqHu+e*zQ)iI#KCu_L2YXtmB_qn&U2gw{& zX*f;*xr9-2WeNE`s54Js ziP6cN47vLGfhddi-|nj_{HGz8xQIy}@Dx5^t(PZwRx^scxu(d{h5M;d;=xh>az(4; zGyd_++gGv^?U#RfYV`|Ybj_}AmRm?BYL-gkh5Ge+vXKIffQtj6w9Hs(R;xoC!itqW zqSV9Z@1{97B6{%g-^iDa!NDsafDR442@gXDI#@#m_>SIwpLbzN7WFfsadNBjDP3;u z>D?3F5Ug^|09=y~qn*zlS}r-OzpCpL2$&9UW)D7L1s#9dxxf5AvZP=Cry?A?0FyEE zwudl}gidC>`svUn!vzd2SJSX+zE5%_2Rtk~|YI1eZUzyOc-F?T*fx-J$mD<_!h zw~l=$oPcH706#_WwfWJ_MU2$C10q5W^vD=!R&)ho&wJ|vA51nv*;W`(CqHhzBN`?AV zz+V&?$iCb(nFR+_NI*FISy!w~ylxlh5yPzq_I9@fat{0tA__MXW^ zOgi8Lz@;!Q|Jzmjr3b1fnExVj9>n{`5)YsK%gT*}mquXnBRz#fXa3+5)rZ_h$<1zN znNKhQK$deIX5-Hg=C8W2b|yzl z7qAxjHQnCz=>^h&fi}3fD#*(^1(X)Mcw7s%VB~&Q1CZM!)ZmX`M$@nq_fNg-H9!Ln z8h7_81l-dIX#0X2!BfyxF!PW(cRhqBMj$K<7!X>(%hdT}x|4in(Ld+2&7DbP;=hzE zXlS}k6@8oippdt4!VCr{5tg^|2@6NsG@^Z_f)R%k05%T*`SueJ;m3%-uWD;}J`>_7 zwqISe^rGj}bC9rFOwUpt^857EKUa-(^TT}M9*c<;A{4uWk6KpY;v8~pA$C9Dl; z0yYsi$Nz%X_E{!ik#^umnao2C}_hPwKFnOI=a2FQ6@Dnt0P_(ng zpppEo`IKWvhoHbPfK6Z)88+d^W-sd%_$4djuO~|t)&RUr9s=W7%XVzPl=m1m@X4&H zaBzbR8luOs#KAZC#=M%ol#x5N?3LgtWILC%&9vosh+S2l0vZ5uFeRMJGVx>c2Y(7e zp(AI)LTXLdmq(gG5JOE+AF<&1AzGu + + + + + diff --git a/web/src/main.js b/web/src/main.js new file mode 100644 index 00000000..d31eee22 --- /dev/null +++ b/web/src/main.js @@ -0,0 +1,20 @@ +/** + * main.js + * + * Bootstraps Vuetify and other plugins then mounts the App` + */ + +// Plugins +import { registerPlugins } from '@/plugins' + +// Components +import App from './App.vue' + +// Composables +import { createApp } from 'vue' + +const app = createApp(App) + +registerPlugins(app) + +app.mount('#app') diff --git a/web/src/pages/index.vue b/web/src/pages/index.vue new file mode 100644 index 00000000..6488c51b --- /dev/null +++ b/web/src/pages/index.vue @@ -0,0 +1,6 @@ + + + diff --git a/web/src/plugins/index.js b/web/src/plugins/index.js new file mode 100644 index 00000000..9eb2eb24 --- /dev/null +++ b/web/src/plugins/index.js @@ -0,0 +1,15 @@ +/** + * plugins/index.js + * + * Automatically included in `./src/main.js` + */ + +// Plugins +import vuetify from './vuetify' +import router from '@/router' + +export function registerPlugins (app) { + app + .use(vuetify) + .use(router) +} diff --git a/web/src/plugins/vuetify.js b/web/src/plugins/vuetify.js new file mode 100644 index 00000000..1db02919 --- /dev/null +++ b/web/src/plugins/vuetify.js @@ -0,0 +1,19 @@ +/** + * plugins/vuetify.js + * + * Framework documentation: https://vuetifyjs.com` + */ + +// Styles +import '@mdi/font/css/materialdesignicons.css' +import 'vuetify/styles' + +// Composables +import { createVuetify } from 'vuetify' + +// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides +export default createVuetify({ + theme: { + defaultTheme: 'light', + }, +}) diff --git a/web/src/router/index.js b/web/src/router/index.js new file mode 100644 index 00000000..cffec2d7 --- /dev/null +++ b/web/src/router/index.js @@ -0,0 +1,36 @@ + +/** + * router/index.ts + * + * Automatic routes for `./src/pages/*.vue` + */ + +// Composables +import { createRouter, createWebHistory } from 'vue-router/auto' +import { routes } from 'vue-router/auto-routes' + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes, +}) + +// Workaround for https://github.com/vitejs/vite/issues/11804 +router.onError((err, to) => { + if (err?.message?.includes?.('Failed to fetch dynamically imported module')) { + if (!localStorage.getItem('vuetify:dynamic-reload')) { + console.log('Reloading page to fix dynamic import error') + localStorage.setItem('vuetify:dynamic-reload', 'true') + location.assign(to.fullPath) + } else { + console.error('Dynamic import error, reloading page did not fix it', err) + } + } else { + console.error(err) + } +}) + +router.isReady().then(() => { + localStorage.removeItem('vuetify:dynamic-reload') +}) + +export default router diff --git a/web/src/styles/settings.scss b/web/src/styles/settings.scss new file mode 100644 index 00000000..3e36a279 --- /dev/null +++ b/web/src/styles/settings.scss @@ -0,0 +1,10 @@ +/** + * src/styles/settings.scss + * + * Configures SASS variables and Vuetify overwrites + */ + +// https://vuetifyjs.com/features/sass-variables/` +// @use 'vuetify/settings' with ( +// $color-pack: false +// ); diff --git a/web/vite.config.mjs b/web/vite.config.mjs new file mode 100644 index 00000000..1e6733cd --- /dev/null +++ b/web/vite.config.mjs @@ -0,0 +1,54 @@ +// Plugins +import Components from 'unplugin-vue-components/vite' +import Vue from '@vitejs/plugin-vue' +import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' +import ViteFonts from 'unplugin-fonts/vite' +import VueRouter from 'unplugin-vue-router/vite' + +// Utilities +import { defineConfig } from 'vite' +import { fileURLToPath, URL } from 'node:url' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + VueRouter(), + Vue({ + template: { transformAssetUrls } + }), + // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme + Vuetify({ + autoImport: true, + styles: { + configFile: 'src/styles/settings.scss', + }, + }), + Components(), + ViteFonts({ + google: { + families: [{ + name: 'Roboto', + styles: 'wght@100;300;400;500;700;900', + }], + }, + }), + ], + define: { 'process.env': {} }, + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + }, + extensions: [ + '.js', + '.json', + '.jsx', + '.mjs', + '.ts', + '.tsx', + '.vue', + ], + }, + server: { + port: 3000, + }, +}) From 7c3557e94355fc1dfd02f35d2d332e82764e3455 Mon Sep 17 00:00:00 2001 From: RockChinQ <1010553892@qq.com> Date: Fri, 11 Oct 2024 22:27:53 +0800 Subject: [PATCH 07/71] =?UTF-8?q?feat:=20=E6=8C=81=E4=B9=85=E5=8C=96?= =?UTF-8?q?=E5=92=8C=20web=20=E6=8E=A5=E5=8F=A3=E5=9F=BA=E7=A1=80=E6=9E=B6?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 7 +- main.py | 13 +-- pkg/api/__init__.py | 0 pkg/api/http/__init__.py | 0 pkg/api/http/controller/__init__.py | 0 pkg/api/http/controller/group.py | 91 +++++++++++++++++++++ pkg/api/http/controller/groups/__init__.py | 0 pkg/api/http/controller/groups/log.py | 21 +++++ pkg/api/http/controller/main.py | 48 +++++++++++ pkg/api/http/service/__init__.py | 0 pkg/core/app.py | 30 +++++-- pkg/core/boot.py | 9 +- pkg/core/bootutils/deps.py | 3 + pkg/core/bootutils/log.py | 5 +- pkg/core/migrations/m013_http_api_config.py | 30 +++++++ pkg/core/stages/build_app.py | 14 ++++ pkg/core/stages/migrate.py | 2 +- pkg/core/stages/setup_logger.py | 36 +++++++- pkg/persistence/__init__.py | 0 pkg/persistence/database.py | 40 +++++++++ pkg/persistence/databases/__init__.py | 0 pkg/persistence/databases/sqlite.py | 13 +++ pkg/persistence/mgr.py | 55 +++++++++++++ pkg/utils/logcache.py | 49 +++++++++++ requirements.txt | 5 +- templates/system.json | 13 ++- 26 files changed, 462 insertions(+), 22 deletions(-) create mode 100644 pkg/api/__init__.py create mode 100644 pkg/api/http/__init__.py create mode 100644 pkg/api/http/controller/__init__.py create mode 100644 pkg/api/http/controller/group.py create mode 100644 pkg/api/http/controller/groups/__init__.py create mode 100644 pkg/api/http/controller/groups/log.py create mode 100644 pkg/api/http/controller/main.py create mode 100644 pkg/api/http/service/__init__.py create mode 100644 pkg/core/migrations/m013_http_api_config.py create mode 100644 pkg/persistence/__init__.py create mode 100644 pkg/persistence/database.py create mode 100644 pkg/persistence/databases/__init__.py create mode 100644 pkg/persistence/databases/sqlite.py create mode 100644 pkg/persistence/mgr.py create mode 100644 pkg/utils/logcache.py diff --git a/.gitignore b/.gitignore index 56282ca8..65ef5e47 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,8 @@ __pycache__/ database.db qchatgpt.log /banlist.py -plugins/ -!plugins/__init__.py +/plugins/ +!/plugins/__init__.py /revcfg.py prompts/ logs/ @@ -34,4 +34,5 @@ bard.json res/instance_id.json .DS_Store /data -botpy.log* \ No newline at end of file +botpy.log* +/poc \ No newline at end of file diff --git a/main.py b/main.py index 0ad87bd2..273c8579 100644 --- a/main.py +++ b/main.py @@ -13,7 +13,10 @@ """ -async def main_entry(): +import asyncio + + +async def main_entry(loop: asyncio.AbstractEventLoop): print(asciiart) import sys @@ -46,7 +49,7 @@ async def main_entry(): sys.exit(0) from pkg.core import boot - await boot.main() + await boot.main(loop) if __name__ == '__main__': @@ -65,8 +68,8 @@ async def main_entry(): if invalid_pwd: print("请在QChatGPT项目根目录下以命令形式运行此程序。") input("按任意键退出...") - exit(0) + exit(1) - import asyncio + loop = asyncio.new_event_loop() - asyncio.run(main_entry()) + loop.run_until_complete(main_entry(loop)) diff --git a/pkg/api/__init__.py b/pkg/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/api/http/__init__.py b/pkg/api/http/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/api/http/controller/__init__.py b/pkg/api/http/controller/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/api/http/controller/group.py b/pkg/api/http/controller/group.py new file mode 100644 index 00000000..b9533567 --- /dev/null +++ b/pkg/api/http/controller/group.py @@ -0,0 +1,91 @@ +from __future__ import annotations + +import abc +import typing +import quart +from quart.typing import RouteCallable + +from ....core import app + + +preregistered_groups: list[type[RouterGroup]] = [] +"""RouterGroup 的预注册列表""" + +def group_class(name: str, path: str) -> None: + """注册一个 RouterGroup""" + + def decorator(cls: typing.Type[RouterGroup]) -> typing.Type[RouterGroup]: + cls.name = name + cls.path = path + preregistered_groups.append(cls) + return cls + + return decorator + + +class RouterGroup(abc.ABC): + + name: str + + path: str + + ap: app.Application + + quart_app: quart.Quart + + def __init__(self, ap: app.Application, quart_app: quart.Quart) -> None: + self.ap = ap + self.quart_app = quart_app + + @abc.abstractmethod + async def initialize(self) -> None: + pass + + def route(self, rule: str, **options: typing.Any) -> typing.Callable[[RouteCallable], RouteCallable]: # decorator + """注册一个路由""" + def decorator(f: RouteCallable) -> RouteCallable: + nonlocal rule + rule = self.path + rule + + async def handler_error(*args, **kwargs): + try: + return await f(*args, **kwargs) + except Exception as e: # 自动 500 + return self.http_status(500, -2, str(e)) + + new_f = handler_error + new_f.__name__ = f.__name__ + new_f.__doc__ = f.__doc__ + + self.quart_app.route(rule, **options)(new_f) + return f + + return decorator + + def _cors(self, response: quart.Response) -> quart.Response: + # Quart-Cors 似乎很久没维护了,所以自己写 + response.headers['Access-Control-Allow-Origin'] = '*' + response.headers['Access-Control-Allow-Headers'] = '*' + response.headers['Access-Control-Allow-Methods'] = '*' + response.headers['Access-Control-Allow-Credentials'] = 'true' + return response + + def success(self, data: typing.Any = None) -> quart.Response: + """返回一个 200 响应""" + return self._cors(quart.jsonify({ + 'code': 0, + 'msg': 'ok', + 'data': data, + })) + + def fail(self, code: int, msg: str) -> quart.Response: + """返回一个异常响应""" + + return self._cors(quart.jsonify({ + 'code': code, + 'msg': msg, + })) + + def http_status(self, status: int, code: int, msg: str) -> quart.Response: + """返回一个指定状态码的响应""" + return self.fail(code, msg), status diff --git a/pkg/api/http/controller/groups/__init__.py b/pkg/api/http/controller/groups/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/api/http/controller/groups/log.py b/pkg/api/http/controller/groups/log.py new file mode 100644 index 00000000..fe6de631 --- /dev/null +++ b/pkg/api/http/controller/groups/log.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +import traceback + +import quart + +from .....core import app +from .. import group + + +@group.group_class('log', '/api/v1/log') +class LogRouterGroup(group.RouterGroup): + + async def initialize(self) -> None: + @self.route('', methods=['GET']) + async def _() -> str: + return self.success( + data={ + "logs": self.ap.log_cache.get_all_logs() + } + ) diff --git a/pkg/api/http/controller/main.py b/pkg/api/http/controller/main.py new file mode 100644 index 00000000..83e86696 --- /dev/null +++ b/pkg/api/http/controller/main.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +import asyncio + +import quart + +from ....core import app +from .groups import log +from . import group + + +class HTTPController: + + ap: app.Application + + quart_app: quart.Quart + + def __init__(self, ap: app.Application) -> None: + self.ap = ap + self.quart_app = quart.Quart(__name__) + + async def initialize(self) -> None: + await self.register_routes() + + async def run(self) -> None: + if self.ap.system_cfg.data['http-api']['enable']: + async def shutdown_trigger_placeholder(): + while True: + await asyncio.sleep(1) + + asyncio.create_task(self.quart_app.run_task( + host=self.ap.system_cfg.data['http-api']['host'], + port=self.ap.system_cfg.data['http-api']['port'], + shutdown_trigger=shutdown_trigger_placeholder + )) + + async def register_routes(self) -> None: + + @self.quart_app.route('/healthz') + async def healthz(): + return { + "code": 0, + "msg": "ok" + } + + for g in group.preregistered_groups: + ginst = g(self.ap, self.quart_app) + await ginst.initialize() diff --git a/pkg/api/http/service/__init__.py b/pkg/api/http/service/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/core/app.py b/pkg/core/app.py index e6e25ea0..2f0e0340 100644 --- a/pkg/core/app.py +++ b/pkg/core/app.py @@ -17,11 +17,18 @@ from ..pipeline import pool from ..pipeline import controller, stagemgr from ..utils import version as version_mgr, proxy as proxy_mgr, announce as announce_mgr +from ..persistence import mgr as persistencemgr +from ..api.http.controller import main as http_controller +from ..utils import logcache class Application: """运行时应用对象和上下文""" + event_loop: asyncio.AbstractEventLoop = None + + asyncio_tasks: list[asyncio.Task] = [] + platform_mgr: im_mgr.PlatformManager = None cmd_mgr: cmdmgr.CommandManager = None @@ -78,6 +85,12 @@ class Application: logger: logging.Logger = None + persistence_mgr: persistencemgr.PersistenceManager = None + + http_ctrl: http_controller.HTTPController = None + + log_cache: logcache.LogCache = None + def __init__(self): pass @@ -91,13 +104,21 @@ async def run(self): try: + # 后续可能会允许动态重启其他任务 + # 故为了防止程序在非 Ctrl-C 情况下退出,这里创建一个不会结束的协程 + async def never_ending(): + while True: + await asyncio.sleep(1) + tasks = [ - asyncio.create_task(self.platform_mgr.run()), - asyncio.create_task(self.ctrl.run()) + asyncio.create_task(self.platform_mgr.run()), # 消息平台 + asyncio.create_task(self.ctrl.run()), # 消息处理循环 + asyncio.create_task(self.http_ctrl.run()), # http 接口服务 + asyncio.create_task(never_ending()) ] + self.asyncio_tasks.extend(tasks) - # 挂信号处理 - + # 挂系统信号处理 import signal def signal_handler(sig, frame): @@ -109,7 +130,6 @@ def signal_handler(sig, frame): signal.signal(signal.SIGINT, signal_handler) await asyncio.gather(*tasks, return_exceptions=True) - except asyncio.CancelledError: pass except Exception as e: diff --git a/pkg/core/boot.py b/pkg/core/boot.py index 5663c2ef..1e5f1052 100644 --- a/pkg/core/boot.py +++ b/pkg/core/boot.py @@ -1,6 +1,7 @@ from __future__ import print_function import traceback +import asyncio from . import app from ..audit import identifier @@ -19,13 +20,15 @@ ] -async def make_app() -> app.Application: +async def make_app(loop: asyncio.AbstractEventLoop) -> app.Application: # 生成标识符 identifier.init() ap = app.Application() + ap.event_loop = loop + # 执行启动阶段 for stage_name in stage_order: stage_cls = stage.preregistered_stages[stage_name] @@ -38,9 +41,9 @@ async def make_app() -> app.Application: return ap -async def main(): +async def main(loop: asyncio.AbstractEventLoop): try: - app_inst = await make_app() + app_inst = await make_app(loop) await app_inst.run() except Exception as e: traceback.print_exc() diff --git a/pkg/core/bootutils/deps.py b/pkg/core/bootutils/deps.py index c392f800..8c0127e5 100644 --- a/pkg/core/bootutils/deps.py +++ b/pkg/core/bootutils/deps.py @@ -15,6 +15,9 @@ "psutil": "psutil", "async_lru": "async-lru", "ollama": "ollama", + "quart": "quart", + "sqlalchemy": "sqlalchemy[asyncio]", + "aiosqlite": "aiosqlite", } diff --git a/pkg/core/bootutils/log.py b/pkg/core/bootutils/log.py index d7b6da0a..ad8252df 100644 --- a/pkg/core/bootutils/log.py +++ b/pkg/core/bootutils/log.py @@ -15,7 +15,7 @@ } -async def init_logging() -> logging.Logger: +async def init_logging(extra_handlers: list[logging.Handler] = None) -> logging.Logger: # 删除所有现有的logger for handler in logging.root.handlers[:]: logging.root.removeHandler(handler) @@ -41,7 +41,8 @@ async def init_logging() -> logging.Logger: stream_handler = logging.StreamHandler(sys.stdout) - log_handlers: logging.Handler = [stream_handler, logging.FileHandler(log_file_name)] + log_handlers: list[logging.Handler] = [stream_handler, logging.FileHandler(log_file_name)] + log_handlers += extra_handlers if extra_handlers is not None else [] for handler in log_handlers: handler.setLevel(level) diff --git a/pkg/core/migrations/m013_http_api_config.py b/pkg/core/migrations/m013_http_api_config.py new file mode 100644 index 00000000..8d7c453d --- /dev/null +++ b/pkg/core/migrations/m013_http_api_config.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from .. import migration + + +@migration.migration_class("http-api-config", 13) +class HttpApiConfigMigration(migration.Migration): + """迁移""" + + async def need_migrate(self) -> bool: + """判断当前环境是否需要运行此迁移""" + return 'http-api' not in self.ap.system_cfg.data or "persistence" not in self.ap.system_cfg.data + + async def run(self): + """执行迁移""" + + self.ap.system_cfg.data['http-api'] = { + "enable": True, + "host": "0.0.0.0", + "port": 5300 + } + + self.ap.system_cfg.data['persistence'] = { + "sqlite": { + "path": "data/persistence.db" + }, + "use": "sqlite" + } + + await self.ap.system_cfg.dump_config() diff --git a/pkg/core/stages/build_app.py b/pkg/core/stages/build_app.py index c0e731ce..8c63f7dc 100644 --- a/pkg/core/stages/build_app.py +++ b/pkg/core/stages/build_app.py @@ -15,6 +15,10 @@ from ...provider.tools import toolmgr as llm_tool_mgr from ...provider import runnermgr from ...platform import manager as im_mgr +from ...persistence import mgr as persistencemgr +from ...api.http.controller import main as http_controller +from ...utils import logcache + @stage.stage_class("BuildAppStage") class BuildAppStage(stage.BootingStage): @@ -58,6 +62,13 @@ async def run(self, ap: app.Application): ap.query_pool = pool.QueryPool() + log_cache = logcache.LogCache() + ap.log_cache = log_cache + + persistence_mgr_inst = persistencemgr.PersistenceManager(ap) + await persistence_mgr_inst.initialize() + ap.persistence_mgr = persistence_mgr_inst + plugin_mgr_inst = plugin_mgr.PluginManager(ap) await plugin_mgr_inst.initialize() ap.plugin_mgr = plugin_mgr_inst @@ -95,6 +106,9 @@ async def run(self, ap: app.Application): await stage_mgr.initialize() ap.stage_mgr = stage_mgr + http_ctrl = http_controller.HTTPController(ap) + await http_ctrl.initialize() + ap.http_ctrl = http_ctrl ctrl = controller.Controller(ap) ap.ctrl = ctrl diff --git a/pkg/core/stages/migrate.py b/pkg/core/stages/migrate.py index 92735c90..a4e57e78 100644 --- a/pkg/core/stages/migrate.py +++ b/pkg/core/stages/migrate.py @@ -6,7 +6,7 @@ from .. import migration from ..migrations import m001_sensitive_word_migration, m002_openai_config_migration, m003_anthropic_requester_cfg_completion, m004_moonshot_cfg_completion from ..migrations import m005_deepseek_cfg_completion, m006_vision_config, m007_qcg_center_url, m008_ad_fixwin_config_migrate, m009_msg_truncator_cfg -from ..migrations import m010_ollama_requester_config, m011_command_prefix_config, m012_runner_config +from ..migrations import m010_ollama_requester_config, m011_command_prefix_config, m012_runner_config, m013_http_api_config @stage.stage_class("MigrationStage") diff --git a/pkg/core/stages/setup_logger.py b/pkg/core/stages/setup_logger.py index 02446b85..8f385d1f 100644 --- a/pkg/core/stages/setup_logger.py +++ b/pkg/core/stages/setup_logger.py @@ -1,9 +1,38 @@ from __future__ import annotations +import logging +import asyncio +from datetime import datetime + from .. import stage, app from ..bootutils import log +class PersistenceHandler(logging.Handler, object): + """ + 保存日志到数据库 + """ + ap: app.Application + + def __init__(self, name, ap: app.Application): + logging.Handler.__init__(self) + self.ap = ap + + def emit(self, record): + """ + emit函数为自定义handler类时必重写的函数,这里可以根据需要对日志消息做一些处理,比如发送日志到服务器 + + 发出记录(Emit a record) + """ + try: + msg = self.format(record) + if self.ap.log_cache is not None: + self.ap.log_cache.add_log(msg) + + except Exception: + self.handleError(record) + + @stage.stage_class("SetupLoggerStage") class SetupLoggerStage(stage.BootingStage): """设置日志器阶段 @@ -12,4 +41,9 @@ class SetupLoggerStage(stage.BootingStage): async def run(self, ap: app.Application): """启动 """ - ap.logger = await log.init_logging() + persistence_handler = PersistenceHandler('LoggerHandler', ap) + + extra_handlers = [] + extra_handlers = [persistence_handler] + + ap.logger = await log.init_logging(extra_handlers) diff --git a/pkg/persistence/__init__.py b/pkg/persistence/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/persistence/database.py b/pkg/persistence/database.py new file mode 100644 index 00000000..0dd82817 --- /dev/null +++ b/pkg/persistence/database.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +import abc + +import sqlalchemy.ext.asyncio as sqlalchemy_asyncio + +from ..core import app + + +preregistered_managers: list[type[BaseDatabaseManager]] = [] + +def manager_class(name: str) -> None: + """注册一个数据库管理类""" + + def decorator(cls: type[BaseDatabaseManager]) -> type[BaseDatabaseManager]: + cls.name = name + preregistered_managers.append(cls) + return cls + + return decorator + + +class BaseDatabaseManager(abc.ABC): + """基础数据库管理类""" + + name: str + + ap: app.Application + + engine: sqlalchemy_asyncio.AsyncEngine + + def __init__(self, ap: app.Application) -> None: + self.ap = ap + + @abc.abstractmethod + async def initialize(self) -> None: + pass + + def get_engine(self) -> sqlalchemy_asyncio.AsyncEngine: + return self.engine diff --git a/pkg/persistence/databases/__init__.py b/pkg/persistence/databases/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pkg/persistence/databases/sqlite.py b/pkg/persistence/databases/sqlite.py new file mode 100644 index 00000000..14f89092 --- /dev/null +++ b/pkg/persistence/databases/sqlite.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +import sqlalchemy.ext.asyncio as sqlalchemy_asyncio + +from .. import database + + +@database.manager_class("sqlite") +class SQLiteDatabaseManager(database.BaseDatabaseManager): + """SQLite 数据库管理类""" + + async def initialize(self) -> None: + self.engine = sqlalchemy_asyncio.create_async_engine(f"sqlite+aiosqlite:///{self.ap.system_cfg.data['persistence']['sqlite']['path']}") diff --git a/pkg/persistence/mgr.py b/pkg/persistence/mgr.py new file mode 100644 index 00000000..d0c1fa26 --- /dev/null +++ b/pkg/persistence/mgr.py @@ -0,0 +1,55 @@ +from __future__ import annotations + +import asyncio +import datetime + +import sqlalchemy.ext.asyncio as sqlalchemy_asyncio +import sqlalchemy + +from . import database +from ..core import app +from .databases import sqlite + + +class PersistenceManager: + """持久化模块管理器""" + + ap: app.Application + + db: database.BaseDatabaseManager + """数据库管理器""" + + meta: sqlalchemy.MetaData + + def __init__(self, ap: app.Application): + self.ap = ap + self.meta = sqlalchemy.MetaData() + + async def initialize(self): + + for manager in database.preregistered_managers: + self.db = manager(self.ap) + await self.db.initialize() + + await self.create_tables() + + async def create_tables(self): + # TODO: 对扩展友好 + + # 日志 + async with self.get_db_engine().connect() as conn: + await conn.run_sync(self.meta.create_all) + + await conn.commit() + + async def execute_async( + self, + *args, + **kwargs + ): + async with self.get_db_engine().connect() as conn: + await conn.execute(*args, **kwargs) + await conn.commit() + + def get_db_engine(self) -> sqlalchemy_asyncio.AsyncEngine: + return self.db.get_engine() diff --git a/pkg/utils/logcache.py b/pkg/utils/logcache.py new file mode 100644 index 00000000..0fe419f1 --- /dev/null +++ b/pkg/utils/logcache.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +import pydantic + + +LOG_PAGE_SIZE = 20 +MAX_CACHED_PAGES = 10 + + +class LogPage(pydantic.BaseModel): + """日志页""" + + cached_count: int = 0 + + logs: str = "" + + def add_log(self, log: str) -> bool: + """添加日志 + + Returns: + bool: 是否已满 + """ + self.logs += log + self.cached_count += 1 + return self.cached_count >= LOG_PAGE_SIZE + + +class LogCache: + """由于 logger 是同步的,但实例中的数据库操作是异步的; + 同时,持久化的日志信息已经写入文件了,故做一个缓存来为前端提供日志查询服务""" + + log_pages: list[LogPage] = [] + """从前到后,越新的日志页越靠后""" + + def __init__(self): + self.log_pages = [] + self.log_pages.append(LogPage()) + + def add_log(self, log: str): + """添加日志""" + if self.log_pages[-1].add_log(log): + self.log_pages.append(LogPage()) + + if len(self.log_pages) > MAX_CACHED_PAGES: + self.log_pages.pop(0) + + def get_all_logs(self) -> str: + """获取所有日志""" + return "".join([page.logs for page in self.log_pages]) diff --git a/requirements.txt b/requirements.txt index 7bf257ed..f001e6af 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,7 @@ websockets urllib3 psutil async-lru -ollama \ No newline at end of file +ollama +quart +sqlalchemy[asyncio] +aiosqlite \ No newline at end of file diff --git a/templates/system.json b/templates/system.json index 0f6669fe..94a8ad16 100644 --- a/templates/system.json +++ b/templates/system.json @@ -11,5 +11,16 @@ }, "pipeline-concurrency": 20, "qcg-center-url": "https://api.qchatgpt.rockchin.top/api/v2", - "help-message": "QChatGPT - 😎高稳定性、🧩支持插件、🌏实时联网的 ChatGPT QQ 机器人🤖\n链接:https://q.rkcn.top" + "help-message": "QChatGPT - 😎高稳定性、🧩支持插件、🌏实时联网的 ChatGPT QQ 机器人🤖\n链接:https://q.rkcn.top", + "http-api": { + "enable": true, + "host": "0.0.0.0", + "port": 5300 + }, + "persistence": { + "sqlite": { + "path": "data/persistence.db" + }, + "use": "sqlite" + } } \ No newline at end of file From 9703fc0366ac6ef99378b5ce6ed336cefccb3f08 Mon Sep 17 00:00:00 2001 From: RockChinQ <1010553892@qq.com> Date: Sun, 13 Oct 2024 22:33:51 +0800 Subject: [PATCH 08/71] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=A2=9E=E9=87=8F=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/api/http/controller/groups/log.py | 21 --------------- pkg/api/http/controller/groups/logs.py | 32 ++++++++++++++++++++++ pkg/api/http/controller/main.py | 2 +- pkg/utils/logcache.py | 37 ++++++++++++++++++-------- 4 files changed, 59 insertions(+), 33 deletions(-) delete mode 100644 pkg/api/http/controller/groups/log.py create mode 100644 pkg/api/http/controller/groups/logs.py diff --git a/pkg/api/http/controller/groups/log.py b/pkg/api/http/controller/groups/log.py deleted file mode 100644 index fe6de631..00000000 --- a/pkg/api/http/controller/groups/log.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -import traceback - -import quart - -from .....core import app -from .. import group - - -@group.group_class('log', '/api/v1/log') -class LogRouterGroup(group.RouterGroup): - - async def initialize(self) -> None: - @self.route('', methods=['GET']) - async def _() -> str: - return self.success( - data={ - "logs": self.ap.log_cache.get_all_logs() - } - ) diff --git a/pkg/api/http/controller/groups/logs.py b/pkg/api/http/controller/groups/logs.py new file mode 100644 index 00000000..36aa0d75 --- /dev/null +++ b/pkg/api/http/controller/groups/logs.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import traceback + +import quart + +from .....core import app +from .. import group + + +@group.group_class('logs', '/api/v1/logs') +class LogsRouterGroup(group.RouterGroup): + + async def initialize(self) -> None: + @self.route('', methods=['GET']) + async def _() -> str: + + start_page_number = int(quart.request.args.get('start_page_number', 0)) + start_offset = int(quart.request.args.get('start_offset', 0)) + + logs_str, end_page_number, end_offset = self.ap.log_cache.get_log_by_pointer( + start_page_number=start_page_number, + start_offset=start_offset + ) + + return self.success( + data={ + "logs": logs_str, + "end_page_number": end_page_number, + "end_offset": end_offset + } + ) diff --git a/pkg/api/http/controller/main.py b/pkg/api/http/controller/main.py index 83e86696..6c7c72f2 100644 --- a/pkg/api/http/controller/main.py +++ b/pkg/api/http/controller/main.py @@ -5,7 +5,7 @@ import quart from ....core import app -from .groups import log +from .groups import logs from . import group diff --git a/pkg/utils/logcache.py b/pkg/utils/logcache.py index 0fe419f1..2b8151c2 100644 --- a/pkg/utils/logcache.py +++ b/pkg/utils/logcache.py @@ -7,12 +7,16 @@ MAX_CACHED_PAGES = 10 -class LogPage(pydantic.BaseModel): +class LogPage(): """日志页""" + number: int + """页码""" - cached_count: int = 0 + logs: list[str] - logs: str = "" + def __init__(self, number: int): + self.number = number + self.logs = [] def add_log(self, log: str) -> bool: """添加日志 @@ -20,9 +24,8 @@ def add_log(self, log: str) -> bool: Returns: bool: 是否已满 """ - self.logs += log - self.cached_count += 1 - return self.cached_count >= LOG_PAGE_SIZE + self.logs.append(log) + return len(self.logs) >= LOG_PAGE_SIZE class LogCache: @@ -34,16 +37,28 @@ class LogCache: def __init__(self): self.log_pages = [] - self.log_pages.append(LogPage()) + self.log_pages.append(LogPage(number=0)) def add_log(self, log: str): """添加日志""" if self.log_pages[-1].add_log(log): - self.log_pages.append(LogPage()) + self.log_pages.append(LogPage(number=self.log_pages[-1].number + 1)) if len(self.log_pages) > MAX_CACHED_PAGES: self.log_pages.pop(0) - def get_all_logs(self) -> str: - """获取所有日志""" - return "".join([page.logs for page in self.log_pages]) + def get_log_by_pointer( + self, + start_page_number: int, + start_offset: int, + ) -> tuple[str, int, int]: + """获取指定页码和偏移量的日志""" + final_logs_str = "" + + for page in self.log_pages: + if page.number == start_page_number: + final_logs_str += "\n".join(page.logs[start_offset:]) + elif page.number > start_page_number: + final_logs_str += "\n".join(page.logs) + + return final_logs_str, page.number, len(page.logs) From 0dd74c825b333532ba9415ed2ec2b7c44ecdd36e Mon Sep 17 00:00:00 2001 From: RockChinQ <1010553892@qq.com> Date: Sun, 13 Oct 2024 22:34:35 +0800 Subject: [PATCH 09/71] =?UTF-8?q?feat:=20=E5=89=8D=E7=AB=AF=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/package-lock.json | 116 +++++++++++++++++++++++++- web/package.json | 4 +- web/public/favicon.ico | Bin 15406 -> 9662 bytes web/src/App.vue | 23 ++++- web/src/assets/langbot-logo-block.png | Bin 0 -> 14953 bytes web/src/assets/logo.png | Bin 11955 -> 0 bytes web/src/assets/logo.svg | 6 -- web/src/pages/DashBoard.vue | 11 +++ web/src/pages/{index.vue => Logs.vue} | 5 +- web/src/pages/Plugins.vue | 9 ++ web/src/pages/Settings.vue | 9 ++ web/src/plugins/index.js | 3 + web/src/router/index.js | 12 ++- web/src/store/index.js | 9 ++ web/vite.config.mjs | 2 +- 15 files changed, 193 insertions(+), 16 deletions(-) create mode 100644 web/src/assets/langbot-logo-block.png delete mode 100644 web/src/assets/logo.png delete mode 100644 web/src/assets/logo.svg create mode 100644 web/src/pages/DashBoard.vue rename web/src/pages/{index.vue => Logs.vue} (53%) create mode 100644 web/src/pages/Plugins.vue create mode 100644 web/src/pages/Settings.vue create mode 100644 web/src/store/index.js diff --git a/web/package-lock.json b/web/package-lock.json index bca39b56..d60c5734 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -9,10 +9,12 @@ "version": "0.0.0", "dependencies": { "@mdi/font": "7.4.47", + "axios": "^1.7.7", "core-js": "^3.37.1", "roboto-fontface": "*", "vue": "^3.4.31", - "vuetify": "^3.6.11" + "vuetify": "^3.6.11", + "vuex": "^4.0.2" }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.5", @@ -958,7 +960,6 @@ "version": "6.6.4", "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", - "dev": true, "license": "MIT" }, "node_modules/@vue/reactivity": { @@ -1260,6 +1261,12 @@ "node": ">=16.14.0" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -1276,6 +1283,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1468,6 +1486,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1642,6 +1672,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2489,6 +2528,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2499,6 +2558,20 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3345,6 +3418,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3740,6 +3834,12 @@ "node": ">= 0.8.0" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4750,6 +4850,18 @@ } } }, + "node_modules/vuex": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz", + "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.0.0-beta.11" + }, + "peerDependencies": { + "vue": "^3.0.2" + } + }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", diff --git a/web/package.json b/web/package.json index 3225f51b..b24ede85 100644 --- a/web/package.json +++ b/web/package.json @@ -9,10 +9,12 @@ }, "dependencies": { "@mdi/font": "7.4.47", + "axios": "^1.7.7", "core-js": "^3.37.1", "roboto-fontface": "*", "vue": "^3.4.31", - "vuetify": "^3.6.11" + "vuetify": "^3.6.11", + "vuex": "^4.0.2" }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.5", diff --git a/web/public/favicon.ico b/web/public/favicon.ico index 8fb9f91b3aab4eec0c76ffc5342528033c61e247..00a756f5cedd93bf5228625c6ddeb185eb09e441 100644 GIT binary patch literal 9662 zcmeHNdvH|M8NUexq$trkrKnZ03e-*oERISmZ6_fF2tE*HM5!o4he5CdtzczJhk;r| z1VY{z0t66HK&HGBAPR|j$fJ!s5=e4)H`&du*}Suxglu*A#}Pwe`|O^c3x@mbgM-VSJ`3ohh7TJoUE4I7 zZ{7HNZT~2elU}5n;v8~zbdb~Gpti;;YA8A-bkU{FQSht2@wXPf?7a>A#t+`e+g_(8 za|Ll>9XiAr&K-7Yt7RG?G*2ki31KAMz&2I}igtdzZ>HH46E)U=|1H-;X5A! z!k_p8)yNk_-yC-|@zK6Ibi>)6=A?!>8W&CFr;~&qm`gk#gdJ|_*MI!rdcq8`r%TLkU)|a!ipr8+BJ=fpF%HjZ?bca7eff=_$3y(IROv*& zwU>^e&s*%zZNRY##6rWt_eh)aa;LuM=X5*Xq>BCTf*-H?wt?=tqw5gYY~(;(G937; zj}QFEKbdrgJ|NqTR&qKWA_t3ggxjycj`&n_Ig{FNG?T-2gPO|nsC@g}Tku7bA!7rz z)EdZ+cFl$>h&4;R@o_xZxqz(oW?`%Ezlu|Gyn8HVKj_!`2R6~4P9TSqsJ+!f6?-|p zVScK8;#{A(iR{SBo~1f}*G>G~9`VLnQ|Z}+8cT9WlQ_epFU*5aU!%tFHKJYB*<_MA zpQ~e`4#fWYtR&=J66*%`zbAef+%e?tA*kn7>aU!<$uq{kZz;95SiRb`n6;$!_=PTqR#CIH$&Jn4-cH7o8*jD)E;;pes;y9R2sh+4NKN4p z#4?Xq06lg4mr+}bMd9l45Bq>Wld0lVJowW=cIJ=Bj~@JiPvwLd(q+a1UlBD_mXI+! zP0VNSI0PL&e-H8HTku0m=E4(VP3sxsu?Hbj{%1nBwno@0ef~`wxHuFDe>4Z)ryH%! z)PePiDdQs_n=n6lY&qo>vDV;N?v35`K7;*$IK>>76Q%y^1qZ}Db~@Ur`kPeP2zy8`E_mSVj5{1hu_mawn&lO59Y`(~#C$_0VwT7U zU2`x{{EV|~+gz&F6be5(Y^_wCw}Z6FFLugPZ{9Fp6uZ}6IfdBh#6G8kEM^^-U}WNyh%o zAC7m1TfNrj_*FePkBWK7^H&wuJnk_k_Yim8V|e6W*Zafko1-L1IP^QO>KM#5+yD6A zCgz3>2zm79!-qm`?{YZWSBj|M&7|kKq0J@L@v+N+rN=Or9#?{zkP;FxO>x zcZP40Yk*&TPsO^Iv9tUE(iqIx`5WGq4UzhlGsjFMDuN1s zU#)s>6<`m-cXffE55TO-eKNX*;_~N5-BXNjMxJHHQzKOTT>k{J4q^-v zJ)C7*qDsJvwR2sSj{!cYH|Gkp|32U+Vd=T R2s@W^jbA>raUCv6{{yx2ON;;j literal 15406 zcmeHO3v5%@8NNVarCoU?zSqezmT6npu}xxXJJhY(x=s~hQ>SSYYSncU&|(|9Y?QVX z@}TB1G8mASM;tRKYgJkrAW)&g7${B%c>oE7;)O>NNO_bu4G-IdNB({PbTkJL{ZGDJe2DLL+vq#sL?l$jZPe_*I2y@|4sBRjso zUy`aUlJo$60})6B%aMKN&yMG1Qvjth%4dDy{HM^34) z&_TyGJg3UC{J|n2-wr)LDYwhNWBH4VL-JgYz;erTEKiMF20`|$Cf~GysBU40j@&)u zboK>@(yL25%R|RmS~6@99bG>PviV`j>&k~M@~J%cn{`uCzUOyY^23rlWt6DH=aut3 zlZo^g63xIXwP(4)hgT<|I>hSGqh7YbLP$jL+%p0>vM2Su?wmOV;-uyLFww=KN5Ox{j<% zmi}mZc22bZyxbmI!x+Ch3+qsk+#YbHJ{CdQjDh~;LRJB63Pq&~p

*N){PEn7FA;EAwj=|b_AUGw z{Eb1Zt8`N8chL?vjkw~yAvg~N+W_qV|N7U7a3HGfPY0GBqUxOaLJzPO2|Qz7H&yF{ z!Y@7SbxH$-YlpxgCaImE$Fk6T4dUMU!=a+u##*PZb&m9d@{VbA<&v z;n*Hvm#Nt5zF}Ud{=9#v%+3;8${f~WV?Q`CFATe5JXp$wT(q1TOU7#0jK6PDXZ)(1 zOSF4N3hStRA?+K$*ZctH(lRF!KFSL%W20hM6%Rz+k9We?_C36J>PVG2%Y`27X;nW+ z*x`6oe7S`dXABgw#vFYvuM;-c|L9ua=7z9?9B!cL=Fyiz34xz{6kpCd?PeyG2V7mkg!!m@d$btKgDK_Ib zt|Qt#$Am-fZ&{xA0GOAnn8Ue+QQBY3NiO*vfvd{5lsp3L_K5f@hhj{}^Nk#us4?p+ ztOM)g!1L3Ff(Tk8KQe4khoQS4_&0;4zo|FQr(iM%k4L+U*z zff0J2FQWxM*K?>u4d$6_8R>l`cK8U+!w`A!D@A2^2+L%A~GSv8* z(uV!wgkD)YqY(1-;X}j zv4?n}%=LOp! z;8GgCC~I2q%*#5{$kpuKJ6-ET*}o#)Z>p>vQsxF25{?c6CQ5()gsGtn{Ul`*ykb!bDMnnlGUdfYdn+9lsY%+pJ_Z_xHs+imH? z!L`+luUT1yv*4C1Z&<#Qlui*r2~lxBI`sAm+}oX{s=OcRf9A0%Q^56>8DC==kKtgy z>D-TeE@f3uu4&Y?ZVVlM3wLg~Z>Y}xfNE|1MIAijELQN`Y2<45F5=t5>wtYuk>yuH zXEJj-vN;uZD0|537U$Eq5Oek?f#JlNGq{dN(qiDuxO185eWG@Tuk1zKUsCs)MAVm# zZTvIN+Wl0&MDXYQF#3{qXA$(Ft>uw;>&qkE2f#n|`-?1D`gl8Gqj+=j77qvaV7#CH z-mDV$x0TsBRP5B|jZ%mFQ}FDv4Rn5l$yl(|`Q7Oe;~u+P58Qvv*6uy)7U=EIpPM-5 zPv}(pkwc!Zx3$@4;Y){)0d2tO5v#db4**wJ~;d2`ShTX zw|GaKwMoD4ydIzqSN(;l&j_8}$q^eYga7tU+}ZvgxyO*m1UDkuojePFZ zG`-$PpE1`DpyrLMO}R7wCfu3t6Y|B7d%f_n+{8^32;HSdS9&hVGwt*sUYr-!%%`a~|XP)!GJa zaPfvq9vz7{ms4}`U~qpQ+@r?~G$CeSuOWUWWPj8xrxoKsi%ALkMoN~Re@yeI<`b#& zQakqh?^Bfdlv*~90r${a#kP)=j;z5r;Qua|_8~6ciamnnhpFd84r{fjgNVx%{UgRa z5XnEKlf}cZ&elidYgP}Qi}Z0^o$C^y%G~k#_Qp8=1^BDy6=xQN&GlMeiCUK;x&F$^ zjA44c!M?-)Y1kOO--Oix|DE7=F!d?WE|oc-P;ICUYO&YsIA3qS68`^+SoxzLDfxQ- z)V%&7_{XQaWqV^?8s0=b5Yxwfs(L2+kBDu`F4FIF*gxGfML8AK#-01US-npCiqalZ z?M~DOWA<^ZR|~P;79^A!*A-C1sshAC6<~Z9P|a%vs7IcNOJh792S@Vc$sCItcXG)K z1Fn?E#hswKujQD~WT#qpf3`ix0(EL{#By^?PeQ2& - - - + + + + + + + + + + + + + + + + + + + diff --git a/web/src/assets/langbot-logo-block.png b/web/src/assets/langbot-logo-block.png new file mode 100644 index 0000000000000000000000000000000000000000..b2caca7b6b950b6fb2b79d71d1bbe20576c2c906 GIT binary patch literal 14953 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 WM;=@(*}%7>bk-Jj=8dMlDgOfo2LuHG literal 0 HcmV?d00001 diff --git a/web/src/assets/logo.png b/web/src/assets/logo.png deleted file mode 100644 index a5f23ae7bff64954cf3537377a9f99306baf083d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11955 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 - - - - - diff --git a/web/src/pages/DashBoard.vue b/web/src/pages/DashBoard.vue new file mode 100644 index 00000000..744848ec --- /dev/null +++ b/web/src/pages/DashBoard.vue @@ -0,0 +1,11 @@ + + + + + diff --git a/web/src/pages/index.vue b/web/src/pages/Logs.vue similarity index 53% rename from web/src/pages/index.vue rename to web/src/pages/Logs.vue index 6488c51b..2a63fbbd 100644 --- a/web/src/pages/index.vue +++ b/web/src/pages/Logs.vue @@ -1,6 +1,9 @@ + + \ No newline at end of file diff --git a/web/src/pages/Plugins.vue b/web/src/pages/Plugins.vue new file mode 100644 index 00000000..e8aa62b4 --- /dev/null +++ b/web/src/pages/Plugins.vue @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/web/src/pages/Settings.vue b/web/src/pages/Settings.vue new file mode 100644 index 00000000..5c044f7c --- /dev/null +++ b/web/src/pages/Settings.vue @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/web/src/plugins/index.js b/web/src/plugins/index.js index 9eb2eb24..c9305e28 100644 --- a/web/src/plugins/index.js +++ b/web/src/plugins/index.js @@ -7,9 +7,12 @@ // Plugins import vuetify from './vuetify' import router from '@/router' +import store from '@/store' + export function registerPlugins (app) { app .use(vuetify) .use(router) + .use(store) } diff --git a/web/src/router/index.js b/web/src/router/index.js index cffec2d7..0c556d28 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -7,7 +7,17 @@ // Composables import { createRouter, createWebHistory } from 'vue-router/auto' -import { routes } from 'vue-router/auto-routes' +import DashBoard from '../pages/DashBoard.vue' +import Settings from '../pages/Settings.vue' +import Logs from '../pages/Logs.vue' +import Plugins from '../pages/Plugins.vue' + +const routes = [ + { path: '/', component: DashBoard }, + { path: '/settings', component: Settings }, + { path: '/logs', component: Logs }, + { path: '/plugins', component: Plugins }, +] const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), diff --git a/web/src/store/index.js b/web/src/store/index.js new file mode 100644 index 00000000..b62f226b --- /dev/null +++ b/web/src/store/index.js @@ -0,0 +1,9 @@ +import { createStore } from 'vuex' +import router from '@/router' +import axios from 'axios' + +export default createStore({ + state: {}, + mutations: {}, + actions: {}, +}) diff --git a/web/vite.config.mjs b/web/vite.config.mjs index 1e6733cd..9870a71e 100644 --- a/web/vite.config.mjs +++ b/web/vite.config.mjs @@ -49,6 +49,6 @@ export default defineConfig({ ], }, server: { - port: 3000, + port: 3002, }, }) From 231dca956d6e8d1a70c6acde421b5690b5e580ec Mon Sep 17 00:00:00 2001 From: Junyan Qin <1010553892@qq.com> Date: Mon, 14 Oct 2024 18:52:28 +0800 Subject: [PATCH 10/71] =?UTF-8?q?feat:=20=E6=97=A5=E5=BF=97=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/App.vue | 50 +++++++++++++++-- web/src/components/PageTitle.vue | 51 +++++++++++++++++ web/src/pages/DashBoard.vue | 4 +- web/src/pages/Logs.vue | 95 +++++++++++++++++++++++++++++++- web/src/pages/Plugins.vue | 8 ++- web/src/pages/Settings.vue | 8 ++- web/src/plugins/index.js | 4 +- web/src/store/index.js | 6 +- 8 files changed, 213 insertions(+), 13 deletions(-) create mode 100644 web/src/components/PageTitle.vue diff --git a/web/src/App.vue b/web/src/App.vue index f5c38a01..7ba342ba 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -2,17 +2,32 @@ - + + + + + - + - + - + - + + @@ -24,3 +39,28 @@ + + \ No newline at end of file diff --git a/web/src/components/PageTitle.vue b/web/src/components/PageTitle.vue new file mode 100644 index 00000000..a4cfb4f0 --- /dev/null +++ b/web/src/components/PageTitle.vue @@ -0,0 +1,51 @@ + + + + + \ No newline at end of file diff --git a/web/src/pages/DashBoard.vue b/web/src/pages/DashBoard.vue index 744848ec..2cf64547 100644 --- a/web/src/pages/DashBoard.vue +++ b/web/src/pages/DashBoard.vue @@ -1,9 +1,11 @@ \ No newline at end of file diff --git a/web/src/pages/Plugins.vue b/web/src/pages/Plugins.vue index e8aa62b4..3ccb7a8e 100644 --- a/web/src/pages/Plugins.vue +++ b/web/src/pages/Plugins.vue @@ -1,9 +1,13 @@ \ No newline at end of file + + diff --git a/web/src/pages/Settings.vue b/web/src/pages/Settings.vue index 5c044f7c..7cb2711b 100644 --- a/web/src/pages/Settings.vue +++ b/web/src/pages/Settings.vue @@ -1,9 +1,13 @@ \ No newline at end of file + + diff --git a/web/src/plugins/index.js b/web/src/plugins/index.js index c9305e28..9d8b9d4e 100644 --- a/web/src/plugins/index.js +++ b/web/src/plugins/index.js @@ -8,11 +8,13 @@ import vuetify from './vuetify' import router from '@/router' import store from '@/store' - +import axios from 'axios' export function registerPlugins (app) { app .use(vuetify) .use(router) .use(store) + + app.config.globalProperties.$axios = axios } diff --git a/web/src/store/index.js b/web/src/store/index.js index b62f226b..b18cfcbc 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -3,7 +3,11 @@ import router from '@/router' import axios from 'axios' export default createStore({ - state: {}, + state: { + apiBaseUrl: 'http://localhost:5300/api/v1', + autoRefreshLog: false, + version: '0.0.1' + }, mutations: {}, actions: {}, }) From 1fbc92bc6d159bf593dfba42891b40081138e96a Mon Sep 17 00:00:00 2001 From: Junyan Qin <1010553892@qq.com> Date: Mon, 14 Oct 2024 21:18:36 +0800 Subject: [PATCH 11/71] =?UTF-8?q?perf:=20=E9=A6=96=E9=A1=B5=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E7=89=88=E6=9C=AC=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/api/http/controller/group.py | 2 +- pkg/api/http/controller/groups/system.py | 19 ++++++++++ pkg/api/http/controller/main.py | 2 +- pkg/core/boot.py | 6 +++ pkg/core/bootutils/log.py | 4 +- pkg/utils/constants.py | 2 + web/src/App.vue | 47 ++++++++++++++++++++++-- web/src/pages/Logs.vue | 4 +- web/src/plugins/index.js | 1 + web/src/store/index.js | 14 ++++++- 10 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 pkg/api/http/controller/groups/system.py diff --git a/pkg/api/http/controller/group.py b/pkg/api/http/controller/group.py index b9533567..4f253e05 100644 --- a/pkg/api/http/controller/group.py +++ b/pkg/api/http/controller/group.py @@ -54,7 +54,7 @@ async def handler_error(*args, **kwargs): return self.http_status(500, -2, str(e)) new_f = handler_error - new_f.__name__ = f.__name__ + new_f.__name__ = (self.name + rule).replace('/', '__') new_f.__doc__ = f.__doc__ self.quart_app.route(rule, **options)(new_f) diff --git a/pkg/api/http/controller/groups/system.py b/pkg/api/http/controller/groups/system.py new file mode 100644 index 00000000..43b06ddd --- /dev/null +++ b/pkg/api/http/controller/groups/system.py @@ -0,0 +1,19 @@ +import quart + +from .....core import app +from .. import group +from .....utils import constants + + +@group.group_class('system', '/api/v1/system') +class SystemRouterGroup(group.RouterGroup): + + async def initialize(self) -> None: + @self.route('/info', methods=['GET']) + async def _() -> str: + return self.success( + data={ + "version": constants.semantic_version, + "debug": constants.debug_mode + } + ) \ No newline at end of file diff --git a/pkg/api/http/controller/main.py b/pkg/api/http/controller/main.py index 6c7c72f2..76545832 100644 --- a/pkg/api/http/controller/main.py +++ b/pkg/api/http/controller/main.py @@ -5,7 +5,7 @@ import quart from ....core import app -from .groups import logs +from .groups import logs, system from . import group diff --git a/pkg/core/boot.py b/pkg/core/boot.py index 1e5f1052..0f0ab3ae 100644 --- a/pkg/core/boot.py +++ b/pkg/core/boot.py @@ -2,10 +2,12 @@ import traceback import asyncio +import os from . import app from ..audit import identifier from . import stage +from ..utils import constants # 引入启动阶段实现以便注册 from .stages import load_config, setup_logger, build_app, migrate, show_notes @@ -25,6 +27,10 @@ async def make_app(loop: asyncio.AbstractEventLoop) -> app.Application: # 生成标识符 identifier.init() + # 确定是否为调试模式 + if "DEBUG" in os.environ and os.environ["DEBUG"] in ["true", "1"]: + constants.debug_mode = True + ap = app.Application() ap.event_loop = loop diff --git a/pkg/core/bootutils/log.py b/pkg/core/bootutils/log.py index ad8252df..36bc51a0 100644 --- a/pkg/core/bootutils/log.py +++ b/pkg/core/bootutils/log.py @@ -5,6 +5,8 @@ import colorlog +from ...utils import constants + log_colors_config = { "DEBUG": "green", # cyan white @@ -22,7 +24,7 @@ async def init_logging(extra_handlers: list[logging.Handler] = None) -> logging. level = logging.INFO - if "DEBUG" in os.environ and os.environ["DEBUG"] in ["true", "1"]: + if constants.debug_mode: level = logging.DEBUG log_file_name = "data/logs/qcg-%s.log" % time.strftime( diff --git a/pkg/utils/constants.py b/pkg/utils/constants.py index 74437702..79b470e6 100644 --- a/pkg/utils/constants.py +++ b/pkg/utils/constants.py @@ -1 +1,3 @@ semantic_version = "v3.3.1.1" + +debug_mode = False \ No newline at end of file diff --git a/web/src/App.vue b/web/src/App.vue index 7ba342ba..59f01d97 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -5,7 +5,13 @@ @@ -23,7 +29,7 @@ \ No newline at end of file From 7174742886e061296f9dd14e8666ef01bd286b3f Mon Sep 17 00:00:00 2001 From: Junyan Qin <1010553892@qq.com> Date: Tue, 15 Oct 2024 00:07:40 +0800 Subject: [PATCH 13/71] =?UTF-8?q?feat:=20settings=20=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/api/http/controller/groups/settings.py | 41 ++++++++++ pkg/api/http/controller/groups/system.py | 2 +- pkg/api/http/controller/main.py | 2 +- pkg/config/manager.py | 14 +++- pkg/config/settings.py | 73 +++++++++++++++++ pkg/core/app.py | 3 + pkg/core/stages/load_config.py | 35 ++++++++ web/src/pages/Logs.vue | 4 +- web/src/pages/Settings.vue | 93 +++++++++++++++++++++- 9 files changed, 258 insertions(+), 9 deletions(-) create mode 100644 pkg/api/http/controller/groups/settings.py create mode 100644 pkg/config/settings.py diff --git a/pkg/api/http/controller/groups/settings.py b/pkg/api/http/controller/groups/settings.py new file mode 100644 index 00000000..15a145d2 --- /dev/null +++ b/pkg/api/http/controller/groups/settings.py @@ -0,0 +1,41 @@ +import quart + +from .....core import app +from .. import group + + +@group.group_class('settings', '/api/v1/settings') +class SettingsRouterGroup(group.RouterGroup): + + async def initialize(self) -> None: + + @self.route('', methods=['GET']) + async def _() -> str: + return self.success( + data={ + "managers": [ + { + "name": m.name, + "description": m.description, + } + for m in self.ap.settings_mgr.get_manager_list() + ] + } + ) + + @self.route('/', methods=['GET']) + async def _(manager_name: str) -> str: + + manager = self.ap.settings_mgr.get_manager(manager_name) + + return self.success( + data={ + "manager": { + "name": manager.name, + "description": manager.description, + "schema": manager.schema, + "file": manager.file.config_file_name, + "data": manager.data + } + } + ) diff --git a/pkg/api/http/controller/groups/system.py b/pkg/api/http/controller/groups/system.py index 43b06ddd..5575d06e 100644 --- a/pkg/api/http/controller/groups/system.py +++ b/pkg/api/http/controller/groups/system.py @@ -16,4 +16,4 @@ async def _() -> str: "version": constants.semantic_version, "debug": constants.debug_mode } - ) \ No newline at end of file + ) diff --git a/pkg/api/http/controller/main.py b/pkg/api/http/controller/main.py index 76545832..ac82a98e 100644 --- a/pkg/api/http/controller/main.py +++ b/pkg/api/http/controller/main.py @@ -5,7 +5,7 @@ import quart from ....core import app -from .groups import logs, system +from .groups import logs, system, settings from . import group diff --git a/pkg/config/manager.py b/pkg/config/manager.py index 88ed6525..41c815c6 100644 --- a/pkg/config/manager.py +++ b/pkg/config/manager.py @@ -4,11 +4,19 @@ from .impls import pymodule, json as json_file, yaml as yaml_file -managers: ConfigManager = [] - - class ConfigManager: """配置文件管理器""" + + name: str = None + """配置管理器名""" + + description: str = None + """配置管理器描述""" + + schema: dict = None + """配置文件 schema + 需要符合 JSON Schema Draft 7 规范 + """ file: file_model.ConfigFile = None """配置文件实例""" diff --git a/pkg/config/settings.py b/pkg/config/settings.py new file mode 100644 index 00000000..9d1a0cbc --- /dev/null +++ b/pkg/config/settings.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from . import manager as config_manager +from ..core import app + + +class SettingsManager: + """设置管理器 + 保存、管理多个配置文件管理器 + """ + + ap: app.Application + + managers: list[config_manager.ConfigManager] = [] + """配置文件管理器列表""" + + def __init__(self, ap: app.Application) -> None: + self.ap = ap + self.managers = [] + + async def initialize(self) -> None: + pass + + def register_manager( + self, + name: str, + description: str, + manager: config_manager.ConfigManager, + schema: dict=None, + ) -> None: + """注册配置管理器 + + Args: + name (str): 配置管理器名 + description (str): 配置管理器描述 + manager (ConfigManager): 配置管理器 + schema (dict): 配置文件 schema,符合 JSON Schema Draft 7 规范 + """ + + for m in self.managers: + if m.name == name: + raise ValueError(f'配置管理器名 {name} 已存在') + + manager.name = name + manager.description = description + manager.schema = schema + self.managers.append(manager) + + def get_manager(self, name: str) -> config_manager.ConfigManager: + """获取配置管理器 + + Args: + name (str): 配置管理器名 + + Returns: + ConfigManager: 配置管理器 + """ + + for m in self.managers: + if m.name == name: + return m + + raise ValueError(f'配置管理器 {name} 不存在') + + def get_manager_list(self) -> list[config_manager.ConfigManager]: + """获取配置管理器列表 + + Returns: + list[ConfigManager]: 配置管理器列表 + """ + + return self.managers + diff --git a/pkg/core/app.py b/pkg/core/app.py index 2f0e0340..11a1eed3 100644 --- a/pkg/core/app.py +++ b/pkg/core/app.py @@ -11,6 +11,7 @@ from ..provider.tools import toolmgr as llm_tool_mgr from ..provider import runnermgr from ..config import manager as config_mgr +from ..config import settings as settings_mgr from ..audit.center import v2 as center_mgr from ..command import cmdmgr from ..plugin import manager as plugin_mgr @@ -43,6 +44,8 @@ class Application: runner_mgr: runnermgr.RunnerManager = None + settings_mgr: settings_mgr.SettingsManager = None + # ======= 配置管理器 ======= command_cfg: config_mgr.ConfigManager = None diff --git a/pkg/core/stages/load_config.py b/pkg/core/stages/load_config.py index cb6e1ed0..006af00f 100644 --- a/pkg/core/stages/load_config.py +++ b/pkg/core/stages/load_config.py @@ -2,6 +2,7 @@ from .. import stage, app from ..bootutils import config +from ...config import settings as settings_mgr @stage.stage_class("LoadConfigStage") @@ -12,12 +13,46 @@ class LoadConfigStage(stage.BootingStage): async def run(self, ap: app.Application): """启动 """ + + ap.settings_mgr = settings_mgr.SettingsManager(ap) + await ap.settings_mgr.initialize() + ap.command_cfg = await config.load_json_config("data/config/command.json", "templates/command.json", completion=False) ap.pipeline_cfg = await config.load_json_config("data/config/pipeline.json", "templates/pipeline.json", completion=False) ap.platform_cfg = await config.load_json_config("data/config/platform.json", "templates/platform.json", completion=False) ap.provider_cfg = await config.load_json_config("data/config/provider.json", "templates/provider.json", completion=False) ap.system_cfg = await config.load_json_config("data/config/system.json", "templates/system.json", completion=False) + ap.settings_mgr.register_manager( + name="command.json", + description="命令配置", + manager=ap.command_cfg + ) + + ap.settings_mgr.register_manager( + name="pipeline.json", + description="消息处理流水线配置", + manager=ap.pipeline_cfg + ) + + ap.settings_mgr.register_manager( + name="platform.json", + description="消息平台配置", + manager=ap.platform_cfg + ) + + ap.settings_mgr.register_manager( + name="provider.json", + description="大模型能力配置", + manager=ap.provider_cfg + ) + + ap.settings_mgr.register_manager( + name="system.json", + description="系统配置", + manager=ap.system_cfg + ) + ap.plugin_setting_meta = await config.load_json_config("plugins/plugins.json", "templates/plugin-settings.json") await ap.plugin_setting_meta.dump_config() diff --git a/web/src/pages/Logs.vue b/web/src/pages/Logs.vue index aeaff77d..f76486f6 100644 --- a/web/src/pages/Logs.vue +++ b/web/src/pages/Logs.vue @@ -68,7 +68,7 @@ onUnmounted(() => { margin: 1rem; margin-top: 1rem; height: 3rem; - border-radius: 1rem; + border-radius: 0.3rem; } .toolbar-component { @@ -80,7 +80,7 @@ onUnmounted(() => { margin: 1rem; margin-top: 1rem; height: calc(100vh - 9.5rem); - border-radius: 1rem; + border-radius: 0.3rem; } diff --git a/web/src/pages/Settings.vue b/web/src/pages/Settings.vue index 7cb2711b..67d15856 100644 --- a/web/src/pages/Settings.vue +++ b/web/src/pages/Settings.vue @@ -1,13 +1,102 @@ +.config-tab-content { + margin: 0.2rem; +} + \ No newline at end of file From d52f9b9543b3ad03dc4f1ca24955516011e1ec82 Mon Sep 17 00:00:00 2001 From: Junyan Qin <1010553892@qq.com> Date: Tue, 15 Oct 2024 14:23:56 +0800 Subject: [PATCH 14/71] =?UTF-8?q?feat(settings):=20json=20=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/api/http/controller/group.py | 5 -- pkg/api/http/controller/groups/settings.py | 9 ++ pkg/api/http/controller/main.py | 2 + pkg/core/bootutils/deps.py | 1 + requirements.txt | 3 +- web/package-lock.json | 7 ++ web/package.json | 1 + web/src/pages/Settings.vue | 97 ++++++++++++++++++++-- web/src/store/index.js | 1 + 9 files changed, 113 insertions(+), 13 deletions(-) diff --git a/pkg/api/http/controller/group.py b/pkg/api/http/controller/group.py index 4f253e05..053ba1cd 100644 --- a/pkg/api/http/controller/group.py +++ b/pkg/api/http/controller/group.py @@ -63,11 +63,6 @@ async def handler_error(*args, **kwargs): return decorator def _cors(self, response: quart.Response) -> quart.Response: - # Quart-Cors 似乎很久没维护了,所以自己写 - response.headers['Access-Control-Allow-Origin'] = '*' - response.headers['Access-Control-Allow-Headers'] = '*' - response.headers['Access-Control-Allow-Methods'] = '*' - response.headers['Access-Control-Allow-Credentials'] = 'true' return response def success(self, data: typing.Any = None) -> quart.Response: diff --git a/pkg/api/http/controller/groups/settings.py b/pkg/api/http/controller/groups/settings.py index 15a145d2..72f41d97 100644 --- a/pkg/api/http/controller/groups/settings.py +++ b/pkg/api/http/controller/groups/settings.py @@ -39,3 +39,12 @@ async def _(manager_name: str) -> str: } } ) + + @self.route('//data', methods=['PUT']) + async def _(manager_name: str) -> str: + data = await quart.request.json + manager = self.ap.settings_mgr.get_manager(manager_name) + manager.data = data['data'] + return self.success(data={ + "data": manager.data + }) diff --git a/pkg/api/http/controller/main.py b/pkg/api/http/controller/main.py index ac82a98e..17b15192 100644 --- a/pkg/api/http/controller/main.py +++ b/pkg/api/http/controller/main.py @@ -3,6 +3,7 @@ import asyncio import quart +import quart_cors from ....core import app from .groups import logs, system, settings @@ -18,6 +19,7 @@ class HTTPController: def __init__(self, ap: app.Application) -> None: self.ap = ap self.quart_app = quart.Quart(__name__) + quart_cors.cors(self.quart_app, allow_origin='*') async def initialize(self) -> None: await self.register_routes() diff --git a/pkg/core/bootutils/deps.py b/pkg/core/bootutils/deps.py index 8c0127e5..69dfe138 100644 --- a/pkg/core/bootutils/deps.py +++ b/pkg/core/bootutils/deps.py @@ -16,6 +16,7 @@ "async_lru": "async-lru", "ollama": "ollama", "quart": "quart", + "quart_cors": "quart-cors", "sqlalchemy": "sqlalchemy[asyncio]", "aiosqlite": "aiosqlite", } diff --git a/requirements.txt b/requirements.txt index f001e6af..389f172f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,4 +17,5 @@ async-lru ollama quart sqlalchemy[asyncio] -aiosqlite \ No newline at end of file +aiosqlite +quart-cors \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index d60c5734..9067fd2b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@mdi/font": "7.4.47", "axios": "^1.7.7", + "codemirror": "^5.65.18", "core-js": "^3.37.1", "roboto-fontface": "*", "vue": "^3.4.31", @@ -1466,6 +1467,12 @@ "node": ">= 6" } }, + "node_modules/codemirror": { + "version": "5.65.18", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.18.tgz", + "integrity": "sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==", + "license": "MIT" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", diff --git a/web/package.json b/web/package.json index b24ede85..8f961de1 100644 --- a/web/package.json +++ b/web/package.json @@ -10,6 +10,7 @@ "dependencies": { "@mdi/font": "7.4.47", "axios": "^1.7.7", + "codemirror": "^5.65.18", "core-js": "^3.37.1", "roboto-fontface": "*", "vue": "^3.4.31", diff --git a/web/src/pages/Settings.vue b/web/src/pages/Settings.vue index 67d15856..16ceaab8 100644 --- a/web/src/pages/Settings.vue +++ b/web/src/pages/Settings.vue @@ -2,11 +2,16 @@ - - {{ manager.name }} + + + + - - + @@ -24,7 +29,22 @@ + +

+ 重置 + 应用 +
+
+ +
+ +
+ + + + +
@@ -25,6 +29,7 @@ const snackbar = inject('snackbar'); const { proxy } = getCurrentInstance() const logContent = ref('') +const logContentHTML = ref('') const refresh = () => { refreshLog() @@ -35,6 +40,10 @@ let logPointer = { "start_offset": 0 } +import { AnsiUp } from 'ansi_up'; + +const ansiUp = new AnsiUp() + const refreshLog = () => { proxy.$axios.get(`/logs`, { params: { @@ -47,8 +56,14 @@ const refreshLog = () => { return } logContent.value += response.data.data.logs + logContentHTML.value += ansiUp.ansi_to_html(response.data.data.logs) logPointer.start_page_number = response.data.data.end_page_number logPointer.start_offset = response.data.data.end_offset + + if (proxy.$store.state.autoScrollLog) { + // 滚动到最底部 + document.getElementById('log-content-html').scrollTop = document.getElementById('log-content-html').scrollHeight + } }).catch(error => { snackbar.error(error.message) }) @@ -73,7 +88,7 @@ onUnmounted(() => { \ No newline at end of file + diff --git a/web/src/store/index.js b/web/src/store/index.js index eb041cbc..fa452a9b 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -6,6 +6,7 @@ export default createStore({ state: { apiBaseUrl: 'http://localhost:5300/api/v1', autoRefreshLog: false, + autoScrollLog: true, settingsPageTab: '', version: 'v0.0.0', debug: false, From 9336abff8ba15a76546b3241c1b4959ff381c99b Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Sun, 10 Nov 2024 11:46:41 +0800 Subject: [PATCH 41/71] =?UTF-8?q?perf:=20=E4=BD=BF=E7=94=A8=20json-editor-?= =?UTF-8?q?vue=20=E4=BD=9C=E4=B8=BAjson=E7=BC=96=E8=BE=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/api/http/controller/groups/settings.py | 3 +- pkg/config/manager.py | 3 + pkg/config/settings.py | 2 + pkg/core/stages/load_config.py | 15 +- web/package-lock.json | 927 ++++++++++++++++++++- web/package.json | 1 + web/src/pages/Settings.vue | 38 +- 7 files changed, 958 insertions(+), 31 deletions(-) diff --git a/pkg/api/http/controller/groups/settings.py b/pkg/api/http/controller/groups/settings.py index 9d8727be..00693239 100644 --- a/pkg/api/http/controller/groups/settings.py +++ b/pkg/api/http/controller/groups/settings.py @@ -38,7 +38,8 @@ async def _(manager_name: str) -> str: "description": manager.description, "schema": manager.schema, "file": manager.file.config_file_name, - "data": manager.data + "data": manager.data, + "doc_link": manager.doc_link } } ) diff --git a/pkg/config/manager.py b/pkg/config/manager.py index 41c815c6..4421003c 100644 --- a/pkg/config/manager.py +++ b/pkg/config/manager.py @@ -24,6 +24,9 @@ class ConfigManager: data: dict = None """配置数据""" + doc_link: str = None + """配置文件文档链接""" + def __init__(self, cfg_file: file_model.ConfigFile) -> None: self.file = cfg_file self.data = {} diff --git a/pkg/config/settings.py b/pkg/config/settings.py index 5265c6e5..1f21e926 100644 --- a/pkg/config/settings.py +++ b/pkg/config/settings.py @@ -27,6 +27,7 @@ def register_manager( description: str, manager: config_manager.ConfigManager, schema: dict=None, + doc_link: str=None, ) -> None: """注册配置管理器 @@ -44,6 +45,7 @@ def register_manager( manager.name = name manager.description = description manager.schema = schema + manager.doc_link = doc_link self.managers.append(manager) def get_manager(self, name: str) -> config_manager.ConfigManager | None: diff --git a/pkg/core/stages/load_config.py b/pkg/core/stages/load_config.py index 9ae4c20c..29a4c446 100644 --- a/pkg/core/stages/load_config.py +++ b/pkg/core/stages/load_config.py @@ -28,35 +28,40 @@ async def run(self, ap: app.Application): name="command.json", description="命令配置", manager=ap.command_cfg, - schema=schema.CONFIG_COMMAND_SCHEMA + schema=schema.CONFIG_COMMAND_SCHEMA, + doc_link="https://qchatgpt.rockchin.top/config/function/command.html" ) ap.settings_mgr.register_manager( name="pipeline.json", description="消息处理流水线配置", manager=ap.pipeline_cfg, - schema=schema.CONFIG_PIPELINE_SCHEMA + schema=schema.CONFIG_PIPELINE_SCHEMA, + doc_link="https://qchatgpt.rockchin.top/config/function/pipeline.html" ) ap.settings_mgr.register_manager( name="platform.json", description="消息平台配置", manager=ap.platform_cfg, - schema=schema.CONFIG_PLATFORM_SCHEMA + schema=schema.CONFIG_PLATFORM_SCHEMA, + doc_link="https://qchatgpt.rockchin.top/config/function/platform.html" ) ap.settings_mgr.register_manager( name="provider.json", description="大模型能力配置", manager=ap.provider_cfg, - schema=schema.CONFIG_PROVIDER_SCHEMA + schema=schema.CONFIG_PROVIDER_SCHEMA, + doc_link="https://qchatgpt.rockchin.top/config/function/provider.html" ) ap.settings_mgr.register_manager( name="system.json", description="系统配置", manager=ap.system_cfg, - schema=schema.CONFIG_SYSTEM_SCHEMA + schema=schema.CONFIG_SYSTEM_SCHEMA, + doc_link="https://qchatgpt.rockchin.top/config/function/system.html" ) ap.plugin_setting_meta = await config.load_json_config("plugins/plugins.json", "templates/plugin-settings.json") diff --git a/web/package-lock.json b/web/package-lock.json index a1b258ae..d692c64b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -19,6 +19,7 @@ "axios": "^1.7.7", "codemirror": "^5.65.18", "core-js": "^3.37.1", + "json-editor-vue": "^0.17.3", "roboto-fontface": "*", "vue": "^3.4.31", "vuedraggable": "^4.1.0", @@ -43,6 +44,19 @@ "vue-router": "^4.4.0" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@antfu/utils": { "version": "0.7.10", "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", @@ -100,6 +114,99 @@ "node": ">=6.9.0" } }, + "node_modules/@codemirror/autocomplete": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.2.tgz", + "integrity": "sha512-wJGylKtMFR/Ds6Gh01+OovXE/pncPiKZNNBKuC39pKnH+XK5d9+WsNqcrdxPjFPFTigRBqse0rfxw9UxrfyhPg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.7.1.tgz", + "integrity": "sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", + "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.3.tgz", + "integrity": "sha512-kDqEU5sCP55Oabl6E7m5N+vZRoc0iWqgDVhEKifcHzPzjqCegcO4amfrYVL9PmPZpl4G0yjkpTpUO/Ui8CzO8A==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.1.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.2.tgz", + "integrity": "sha512-PDFG5DjHxSEjOXk9TQYYVjZDqlZTFaDBfhQixHnQOEVDDNHUbEh/hstAjcQJaA6FQdZTD1hquXTK0rVBLADR1g==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.7.tgz", + "integrity": "sha512-6+iLsXvITWKHYlkgHPCs/qiX4dNzn8N78YfhOFvPtPYCkuXqZq10rAfsUMhOq7O/1VjJqdXRflyExlfVcu/9VQ==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", + "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==", + "license": "MIT" + }, + "node_modules/@codemirror/view": { + "version": "6.34.2", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.34.2.tgz", + "integrity": "sha512-d6n0WFvL970A9Z+l9N2dO+Hk9ev4hDYQzIx+B9tCyBP0W5wPEszi1rhuyFesNSkLZzXbQE5FPH7F/z/TMJfoPA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.4.0", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -552,6 +659,39 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", + "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.6.0.tgz", + "integrity": "sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz", + "integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==", + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -590,12 +730,78 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "license": "MIT" }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsep-plugin/assignment": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsep-plugin/assignment/-/assignment-1.3.0.tgz", + "integrity": "sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, + "node_modules/@jsep-plugin/regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.4.tgz", + "integrity": "sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + }, + "peerDependencies": { + "jsep": "^0.4.0||^1.0.0" + } + }, "node_modules/@json-layout/core": { "version": "0.32.1", "resolved": "https://registry.npmjs.org/@json-layout/core/-/core-0.32.1.tgz", @@ -660,6 +866,15 @@ } } }, + "node_modules/@jsonquerylang/jsonquery": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jsonquerylang/jsonquery/-/jsonquery-3.1.1.tgz", + "integrity": "sha512-P6Qo5egd3W8TBpqQsqaZtZ9lPO7oXBM21QdkYamCAYZHv9VCPXiI8NeIuSoXdoe5zKVZPUWmqaI14uacJLmcNw==", + "license": "ISC", + "bin": { + "jsonquery": "bin/cli.js" + } + }, "node_modules/@koumoul/vjsf": { "version": "3.0.0-beta.46", "resolved": "https://registry.npmjs.org/@koumoul/vjsf/-/vjsf-3.0.0-beta.46.tgz", @@ -676,6 +891,41 @@ "vuetify": "^3.6.13" } }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "license": "MIT" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.2.tgz", + "integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, "node_modules/@mdi/font": { "version": "7.4.47", "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz", @@ -720,6 +970,313 @@ "node": ">= 8" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@replit/codemirror-indentation-markers": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@replit/codemirror-indentation-markers/-/codemirror-indentation-markers-6.5.3.tgz", + "integrity": "sha512-hL5Sfvw3C1vgg7GolLe/uxX5T3tmgOA3ZzqlMv47zjU1ON51pzNWiVbS22oh6crYhtVhv8b3gdXwoYp++2ilHw==", + "license": "MIT", + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, "node_modules/@rollup/pluginutils": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", @@ -958,11 +1515,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@sphinxxxx/color-conversion": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz", + "integrity": "sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==", + "license": "ISC" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/json5": { @@ -1259,7 +1821,6 @@ "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -1278,6 +1839,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-typescript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz", + "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==", + "license": "MIT", + "peerDependencies": { + "acorn": ">=8.9.0" + } + }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -1389,6 +1959,15 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -1576,6 +2155,15 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1751,6 +2339,17 @@ "integrity": "sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==", "license": "MIT" }, + "node_modules/codemirror-wrapped-line-indent": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/codemirror-wrapped-line-indent/-/codemirror-wrapped-line-indent-1.0.8.tgz", + "integrity": "sha512-5UwuHCz4oAZuvot1DbfFxSxJacTESdNGa/KpJD7HfpVpDAJdgB1vV9OG4b4pkJqPWuOfIpFLTQEKS85kTpV+XA==", + "license": "MIT", + "peerDependencies": { + "@codemirror/language": "^6.9.0", + "@codemirror/state": "^6.2.1", + "@codemirror/view": "^6.17.1" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1805,6 +2404,12 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1962,6 +2567,28 @@ "node": ">=0.4.0" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -2652,6 +3279,12 @@ "dev": true, "license": "MIT" }, + "node_modules/esm-env": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.1.4.tgz", + "integrity": "sha512-oO82nKPHKkzIj/hbtuDYy/JHqBHFlMIW36SDiPCVsj87ntDLcWN+sJ1erdVryd4NxODacFTsdrIE3b7IamqbOg==", + "license": "MIT" + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -2683,6 +3316,16 @@ "node": ">=0.10" } }, + "node_modules/esrap": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.2.2.tgz", + "integrity": "sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1" + } + }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -3237,9 +3880,14 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "devOptional": true, "license": "MIT" }, + "node_modules/immutable-json-patch": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/immutable-json-patch/-/immutable-json-patch-6.0.1.tgz", + "integrity": "sha512-BHL/cXMjwFZlTOffiWNdY8ZTvNyYLrutCnWxrcKPHr5FqpAb6vsO6WWSPnVSys3+DruFN6lhHJJPHi8uELQL5g==", + "license": "ISC" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -3510,6 +4158,15 @@ "node": ">=8" } }, + "node_modules/is-reference": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", + "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -3636,6 +4293,15 @@ "node": ">=10" } }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3649,6 +4315,15 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsep": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.4.0.tgz", + "integrity": "sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==", + "license": "MIT", + "engines": { + "node": ">= 10.16.0" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -3656,12 +4331,64 @@ "dev": true, "license": "MIT" }, + "node_modules/json-editor-vue": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/json-editor-vue/-/json-editor-vue-0.17.3.tgz", + "integrity": "sha512-MVpD3TInIlruq9ye/J3XmYHTH+pqfyW0E1GVUV2ug5M0X/19zGslJ+FgeikDflvTVUxhVuCEnc9spMYmPj5Lyw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "vanilla-jsoneditor": "^2.0.0", + "vue-demi": "^0.14.10" + }, + "peerDependencies": { + "@vue/composition-api": ">=1", + "vue": "2||3" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/json-editor-vue/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "node_modules/json-source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/json-source-map/-/json-source-map-0.6.1.tgz", + "integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==", + "license": "MIT" + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -3682,6 +4409,33 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonpath-plus": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-10.1.0.tgz", + "integrity": "sha512-gHfV1IYqH8uJHYVTs8BJX1XKy2/rR93+f8QQi0xhx95aCiXn1ettYAd5T+7FU6wfqyDoX/wy0pm/fL3jOKJ9Lg==", + "license": "MIT", + "dependencies": { + "@jsep-plugin/assignment": "^1.2.1", + "@jsep-plugin/regex": "^1.0.3", + "jsep": "^1.3.9" + }, + "bin": { + "jsonpath": "bin/jsonpath-cli.js", + "jsonpath-plus": "bin/jsonpath-cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/jsonrepair": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.10.0.tgz", + "integrity": "sha512-0Ex64Exiw0rPUEcSbhPN0ae4/5D0DZLIob9yagAF1OG5iU0mP+/t7q4gcxtQdn6i7FuQy2J/w1XbOdu/uhGV0w==", + "license": "ISC", + "bin": { + "jsonrepair": "bin/cli.js" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3732,6 +4486,12 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3755,6 +4515,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -3829,6 +4595,12 @@ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "license": "MIT" }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3843,7 +4615,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -3940,6 +4712,19 @@ "dev": true, "license": "MIT" }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4741,6 +5526,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -4766,6 +5557,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svelte": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.1.13.tgz", + "integrity": "sha512-xVNk8yLsZNfkyqWzVg8+nfU9ewiSjVW0S4qyTxfKa6Y7P5ZBhA+LDsh2cHWIXJQMltikQAk6W3sqGdQZSH58PA==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "acorn-typescript": "^1.4.13", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "esm-env": "^1.0.0", + "esrap": "^1.2.2", + "is-reference": "^3.0.2", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5118,6 +5933,98 @@ "dev": true, "license": "MIT" }, + "node_modules/vanilla-jsoneditor": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/vanilla-jsoneditor/-/vanilla-jsoneditor-2.0.2.tgz", + "integrity": "sha512-/qsSp2B/sQsyW7SO8wq7vvEeq4Wqs5JT1j5SSfqlID7CPB9S95+vhzUjrKMpUk0YtxsySlxDecAcXK5Lzg22Sw==", + "license": "ISC", + "dependencies": { + "@codemirror/autocomplete": "^6.18.1", + "@codemirror/commands": "^6.7.1", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/language": "^6.10.3", + "@codemirror/lint": "^6.8.2", + "@codemirror/search": "^6.5.6", + "@codemirror/state": "^6.4.1", + "@codemirror/view": "^6.34.1", + "@fortawesome/free-regular-svg-icons": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@jsonquerylang/jsonquery": "^3.1.1", + "@lezer/highlight": "^1.2.1", + "@replit/codemirror-indentation-markers": "^6.5.3", + "ajv": "^8.17.1", + "codemirror-wrapped-line-indent": "^1.0.8", + "diff-sequences": "^29.6.3", + "immutable-json-patch": "^6.0.1", + "jmespath": "^0.16.0", + "json-source-map": "^0.6.1", + "jsonpath-plus": "^9.0.0 || ^10.1.0", + "jsonrepair": "^3.0.0", + "lodash-es": "^4.17.21", + "memoize-one": "^6.0.0", + "natural-compare-lite": "^1.4.0", + "sass": "^1.80.4", + "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0", + "vanilla-picker": "^2.12.3" + } + }, + "node_modules/vanilla-jsoneditor/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/vanilla-jsoneditor/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "license": "MIT", + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/vanilla-jsoneditor/node_modules/sass": { + "version": "1.80.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.6.tgz", + "integrity": "sha512-ccZgdHNiBF1NHBsWvacvT5rju3y1d/Eu+8Ex6c21nHp2lZGLBEtuwc415QfiI1PJa1TpCo3iXwwSRjRpn2Ckjg==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/vanilla-picker": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/vanilla-picker/-/vanilla-picker-2.12.3.tgz", + "integrity": "sha512-qVkT1E7yMbUsB2mmJNFmaXMWE2hF8ffqzMMwe9zdAikd8u2VfnsVY2HQcOUi2F38bgbxzlJBEdS1UUhOXdF9GQ==", + "license": "ISC", + "dependencies": { + "@sphinxxxx/color-conversion": "^2.2.2" + } + }, "node_modules/vite": { "version": "5.4.8", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", @@ -5327,6 +6234,12 @@ "vue": "^3.0.2" } }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, "node_modules/webpack-virtual-modules": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", @@ -5439,6 +6352,12 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zimmerframe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", + "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", + "license": "MIT" } } } diff --git a/web/package.json b/web/package.json index 2420dd2b..fa14b869 100644 --- a/web/package.json +++ b/web/package.json @@ -19,6 +19,7 @@ "axios": "^1.7.7", "codemirror": "^5.65.18", "core-js": "^3.37.1", + "json-editor-vue": "^0.17.3", "roboto-fontface": "*", "vue": "^3.4.31", "vuedraggable": "^4.1.0", diff --git a/web/src/pages/Settings.vue b/web/src/pages/Settings.vue index adc40b57..e5d6d73b 100644 --- a/web/src/pages/Settings.vue +++ b/web/src/pages/Settings.vue @@ -45,10 +45,10 @@
-