+
@@ -8,7 +9,7 @@
API连接问题
{{ appStore.apiMessage || '无法连接到后端服务' }}
-
请确保后端服务正在运行 (端口: 8000)
+
请确保后端服务正在运行
@@ -18,4 +19,4 @@
import { useAppStore } from '~/stores/app'
const appStore = useAppStore()
-
\ No newline at end of file
+
diff --git a/frontend/components/BizMessages.vue b/frontend/components/BizMessages.vue
new file mode 100644
index 0000000..2770b77
--- /dev/null
+++ b/frontend/components/BizMessages.vue
@@ -0,0 +1,394 @@
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+ {{ (item.name || item.username).charAt(0).toUpperCase() }}
+
+
+
+
+
{{ item.name || item.username }}
+
+ {{ item.formatted_last_time }}
+
+
+
+
+ {{ {1: '服务号', 0: '公众号', 2: '企业号', 3: '未知'}[item.type] || '未知' }}
+
+
+
+
+
+
+
+
+
+
+
{{ selectedBizAccount.name }}
+
+
+
+
+
没有更多消息了
+
正在加载...
+
+
+
+
+
+
+
+
¥
+
{{ msg.merchant_name || '微信支付' }}
+
+
+
{{ msg.title }}
+
+
+ {{ msg.description }}
+
+
+ {{ msg.formatted_time }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/ChatLocationCard.vue b/frontend/components/ChatLocationCard.vue
new file mode 100644
index 0000000..1b9c0d2
--- /dev/null
+++ b/frontend/components/ChatLocationCard.vue
@@ -0,0 +1,276 @@
+
+
+
+
+
{{ primaryText }}
+
{{ secondaryText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/DesktopTitleBar.vue b/frontend/components/DesktopTitleBar.vue
index 7936d46..13f9c7c 100644
--- a/frontend/components/DesktopTitleBar.vue
+++ b/frontend/components/DesktopTitleBar.vue
@@ -60,7 +60,7 @@ const closeWindow = () => {
diff --git a/frontend/components/SidebarRail.vue b/frontend/components/SidebarRail.vue
new file mode 100644
index 0000000..c83b4d4
--- /dev/null
+++ b/frontend/components/SidebarRail.vue
@@ -0,0 +1,607 @@
+
+
+
+
+
+
+
+
+
正在加载账号信息...
+
+
+
+
+
+ 我
+
+
+
+
{{ selectedAccount || '未选择账号' }}
+
账号标识(wxid)
+
+
+
+
+
+ 数据库数量
+ {{ accountInfo?.database_count ?? '—' }}
+
+
+ 数据目录
+ {{ accountInfo?.path || (selectedAccount ? `output/databases/${selectedAccount}` : '—') }}
+
+
+ 最近会话库更新时间
+ {{ sessionUpdatedAtText }}
+
+
+
+
+
+ 仅删除本项目中的该账号解析数据/缓存/编辑记录,不会删除微信客户端中的任何聊天内容或账号数据。
+
+
+
+ {{ accountDeleteLoading ? '删除中...' : '删除当前账号的项目数据' }}
+
+
删除成功后将自动返回引导页。
+
+
{{ accountInfoError }}
+
{{ accountDeleteError }}
+
+
+
+
+
+
+
+
diff --git a/frontend/components/chat/ChatOverlays.vue b/frontend/components/chat/ChatOverlays.vue
new file mode 100644
index 0000000..d11d4bc
--- /dev/null
+++ b/frontend/components/chat/ChatOverlays.vue
@@ -0,0 +1,1628 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ previewVideoError }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 加载中...
+
+ 没有可显示的聊天记录
+
+
+
+
+
+
+ {{ (rec.senderDisplayName || rec.sourcename || '?').charAt(0) }}
+
+
+
+
+
+
+
+ {{ rec.senderDisplayName || rec.sourcename }}
+
+
+
+ {{ rec.fullTime || rec.sourcetime }}
+
+
+
+
+
+
+
+
{{ rec.title || '聊天记录' }}
+
+
+
+ 聊天记录
+
+
+
+
+
+
+
{{ rec.title || rec.content || rec.url || '链接' }}
+
+
{{ rec.content }}
+
+
+
+
+
+
+
+
{{ getChatHistoryLinkFromAvatarText(rec) || '\u200B' }}
+
+
+
{{ getChatHistoryLinkFromText(rec) || '\u200B' }}
+
+
+
+
+
+
+
{{ rec.content || '[图片]' }}
+
+
+
+
+
+
{{ rec.content || '[表情]' }}
+
+
+
+
+
+
{{ rec.content || '[视频]' }}
+
+
+
+
+ {{ formatChatHistoryVideoDuration(rec.videoDuration) }}
+
+
+
+
+
+
+ {{ seg.content }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ win.title || win.url || '链接' }}
+
+
{{ win.content }}
+
+
+
+
+
+
+
+
{{ getChatHistoryLinkFromAvatarText(win) || '\u200B' }}
+
+
+
{{ getChatHistoryLinkFromText(win) || '\u200B' }}
+
+
+
+
解析中...
+
{{ win.url }}
+
+
+ 在浏览器打开
+
+
+ 复制链接
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 没有可显示的聊天记录
+
+
+
+
+
+
+ {{ (rec.senderDisplayName || rec.sourcename || '?').charAt(0) }}
+
+
+
+
+
+
+
+ {{ rec.senderDisplayName || rec.sourcename }}
+
+
+
+ {{ rec.fullTime || rec.sourcetime }}
+
+
+
+
+
+
+
+
{{ rec.title || '聊天记录' }}
+
+
+
+ 聊天记录
+
+
+
+
+
+
+
{{ rec.title || rec.content || rec.url || '链接' }}
+
+
{{ rec.content }}
+
+
+
+
+
+
+
+
{{ getChatHistoryLinkFromAvatarText(rec) || '\u200B' }}
+
+
+
{{ getChatHistoryLinkFromText(rec) || '\u200B' }}
+
+
+
+
+
+
+
{{ rec.content || '[视频]' }}
+
+
+
+
+
+
+ {{ formatChatHistoryVideoDuration(rec.videoDuration) }}
+
+
+
+
+
+
+
{{ rec.content || '[图片]' }}
+
+
+
+
+
+
{{ rec.content || '[表情]' }}
+
+
+
+
+
+
+
+
+ {{ rec.quote?.kind === 'video' ? '视频' : (rec.quote?.kind === 'image' ? '图片' : '表情') }}
+
+
+
+
+ {{ rec.quote?.label || (rec.quote?.kind === 'video' ? '[视频]' : (rec.quote?.kind === 'image' ? '[图片]' : '[表情]')) }}
+
+
+
+ {{ formatChatHistoryVideoDuration(rec.quote.duration) }}
+
+
+
+
+
+ {{ seg.content }}
+
+
+
+
+
+
+
+
+ {{ seg.content }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ messageEditModal.mode === 'content' ? '修改消息' : '编辑源码' }}
+
+
+
+
+
+
+
+
+
{{ messageEditModal.error }}
+
加载中…
+
+
+
+
+ 查看源消息(raw)
+
+
{{ prettyJson(messageEditModal.rawRow) }}
+
+
+
+
+
+ 取消
+
+ 保存
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ messageFieldsModal.error }}
+
加载中…
+
+
+
+
+ 我已知风险(允许修改 local_id / WCDB_CT / BLOB 等)
+
+
修改时间/类型会自动同步 message_resource 关键字段
+
+
+
+
+
+ 查看源消息(raw)
+
+
{{ prettyJson(messageFieldsModal.rawRow) }}
+
+
+
+
+
+ 取消
+
+ 保存
+
+
+
+
+
+
+
+
+
+
+
导出聊天记录(离线 ZIP)
+
+
+
+
+
+
+
+
+
{{ exportError }}
+
+ 已开启隐私模式:导出将隐藏会话/用户名/内容,并且不会打包头像与媒体。
+
+
+
+
+
+
范围
+
+
+ 当前会话
+
+
+ 全部 {{ exportContactCounts.total }}
+
+
+ 群聊 {{ exportContactCounts.groups }}
+
+
+ 单聊 {{ exportContactCounts.singles }}
+
+
+
+
+
+
+
+
+
+
+
HTML 选项
+
+
+
+ 允许联网下载链接/引用缩略图(提高离线完整性)
+
+
+ 仅 HTML 生效;会在导出时尝试下载远程缩略图并写入 ZIP(已做安全限制)。隐私模式下自动忽略。
+
+
+
+
每页消息数
+
+
推荐 1000;0=单文件(打开大聊天可能很卡)
+
+
+
+
+
+
+ 点击上方范围可筛选并默认全选当前结果,再次点击可取消全选;下方整行可点选会话
+
+
+
+
+
+
+
+
+
+
+ {{ (c.name || c.username || '?').charAt(0) }}
+
+
+
+
+ {{ c.name }}
+ {{ c.isGroup ? '(群)' : '' }}
+
+
{{ c.username }}
+
+
+
+ 无匹配会话
+
+
+
+ 已选 {{ exportSelectedUsernames.length }} 个会话
+
+
+
+
+
消息类型(导出内容)
+
+
+
x.value)"
+ >
+ 全选
+
+
+ 只语音
+
+
+ 只转账
+
+
+ 只红包
+
+
已选 {{ exportMessageTypes.length }} 项
+
+
+
+
+ {{ opt.label }}
+
+
+
+
+ 仅导出已勾选的消息类型;勾选图片/表情/视频/语音/文件时,会导出对应多媒体文件。
+
+
+
+
+
+
+
+
导出目录
+
+
+ {{ exportFolder || '未选择' }}
+
+
+ 选择文件夹
+
+
+ 清空
+
+
+
桌面端与支持 File System Access API 的浏览器均可选择目录。
+
+
+
+
+
+
+
任务:{{ exportJob.exportId }}
+
状态:{{ exportJob.status }}
+
+
+
+
会话:{{ exportJob.progress?.conversationsDone || 0 }}/{{ exportJob.progress?.conversationsTotal || 0 }}
+
{{ exportOverallPercent }}%
+
+
+
+
+
+
+ 当前:{{ exportJob.progress?.currentConversationName || exportJob.progress?.currentConversationUsername }}
+ ({{ exportJob.progress?.currentConversationMessagesExported || 0 }}/{{ exportJob.progress?.currentConversationMessagesTotal || 0 }})
+
+
+ {{ exportCurrentPercent }}%
+ …
+
+
+
+
+
+
消息:{{ exportJob.progress?.messagesExported || 0 }};媒体:{{ exportJob.progress?.mediaCopied || 0 }};缺失:{{ exportJob.progress?.mediaMissing || 0 }}
+
+
+
+
+ {{ exportSaveBusy ? '保存中...' : '保存到已选目录' }}
+
+
+ 下载 ZIP
+
+
+ 取消任务
+
+
+
{{ exportSaveMsg }}
+
+
+ {{ exportJob.error || '导出失败' }}
+
+
+
+
+
+
+ 关闭
+
+
+ {{ isExportCreating ? '创建中...' : '开始导出' }}
+
+
+
+
+
+
+
diff --git a/frontend/components/chat/ConversationPane.vue b/frontend/components/chat/ConversationPane.vue
new file mode 100644
index 0000000..dc2998f
--- /dev/null
+++ b/frontend/components/chat/ConversationPane.vue
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+ {{ searchContextBannerText }}
+
+
+
+ 退出定位
+
+
+ 返回最新
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
选择一个会话
+
+ 从左侧列表选择联系人查看聊天记录
+
+
+
+
+
+
+
diff --git a/frontend/components/chat/FileTypeIcon.vue b/frontend/components/chat/FileTypeIcon.vue
new file mode 100644
index 0000000..69f7732
--- /dev/null
+++ b/frontend/components/chat/FileTypeIcon.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+ PPT
+
+
+
+
+ TXT
+
+
+
+
+
+
+
diff --git a/frontend/components/chat/LinkCard.vue b/frontend/components/chat/LinkCard.vue
new file mode 100644
index 0000000..1599be2
--- /dev/null
+++ b/frontend/components/chat/LinkCard.vue
@@ -0,0 +1,352 @@
+
diff --git a/frontend/components/chat/MessageContent.vue b/frontend/components/chat/MessageContent.vue
new file mode 100644
index 0000000..80dc364
--- /dev/null
+++ b/frontend/components/chat/MessageContent.vue
@@ -0,0 +1,316 @@
+
+
+
+
+
+ {{ message.title || message.content || '文件' }}
+ {{ formatFileSize(message.fileSize) }}
+
+
+
+
+
+
微信电脑版
+
+
+
+
+
+
+ {{ message.content }}
+
+
+
+
+
+
+
+ {{ message.content }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ getVoiceDurationInSeconds(message.voiceDuration) }}"
+
+
+
+
+
+
+
+
+
+
{{ message.content || '通话' }}
+
+
+
+
+
+
+ {{ message._emojiDownloading ? '下载中...' : (message._emojiDownloaded ? '已下载' : '下载') }}
+
+
+
+ {{ message.content }}
+
+
+
+
+
+ {{ seg.content }}
+
+
+
+
+
+
+
{{ message.quoteTitle }}:
+
+
+
+
+
+
+ {{ getVoiceDurationInSeconds(message.quoteVoiceLength) }}"
+ 语音
+
+
+
+
+
+
+ {{ message.quoteTitle }}:
+
+ 🔗 {{ getQuotedLinkText(message) }}
+
+
+
+
+
+ {{ message.quoteTitle }}:
+
+ {{ message.quoteContent }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ message.title || '聊天记录' }}
+
+
+
+ 聊天记录
+
+
+
+
+
+
+
+
+
+ ¥{{ formatTransferAmount(message.amount) }}
+ {{ getTransferTitle(message) }}
+
+
+
+ 微信转账
+
+
+
+
+
+
+
+
+ {{ getRedPacketText(message) }}
+ 已领取
+
+
+
+ 微信红包
+
+
+
+
+
+
+
+
+ {{ seg.content }}
+
+
+
+
+
+
+ {{ message.content || ('[' + (message.type || 'unknown') + '] 消息组件已移除') }}
+
+
+
+
diff --git a/frontend/components/chat/MessageItem.vue b/frontend/components/chat/MessageItem.vue
new file mode 100644
index 0000000..cab78d9
--- /dev/null
+++ b/frontend/components/chat/MessageItem.vue
@@ -0,0 +1,185 @@
+
+
+
+
+ {{ message.timeDivider }}
+
+
+
+
+
+ {{ message.content }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ message.sender.charAt(0) }}
+
+
+
+
+
+
+
+
+ {{ message.senderDisplayName }}
+
+
+ {{ message.fullTime }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/chat/MessageList.vue b/frontend/components/chat/MessageList.vue
new file mode 100644
index 0000000..c3e0b9f
--- /dev/null
+++ b/frontend/components/chat/MessageList.vue
@@ -0,0 +1,48 @@
+
+
+
+
+ {{ isLoadingMessages ? '加载中...' : '继续上滑加载更多' }}
+
+
+
+
+ 加载中...
+
+
+ {{ messagesError }}
+
+
+ 暂无聊天记录
+
+
+
+
+
+
+
diff --git a/frontend/components/chat/SessionListPanel.vue b/frontend/components/chat/SessionListPanel.vue
new file mode 100644
index 0000000..95920e6
--- /dev/null
+++ b/frontend/components/chat/SessionListPanel.vue
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ chatAccounts.loading ? '加载中...' : (chatAccounts.error || '无数据库') }}
+ {{ acc }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/cards/Card00GlobalOverview.vue b/frontend/components/wrapped/cards/Card00GlobalOverview.vue
new file mode 100644
index 0000000..d9c483b
--- /dev/null
+++ b/frontend/components/wrapped/cards/Card00GlobalOverview.vue
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+ 这一年,你在微信里发送了
+ {{ formatInt(totalMessages) }}
+ 条消息,平均每天
+ {{ formatFloat(messagesPerDay, 1) }}
+ 条。
+
+
+ 这一年,你在微信里还没有发出聊天消息——也许,你把时间留给了更重要的人和事。
+
+
+
+ 在与你相伴的
+ {{ formatInt(activeDays) }}
+ 天里,
+
+ 你总共加了
+ {{ formatInt(addedFriends) }}
+ 位好友,
+
+
+ 你最常在 {{ mostActiveWeekdayName }} 的
+ {{ mostActiveHour }}
+ 点出现。
+
+
+ 你留下了不少对话的痕迹。
+
+
+
+
+
+ 你发消息最多的人是
+ 「
+
+
+
+ {{ avatarFallback(topContact.displayName) }}
+
+
+ {{ topContact.displayName }}
+ 」
+ ({{ formatInt(topContact.messages) }} 条)
+
+ ,
+
+ 你最常发言的群是
+ 「
+
+
+
+ {{ avatarFallback(topGroup.displayName) }}
+
+
+ {{ topGroup.displayName }}
+ 」
+ ({{ formatInt(topGroup.messages) }} 条)
+
+ 。
+
+
+
+ 你更常用 {{ topKind.label }} 来表达({{ topKindPct }} %)。
+
+
+
+ 你说得最多的一句话是「{{ topPhrase.phrase }} 」({{ formatInt(topPhrase.count) }} 次)。
+
+
+ 愿你的每一句分享,都有人回应。
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/cards/Card01CyberSchedule.vue b/frontend/components/wrapped/cards/Card01CyberSchedule.vue
new file mode 100644
index 0000000..7bdf2e0
--- /dev/null
+++ b/frontend/components/wrapped/cards/Card01CyberSchedule.vue
@@ -0,0 +1,346 @@
+
+
+
+
+
+
+ 今年你没有发出聊天消息
+
+
+
+ 清晨
+ {{ pad2(mostActiveHour) }} :00,
+ 当城市还在沉睡,你已经开始了新一天的问候。
+ {{ mostActiveWeekdayName }}
+ 是你最健谈的一天,这一年你用
+ {{ formatInt(totalMessages) }}
+ 条消息记录了这些早起时光。
+
+
+
+ 忙碌的上午
+ {{ pad2(mostActiveHour) }} :00,
+ 是你最常敲击键盘的时刻。
+ {{ mostActiveWeekdayName }}
+ 最活跃,这一年你用
+ {{ formatInt(totalMessages) }}
+ 条消息把工作与生活都留在了对话里。
+
+
+
+ 午后的阳光里,
+ {{ pad2(mostActiveHour) }} :00
+ 是你最爱分享的时刻。
+ {{ mostActiveWeekdayName }}
+ 的聊天最热闹,这一年共
+ {{ formatInt(totalMessages) }}
+ 条消息串起了 你的午后时光。
+
+
+
+ 夜幕降临,
+ {{ pad2(mostActiveHour) }} :00
+ 是你最常出没的时刻。
+ {{ mostActiveWeekdayName }}
+ 最活跃,这一年
+ {{ formatInt(totalMessages) }}
+ 条消息陪你把每个夜晚都聊得更亮。
+
+
+
+ 当世界沉睡,凌晨
+ {{ pad2(mostActiveHour) }} :00
+ 的你依然在线。
+ {{ mostActiveWeekdayName }}
+ 最活跃,这一年
+ {{ formatInt(totalMessages) }}
+ 条深夜消息,是你与这个世界的悄悄话。
+
+
+
+ 你在
+ {{ pad2(mostActiveHour) }} :00
+ 最活跃
+
+
+
+
+
+ 最先想起的是「{{ earliestSent.displayName }} 」,
+ 最后放不下的也还是「{{ earliestSent.displayName }} 」。
+
+
+
+ 在 {{ earliestDateLabel }},最早的一条发给了「{{ earliestSent.displayName }} 」,
+ 最晚的一条发给了「{{ latestSent.displayName }} 」。
+
+
+ 最早的一条发给了
+ {{ earliestSent.displayName }} ,
+ 最晚的一条发给了
+ {{ latestSent.displayName }} 。
+
+
+ 最早的一条({{ earliestDateLabel }})发给了「{{ earliestSent.displayName }} 」,
+ 最晚的一条({{ latestDateLabel }})发给了「{{ latestSent.displayName }} 」。
+
+
+ 最早的收件人是「{{ earliestSent.displayName }} 」({{ earliestDateLabel }}),
+ 最晚的收件人是「{{ latestSent.displayName }} 」({{ latestDateLabel }})。
+
+
+ 在 {{ earliestDateLabel }},你把消息发给了「{{ earliestSent.displayName }} 」;
+ 在 {{ latestDateLabel }},你又发给了「{{ latestSent.displayName }} 」。
+
+
+ 最早与最晚,分别写给了「{{ earliestSent.displayName }} 」({{ earliestDateLabel }})
+ 和「{{ latestSent.displayName }} 」({{ latestDateLabel }})。
+
+
+ 最早的一条落在 {{ earliestDateLabel }},发给了「{{ earliestSent.displayName }} 」;
+ 最晚的一条落在 {{ latestDateLabel }},发给了「{{ latestSent.displayName }} 」。
+
+
+
+
+
+
+
+ 今年的第一条消息({{ yearFirstDateLabel }} {{ yearFirstSent.time }} )发给了
+ {{ yearFirstSent.displayName }} :「{{ yearFirstSent.content || '...' }} 」;
+ 最后一条消息({{ yearLastDateLabel }} {{ yearLastSent.time }} )发给了
+ {{ yearLastSent.displayName }} :「{{ yearLastSent.content || '...' }} 」 。
+
+ ——从年初到年末,始终如一。
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/cards/Card02MessageChars.vue b/frontend/components/wrapped/cards/Card02MessageChars.vue
new file mode 100644
index 0000000..bb5fd0a
--- /dev/null
+++ b/frontend/components/wrapped/cards/Card02MessageChars.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ 这一年,你在微信里敲下了
+ {{ formatInt(sentChars) }}
+ 个字。
+
+
+ 这一年,你还没有发出文字消息。
+
+
+
+ 你也收到了
+ {{ formatInt(receivedChars) }}
+ 个字。
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/cards/Card03ReplySpeed.vue b/frontend/components/wrapped/cards/Card03ReplySpeed.vue
new file mode 100644
index 0000000..4a06a62
--- /dev/null
+++ b/frontend/components/wrapped/cards/Card03ReplySpeed.vue
@@ -0,0 +1,873 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ avatarFallback(bestBuddy?.displayName) }}
+
+
+
+ {{ bestBuddy?.displayName || '' }}
+
+
+
+
+
+
+
+
+
+ {{ avatarFallback(seg.contact?.displayName) }}
+
+
+
+ {{ seg.contact?.displayName || '' }}
+
+
+
+
+
+
+ {{ segTextShown(i) }}
+
+ {{ segTextShown(i) }}
+
+
+
+
+
+
+
+
+
+
+
+
如何生成本页数据
+
+
本页需要使用“消息搜索索引”来合并所有消息分片并计算回复耗时。
+
+ 索引正在构建中:已索引
+ {{ formatInt(indexBuild.indexedMessages) }}
+ 条消息。
+ (当前:{{ indexBuild.currentConversation }})
+
+
+ 索引构建失败:{{ indexBuild.error || '未知错误' }}
+
+
+ 你可以先在「聊天记录搜索」中构建索引(或调用后端接口
+ /api/chat/search-index/build),
+ 然后回到这里点击左上角“强制刷新”或本页“重试”。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
年度聊天排行(我发 + 对方)
+
+ {{ raceDate }}
+ · 0.1秒/天
+
+
+
+
+
+ 我发
+
+
+
+ 对方
+
+
+
+
+
+ 暂无可展示的排行榜数据。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/cards/Card04EmojiUniverse.vue b/frontend/components/wrapped/cards/Card04EmojiUniverse.vue
new file mode 100644
index 0000000..9b9fd9b
--- /dev/null
+++ b/frontend/components/wrapped/cards/Card04EmojiUniverse.vue
@@ -0,0 +1,795 @@
+
+
+
+
+
+
+
+
+
+
+ {{ seg.content }}
+
+ {{ seg.content }}
+
+
+
+
+
+
+
+
+
+
高频表情卡堆(Vue Bits)
+
+
+
+
+
+ {{ formatInt(Number(stackTopCount || 0)) }} 次
+
+
+
+
+
+
+ {{ avatarFallback(heroStickerOwnerName) }}
+
+
+
+ 常发送给 {{ heroStickerOwnerName || '未知' }}
+
+
+
+
拖动表情卡片翻一翻
+
+
+
+ 暂无可展示的高频表情图片。
+
+
+
+
+
+
Emoji Top(小黄脸 + Unicode)
+
+
+ 小黄脸
+
+
+ Unicode
+
+
+
+
+
+
+
+
+
+
+ {{ row.kind === 'unicode' ? row.label : '🙂' }}
+
+
+
+ {{ formatInt(row.count) }}
+
+
+
+
+
+ 今年没有统计到可识别的 Emoji(小黄脸/Unicode)。
+
+
+
+
+
+
+
斗图热力时段
+
+
+ 高峰在
+ {{ peakWeekdayName }} {{ peakHour }}:00
+
+
+ 今年没有明显的斗图高峰时段
+
+
+
+
+
+ 00
+ 06
+ 12
+ 18
+ 23
+
+
+
+
表情新鲜度
+
+
+
+
年度新解锁
+
+ {{ formatInt(newStickerCountThisYear) }}
+
+
+ 占类型 {{ newStickerSharePct }}%
+
+
+
+
+
+
+
+
+
+
+
+
回温表情
+
+ {{ formatInt(revivedStickerCount) }}
+
+
+ 间隔≥{{ formatInt(revivedMinGapDays) }}天,最长 {{ formatInt(revivedMaxGapDays) }} 天
+
+
+
+
+
+
+
+
+
+
+
+ 今年共用过 {{ formatInt(uniqueStickerTypeCount) }} 种表情,
+ 回温占比 {{ revivedStickerSharePct }} %。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/cards/Card04MonthlyBestFriendsWall.vue b/frontend/components/wrapped/cards/Card04MonthlyBestFriendsWall.vue
new file mode 100644
index 0000000..e30a888
--- /dev/null
+++ b/frontend/components/wrapped/cards/Card04MonthlyBestFriendsWall.vue
@@ -0,0 +1,434 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
onPointerDown(e, index)"
+ @pointermove="onPointerMove"
+ @pointerup="onPointerUp"
+ @pointercancel="onPointerUp"
+ >
+
+
+
+
+
+
+
+ {{ avatarFallback(item.winner.displayName) }}
+
+
+
+
+
+
+
+ {{ item.winner.displayName }}
+
+
+
+ {{ item.month }}月
+
+
+
+ 综合分 {{ formatScore(item.winner.score100) }}
+
+
+
+
+
+
+ {{ metric.label }}
+ {{ metric.pct }}
+
+
+
+
+
+
+
+
+
+ 共 {{ formatInt(item?.raw?.totalMessages) }} 条 ·
+ 互动 {{ formatInt(item?.raw?.interaction) }} 次 ·
+ 活跃 {{ formatInt(item?.raw?.activeDays) }} 天
+
+
+
+
+
+
+
+ 〜
+
+
+
+
本月静悄悄
+
+ {{ item.month }}月
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/cards/Card06KeywordsWordCloud.vue b/frontend/components/wrapped/cards/Card06KeywordsWordCloud.vue
new file mode 100644
index 0000000..427e2ec
--- /dev/null
+++ b/frontend/components/wrapped/cards/Card06KeywordsWordCloud.vue
@@ -0,0 +1,861 @@
+
+
+
+
+
+
+
+ 点击空白处加速
+
+
+
+
+
+
+
+
+
+ {{ seg.content }}
+
+
+
+ {{ b.text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 你的话,正在涌来。
+
+
+ 这一年,你一共发出了 {{ card.data?.meta?.matchedCandidates || 0 }} 句简短的表达,其中 {{ card.data?.meta?.uniquePhrases || 0 }} 句话成了你的专属口头禅。
+
+ 「{{ card.data.topKeyword.word }} 」是你最常说的话,足足被你重复了 {{ card.data.topKeyword.count }} 次。
+
+ 点击气泡,找回当时的心情。
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/cards/Card07BentoSummary.vue b/frontend/components/wrapped/cards/Card07BentoSummary.vue
new file mode 100644
index 0000000..d399942
--- /dev/null
+++ b/frontend/components/wrapped/cards/Card07BentoSummary.vue
@@ -0,0 +1,2794 @@
+
+
+
+
+
+
+
+
+
+ ✈
+ 发送消息条数
+
+
+
+
+
+
+ {{ formatInt(totalMessages) }}
+ 条
+
+
+ 平均每天发送 {{ formatInt(messagesPerDayRounded) }} 条
+
+
+
✈
+
+
+
+
+
+
+
+
+ ✍
+ 发送消息字数
+
+
+
+ {{ sentCharsWan }}
+ 万字
+
+
+ 📖
+ 相当于写了一本《了不起的盖茨比》
+
+
+
✍
+
+
+
+
+
+
+
+
+ ➕
+ 新加好友
+
+
+
+
+
+
+ {{ formatInt(addedFriends) }}
+ 位
+
+
扩列了全新的生活圈
+
+
➕
+
+
+
+
+
+
+
+
+ 🕒
+ 最常活跃时间
+
+
+
{{ mostActiveHourLabel }}
+
+ {{ mostActiveHourDesc }}
+
+
+
+
🕒
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 年度聊天搭子
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ avatarFallback(bestBuddyName) }}
+
+
+
+
+ MVP
+
+
+
+
+ {{ bestBuddyName }}
+
+
+
+
+
+
+
+
+
+
+
总互动
+
+ {{ formatInt(bestBuddyTotal) }}
+ 次
+
+
+
+
+
+
+
+
+
+
+
最长连聊
+
+ {{ bestBuddyStreakDaysLabel }}
+ 天
+
+
+
+
+
+
+
+
+
+
+
同频时刻
+
+ {{ bestBuddyPeakLabel }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 👥
+ 最爱群聊
+
+
+
+
+
+
+
+
+ {{ avatarFallback(topGroupName) }}
+
+
+
+
+
+
{{ topGroupName }}
+
+ 🔥
+ 全年发了 {{ formatInt(topGroupMessages) }} 条
+
+
+
+
+
+
+
{{ topGroupSharePct }}%
+
占全年
+
+
+
+ 日均 {{ topGroupDailyLabel }} 条
+
+
+
+
👥
+
+
+
+
+
+
+
+
+ ⏱
+ 回复速度
+
+
+
+
+
中位数 P50
+
+ {{ replyP50Label }}
+
+
⚡
+
+
+
+
🚀
+
+ 🚀
+ 秒回
+
+
+
{{ fastestReplyLabel }}
+
+
+
+
+
+
+ {{ avatarFallback(fastestContactName) }}
+
+
+
+
+
+
+
+
🐌
+
+ 🐌
+ 意念
+
+
+
{{ slowestReplyLabel }}
+
+
+
+
+
+
+ {{ avatarFallback(slowestContactName) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ❝
+ 年度口头禅
+
+
+
+ "{{ topPhraseWord }}"
+
+
+
+ 说了 {{ formatInt(topPhraseCount) }} 次
+
+
❝
+
+
+
+
+
+
+
+
+ 🖼
+ 最爱表情包
+
+
+
+
+
+
+
+
+
+ 🧩
+
+
+
+
+
+
+ {{ formatInt(sentStickerCount) }}
+
+ 次发送
+
+
+ 占全年消息的 {{ stickerShareText }}
+
+
+
🖼
+
+
+
+
+
+
+
+
+ ☺
+ 最爱emoji
+
+
+
+
+
😂
+
🤣
+
❤️
+
😭
+
🙏
+
👍
+
+
+
+
+
+
+ {{ topEmojiLabel }}
+
+
+
+
+ 使用了 {{ formatInt(topEmojiCount) }} 次
+
+
☺
+
+
+
+
+
+
+
+
+ 📅
+ 月度最佳好友
+
+
+
+
+
+
+
+
+ {{ avatarFallback(monthlyMvpName) }}
+
+
+
+
+
+ {{ monthlyMvpName }}
+
+
+ 上榜 {{ monthlyMvpMonths }} /12 个月
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ avatarFallback(item._nameLabel) }}
+
+
+
{{ item.month }}月
+
{{ item._nameLabel }}
+
+
+
📅
+
+
+
+
+
+
+
+
+
+ 🗓
+ 活跃时段热力图
+
+
+
{{ heatmapYearLabel }}
+
{{ heatmapMaxLabel }}
+
+
+
+
+
+
+
+
+ {{ h.label }}
+
+
+
+
+
+
🗓
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 🍱 便当装盒中…
+ {{ funLoaderText }}
+
+ 翻到此页后开始聚合生成,首次加载可能稍久。
+ 正在聚合各页数据,这一页会比其它页久一点点。
+ 生成失败,可以重试一次。
+ 正在准备数据…
+
+
+ {{ cardErrorText }}
+
+
+ 重试
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/shared/BitsCardSwap.vue b/frontend/components/wrapped/shared/BitsCardSwap.vue
new file mode 100644
index 0000000..768e4df
--- /dev/null
+++ b/frontend/components/wrapped/shared/BitsCardSwap.vue
@@ -0,0 +1,291 @@
+
+
+
+
+
diff --git a/frontend/components/wrapped/shared/BitsGridMotion.vue b/frontend/components/wrapped/shared/BitsGridMotion.vue
new file mode 100644
index 0000000..5a09be2
--- /dev/null
+++ b/frontend/components/wrapped/shared/BitsGridMotion.vue
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+ {{ String(loopedItems[resolveIndex(rowIndex, columnIndex)] ?? '') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/shared/BitsSplitText.vue b/frontend/components/wrapped/shared/BitsSplitText.vue
new file mode 100644
index 0000000..df2a139
--- /dev/null
+++ b/frontend/components/wrapped/shared/BitsSplitText.vue
@@ -0,0 +1,160 @@
+
+
+ {{ text }}
+
+
+
+
diff --git a/frontend/components/wrapped/shared/VueBitsImageTrail.vue b/frontend/components/wrapped/shared/VueBitsImageTrail.vue
new file mode 100644
index 0000000..612b5cc
--- /dev/null
+++ b/frontend/components/wrapped/shared/VueBitsImageTrail.vue
@@ -0,0 +1,1225 @@
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/shared/VueBitsStack.vue b/frontend/components/wrapped/shared/VueBitsStack.vue
new file mode 100644
index 0000000..c191c7e
--- /dev/null
+++ b/frontend/components/wrapped/shared/VueBitsStack.vue
@@ -0,0 +1,294 @@
+
+
+
onPointerDown(e, card.id, index)"
+ @pointermove="onPointerMove"
+ @pointerup="onPointerUp"
+ @pointercancel="onPointerCancel"
+ >
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/shared/WrappedCardShell.vue b/frontend/components/wrapped/shared/WrappedCardShell.vue
new file mode 100644
index 0000000..77274e7
--- /dev/null
+++ b/frontend/components/wrapped/shared/WrappedCardShell.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
{{ title }}
+
+
+ {{ narrative }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ title }}
+
+
+ {{ narrative }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/shared/WrappedControls.vue b/frontend/components/wrapped/shared/WrappedControls.vue
new file mode 100644
index 0000000..9cdb4ad
--- /dev/null
+++ b/frontend/components/wrapped/shared/WrappedControls.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
Account
+
+ 默认(自动选择)
+ {{ a }}
+
+
+
+
+
+
+
+ 强制刷新(忽略缓存)
+
+
+
+
+
+ Generate
+ Loading...
+
+
+
+
+
+ {{ showAccount ? '正在加载账号列表...' : '正在检查数据...' }}
+
+
+ {{ showAccount ? '未发现已解密账号(请先解密数据库)。' : '未发现可用数据(请先解密数据库)。' }}
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/shared/WrappedDeckBackground.vue b/frontend/components/wrapped/shared/WrappedDeckBackground.vue
new file mode 100644
index 0000000..7e621c6
--- /dev/null
+++ b/frontend/components/wrapped/shared/WrappedDeckBackground.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/shared/WrappedHero.vue b/frontend/components/wrapped/shared/WrappedHero.vue
new file mode 100644
index 0000000..ae7bf94
--- /dev/null
+++ b/frontend/components/wrapped/shared/WrappedHero.vue
@@ -0,0 +1,403 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ randomTitle.main }}
+
+ {{ randomTitle.highlight }}
+
+
+
+
+
+ {{ randomSubtitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ String(item?.summary || '年度线索') }}
+
+
+ {{ String(item?.question || '这一页会揭晓你的聊天答案。') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ WECHAT WRAPPED
+
+
+
+ {{ yearText }}
+
+
+
+
+
+ 聊天年度总结
+
+
+ 从时间里回看你的聊天节奏。第一张卡:年度赛博作息表(24H x 7Days)。
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/shared/WrappedYearSelector.vue b/frontend/components/wrapped/shared/WrappedYearSelector.vue
new file mode 100644
index 0000000..f296e3b
--- /dev/null
+++ b/frontend/components/wrapped/shared/WrappedYearSelector.vue
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/visualizations/AnnualCalendarHeatmap.vue b/frontend/components/wrapped/visualizations/AnnualCalendarHeatmap.vue
new file mode 100644
index 0000000..21038e0
--- /dev/null
+++ b/frontend/components/wrapped/visualizations/AnnualCalendarHeatmap.vue
@@ -0,0 +1,417 @@
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/visualizations/ChatReplayAnimation.vue b/frontend/components/wrapped/visualizations/ChatReplayAnimation.vue
new file mode 100644
index 0000000..3ded96b
--- /dev/null
+++ b/frontend/components/wrapped/visualizations/ChatReplayAnimation.vue
@@ -0,0 +1,311 @@
+
+
+
+
+
+
+
+
+ {{ avatarFallback }}
+
+
+
+
+
{{ label }}
+
+ {{ displayNameShown }}
+
+
+
+
+
+
聊天回放
+
+ {{ date }} {{ time }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/visualizations/GlobalOverviewChart.vue b/frontend/components/wrapped/visualizations/GlobalOverviewChart.vue
new file mode 100644
index 0000000..cb0bdad
--- /dev/null
+++ b/frontend/components/wrapped/visualizations/GlobalOverviewChart.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
diff --git a/frontend/components/wrapped/visualizations/KeywordWordCloud.vue b/frontend/components/wrapped/visualizations/KeywordWordCloud.vue
new file mode 100644
index 0000000..6f9a226
--- /dev/null
+++ b/frontend/components/wrapped/visualizations/KeywordWordCloud.vue
@@ -0,0 +1,484 @@
+
+
+
+
+
+
+
+
+
暂无常用语
+
这一年你还没有足够的重复短句来生成常用语词云。
+
+
+
+
+
+
+
+
+
+
+ {{ selectedInfo.word }}
+ · {{ formatInt(selectedInfo.count) }} 次
+
+
+ 点击其它词,看看它出现的瞬间
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 没找到可展示的例句。
+
+
+
+
+
+
+
+
+ {{ seg.content }}
+
+
+
+
{{ m.raw }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/visualizations/MessageCharsChart.vue b/frontend/components/wrapped/visualizations/MessageCharsChart.vue
new file mode 100644
index 0000000..8a0c423
--- /dev/null
+++ b/frontend/components/wrapped/visualizations/MessageCharsChart.vue
@@ -0,0 +1,760 @@
+
+
+
+
+
+
+
+
+
+
你收到的字
+
+ {{ formatInt(receivedChars) }}
+
+
+ {{ receivedA4Text }}
+ 这么多字,都是别人认真对你的回应。
+ 今年还没有收到文字消息。
+
+
+ 约 {{ formatInt(receivedA4.a4.sheets) }} 张 A4 · 堆叠高度约 {{ receivedA4.a4.heightText }}
+
+
+
+
+
+
+
+
你发送的字
+
+ {{ formatInt(sentChars) }}
+
+
+ {{ sentBookText }}
+ 这么多字,是你打出的每一次认真。
+ 今年还没有文字消息。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ key.sub }}
+
{{ key.label }}
+
+
+
+
+
+
+
+
微信机械键盘
+
+
+
+
+
+
+
+
diff --git a/frontend/components/wrapped/visualizations/WeekdayHourHeatmap.vue b/frontend/components/wrapped/visualizations/WeekdayHourHeatmap.vue
new file mode 100644
index 0000000..36e5bf4
--- /dev/null
+++ b/frontend/components/wrapped/visualizations/WeekdayHourHeatmap.vue
@@ -0,0 +1,114 @@
+
+
+
+
+ 共 {{ totalMessages }} 条消息
+
+
24H x 7Days
+
+
+
+
+
+
+
+
+
diff --git a/frontend/composables/chat/useChatEditing.js b/frontend/composables/chat/useChatEditing.js
new file mode 100644
index 0000000..2d11360
--- /dev/null
+++ b/frontend/composables/chat/useChatEditing.js
@@ -0,0 +1,592 @@
+import { nextTick, ref, toRaw } from 'vue'
+
+const CONTEXT_MENU_MARGIN = 8
+
+const initialContextMenu = () => ({
+ visible: false,
+ x: 0,
+ y: 0,
+ message: null,
+ kind: '',
+ disabled: false,
+ editStatus: null,
+ editStatusLoading: false
+})
+
+const initialMessageEditModal = () => ({
+ open: false,
+ loading: false,
+ saving: false,
+ error: '',
+ mode: 'content',
+ sessionId: '',
+ messageId: '',
+ draft: '',
+ rawRow: null
+})
+
+const initialMessageFieldsModal = () => ({
+ open: false,
+ loading: false,
+ saving: false,
+ error: '',
+ sessionId: '',
+ messageId: '',
+ unsafe: false,
+ editsJson: '',
+ rawRow: null
+})
+
+export const useChatEditing = ({
+ api,
+ selectedAccount,
+ selectedContact,
+ refreshSelectedMessages,
+ normalizeMessage,
+ allMessages,
+ locateMessageByServerId
+}) => {
+ const contextMenu = ref(initialContextMenu())
+ const contextMenuElement = ref(null)
+ const messageEditModal = ref(initialMessageEditModal())
+ const messageFieldsModal = ref(initialMessageFieldsModal())
+
+ const closeContextMenu = () => {
+ contextMenu.value = initialContextMenu()
+ }
+
+ const repositionContextMenu = () => {
+ if (!process.client || !contextMenu.value.visible) return
+ const menuEl = contextMenuElement.value
+ if (!menuEl) return
+
+ const rect = menuEl.getBoundingClientRect()
+ const viewportWidth = Math.max(window.innerWidth || 0, document.documentElement?.clientWidth || 0)
+ const viewportHeight = Math.max(window.innerHeight || 0, document.documentElement?.clientHeight || 0)
+ if (!viewportWidth || !viewportHeight) return
+
+ const maxX = Math.max(CONTEXT_MENU_MARGIN, viewportWidth - rect.width - CONTEXT_MENU_MARGIN)
+ const maxY = Math.max(CONTEXT_MENU_MARGIN, viewportHeight - rect.height - CONTEXT_MENU_MARGIN)
+ const currentX = Number(contextMenu.value.x || 0)
+ const currentY = Number(contextMenu.value.y || 0)
+ const nextX = Math.min(Math.max(currentX, CONTEXT_MENU_MARGIN), maxX)
+ const nextY = Math.min(Math.max(currentY, CONTEXT_MENU_MARGIN), maxY)
+
+ if (nextX !== currentX || nextY !== currentY) {
+ contextMenu.value = {
+ ...contextMenu.value,
+ x: nextX,
+ y: nextY
+ }
+ }
+ }
+
+ const scheduleContextMenuReposition = () => {
+ if (!process.client) return
+ void nextTick(() => {
+ const run = () => repositionContextMenu()
+ if (typeof window.requestAnimationFrame === 'function') {
+ window.requestAnimationFrame(run)
+ } else {
+ run()
+ }
+ })
+ }
+
+ const loadContextMenuEditStatus = async (params) => {
+ if (!process.client) return
+ const account = String(params?.account || '').trim()
+ const username = String(params?.username || '').trim()
+ const messageId = String(params?.message_id || '').trim()
+ if (!account || !username || !messageId) {
+ contextMenu.value.editStatusLoading = false
+ return
+ }
+
+ try {
+ const response = await api.getChatEditStatus({ account, username, message_id: messageId })
+ const current = String(contextMenu.value?.message?.id || '').trim()
+ if (contextMenu.value.visible && current === messageId) {
+ contextMenu.value.editStatus = response || { modified: false }
+ scheduleContextMenuReposition()
+ }
+ } catch {
+ const current = String(contextMenu.value?.message?.id || '').trim()
+ if (contextMenu.value.visible && current === messageId) {
+ contextMenu.value.editStatus = null
+ scheduleContextMenuReposition()
+ }
+ } finally {
+ const current = String(contextMenu.value?.message?.id || '').trim()
+ if (contextMenu.value.visible && current === messageId) {
+ contextMenu.value.editStatusLoading = false
+ scheduleContextMenuReposition()
+ }
+ }
+ }
+
+ const openMediaContextMenu = (event, message, kind) => {
+ if (!process.client) return
+ event.preventDefault()
+ event.stopPropagation()
+
+ let actualKind = kind
+ let disabled = true
+ if (kind === 'voice') {
+ disabled = !(message?.serverIdStr || message?.serverId)
+ } else if (kind === 'file') {
+ disabled = !message?.fileMd5
+ } else if (kind === 'image') {
+ disabled = !(message?.imageMd5 || message?.imageFileId)
+ } else if (kind === 'emoji') {
+ disabled = !message?.emojiMd5
+ } else if (kind === 'video') {
+ if (message?.videoMd5 || message?.videoFileId) {
+ disabled = false
+ actualKind = 'video'
+ } else if (message?.videoThumbMd5 || message?.videoThumbFileId) {
+ disabled = false
+ actualKind = 'video_thumb'
+ }
+ }
+
+ contextMenu.value = {
+ visible: true,
+ x: event.clientX,
+ y: event.clientY,
+ message,
+ kind: actualKind,
+ disabled,
+ editStatus: null,
+ editStatusLoading: false
+ }
+
+ try {
+ const account = String(selectedAccount.value || '').trim()
+ const username = String(selectedContact.value?.username || '').trim()
+ const messageId = String(message?.id || '').trim()
+ if (account && username && messageId) {
+ contextMenu.value.editStatusLoading = true
+ void loadContextMenuEditStatus({ account, username, message_id: messageId })
+ }
+ } catch {}
+
+ scheduleContextMenuReposition()
+ }
+
+ const prettyJson = (value) => {
+ try {
+ return JSON.stringify(value ?? null, null, 2)
+ } catch {
+ return String(value ?? '')
+ }
+ }
+
+ const isLikelyTextMessage = (message) => {
+ if (!message) return false
+ const renderType = String(message?.renderType || '').trim()
+ if (renderType && renderType !== 'text') return false
+ if (message?.imageUrl || message?.emojiUrl || message?.videoUrl || message?.voiceUrl) return false
+ return true
+ }
+
+ const closeMessageEditModal = () => {
+ messageEditModal.value = initialMessageEditModal()
+ }
+
+ const openMessageEditModal = async ({ message, mode }) => {
+ if (!process.client) return
+ const account = String(selectedAccount.value || '').trim()
+ const sessionId = String(selectedContact.value?.username || '').trim()
+ const messageId = String(message?.id || '').trim()
+ if (!account || !sessionId || !messageId) return
+
+ const resolvedMode = mode === 'raw' ? 'raw' : 'content'
+ const initialDraft = resolvedMode === 'content'
+ ? (typeof message?.content === 'string' ? message.content : String(message?.content ?? ''))
+ : ''
+
+ messageEditModal.value = {
+ open: true,
+ loading: true,
+ saving: false,
+ error: '',
+ mode: resolvedMode,
+ sessionId,
+ messageId,
+ draft: initialDraft,
+ rawRow: null
+ }
+
+ try {
+ const response = await api.getChatMessageRaw({ account, username: sessionId, message_id: messageId })
+ const row = response?.row || null
+ const rawContent = row?.message_content
+ const rawDraft = typeof rawContent === 'string' ? rawContent : String(rawContent ?? '')
+ const draft = resolvedMode === 'raw' ? rawDraft : messageEditModal.value.draft
+ messageEditModal.value = { ...messageEditModal.value, loading: false, rawRow: row, draft }
+ } catch (error) {
+ messageEditModal.value = { ...messageEditModal.value, loading: false, error: error?.message || '加载失败' }
+ }
+ }
+
+ const saveMessageEditModal = async () => {
+ if (!process.client) return
+ if (messageEditModal.value.saving || messageEditModal.value.loading) return
+
+ const account = String(selectedAccount.value || '').trim()
+ const sessionId = String(messageEditModal.value.sessionId || '').trim()
+ const messageId = String(messageEditModal.value.messageId || '').trim()
+ if (!account || !sessionId || !messageId) return
+
+ messageEditModal.value = { ...messageEditModal.value, saving: true, error: '' }
+ try {
+ const response = await api.editChatMessage({
+ account,
+ session_id: sessionId,
+ message_id: messageId,
+ edits: {
+ message_content: String(messageEditModal.value.draft ?? '')
+ },
+ unsafe: false
+ })
+
+ if (response?.updated_message) {
+ try {
+ const updated = normalizeMessage(response.updated_message)
+ const username = String(selectedContact.value?.username || '').trim()
+ const list = allMessages.value[username] || []
+ const index = list.findIndex((message) => String(message?.id || '') === String(updated?.id || ''))
+ if (index >= 0) {
+ const next = [...list]
+ next[index] = updated
+ allMessages.value = { ...allMessages.value, [username]: next }
+ } else {
+ await refreshSelectedMessages()
+ }
+ } catch {
+ await refreshSelectedMessages()
+ }
+ } else {
+ await refreshSelectedMessages()
+ }
+
+ closeMessageEditModal()
+ } catch (error) {
+ messageEditModal.value = { ...messageEditModal.value, saving: false, error: error?.message || '保存失败' }
+ return
+ } finally {
+ messageEditModal.value = { ...messageEditModal.value, saving: false }
+ }
+ }
+
+ const closeMessageFieldsModal = () => {
+ messageFieldsModal.value = initialMessageFieldsModal()
+ }
+
+ const openMessageFieldsModal = async (message) => {
+ if (!process.client) return
+ const account = String(selectedAccount.value || '').trim()
+ const sessionId = String(selectedContact.value?.username || '').trim()
+ const messageId = String(message?.id || '').trim()
+ if (!account || !sessionId || !messageId) return
+
+ messageFieldsModal.value = {
+ open: true,
+ loading: true,
+ saving: false,
+ error: '',
+ sessionId,
+ messageId,
+ unsafe: false,
+ editsJson: '',
+ rawRow: null
+ }
+
+ try {
+ const response = await api.getChatMessageRaw({ account, username: sessionId, message_id: messageId })
+ const row = response?.row || null
+ const seed = {}
+ for (const key of ['message_content', 'local_type', 'create_time', 'server_id', 'origin_source', 'source']) {
+ if (row && Object.prototype.hasOwnProperty.call(row, key)) seed[key] = row[key]
+ }
+ messageFieldsModal.value = {
+ ...messageFieldsModal.value,
+ loading: false,
+ rawRow: row,
+ editsJson: JSON.stringify(seed, null, 2)
+ }
+ } catch (error) {
+ messageFieldsModal.value = { ...messageFieldsModal.value, loading: false, error: error?.message || '加载失败' }
+ }
+ }
+
+ const saveMessageFieldsModal = async () => {
+ if (!process.client) return
+ if (messageFieldsModal.value.saving || messageFieldsModal.value.loading) return
+
+ const account = String(selectedAccount.value || '').trim()
+ const sessionId = String(messageFieldsModal.value.sessionId || '').trim()
+ const messageId = String(messageFieldsModal.value.messageId || '').trim()
+ if (!account || !sessionId || !messageId) return
+
+ let edits = null
+ try {
+ edits = JSON.parse(String(messageFieldsModal.value.editsJson || '').trim() || 'null')
+ } catch {
+ messageFieldsModal.value = { ...messageFieldsModal.value, error: 'JSON 格式错误' }
+ return
+ }
+ if (!edits || typeof edits !== 'object' || Array.isArray(edits)) {
+ messageFieldsModal.value = { ...messageFieldsModal.value, error: 'edits 必须是 JSON 对象' }
+ return
+ }
+ if (!Object.keys(edits).length) {
+ messageFieldsModal.value = { ...messageFieldsModal.value, error: 'edits 不能为空' }
+ return
+ }
+
+ messageFieldsModal.value = { ...messageFieldsModal.value, saving: true, error: '' }
+ try {
+ await api.editChatMessage({
+ account,
+ session_id: sessionId,
+ message_id: messageId,
+ edits,
+ unsafe: !!messageFieldsModal.value.unsafe
+ })
+ await refreshSelectedMessages()
+ closeMessageFieldsModal()
+ } catch (error) {
+ messageFieldsModal.value = { ...messageFieldsModal.value, saving: false, error: error?.message || '保存失败' }
+ return
+ } finally {
+ messageFieldsModal.value = { ...messageFieldsModal.value, saving: false }
+ }
+ }
+
+ const copyTextToClipboard = async (text) => {
+ if (!process.client) return false
+
+ const value = String(text ?? '').trim()
+ if (!value) return false
+
+ try {
+ await navigator.clipboard.writeText(value)
+ return true
+ } catch {}
+
+ try {
+ const element = document.createElement('textarea')
+ element.value = value
+ element.setAttribute('readonly', 'true')
+ element.style.position = 'fixed'
+ element.style.left = '-9999px'
+ element.style.top = '-9999px'
+ document.body.appendChild(element)
+ element.select()
+ const ok = document.execCommand('copy')
+ document.body.removeChild(element)
+ if (ok) return true
+ } catch {}
+
+ try {
+ window.prompt('复制内容:', value)
+ return true
+ } catch {
+ return false
+ }
+ }
+
+ const onCopyMessageTextClick = async () => {
+ if (!process.client) return
+ const message = contextMenu.value.message
+ if (!message) return
+ try {
+ const text = String(message?.content || '').trim()
+ if (!text) {
+ window.alert('该消息没有可复制的文本')
+ return
+ }
+ const ok = await copyTextToClipboard(text)
+ if (!ok) window.alert('复制失败:无法写入剪贴板')
+ } catch {
+ window.alert('复制失败')
+ } finally {
+ closeContextMenu()
+ }
+ }
+
+ const onCopyMessageJsonClick = async () => {
+ if (!process.client) return
+ const message = contextMenu.value.message
+ if (!message) return
+ try {
+ const raw = toRaw(message) || message
+ const json = JSON.stringify(raw, (_key, value) => (typeof value === 'bigint' ? value.toString() : value), 2)
+ const ok = await copyTextToClipboard(json)
+ if (!ok) window.alert('复制失败:无法写入剪贴板')
+ } catch {
+ window.alert('复制失败')
+ } finally {
+ closeContextMenu()
+ }
+ }
+
+ const onOpenFolderClick = async () => {
+ if (contextMenu.value.disabled) return
+ const message = contextMenu.value.message
+ const kind = contextMenu.value.kind
+
+ try {
+ if (!selectedAccount.value || !selectedContact.value?.username) return
+
+ const params = {
+ account: selectedAccount.value,
+ username: selectedContact.value.username,
+ kind
+ }
+
+ if (kind === 'voice') {
+ params.server_id = message.serverIdStr || message.serverId
+ } else if (kind === 'file') {
+ params.md5 = message.fileMd5
+ } else if (kind === 'image') {
+ if (message.imageMd5) params.md5 = message.imageMd5
+ else if (message.imageFileId) params.file_id = message.imageFileId
+ } else if (kind === 'emoji') {
+ params.md5 = message.emojiMd5
+ } else if (kind === 'video') {
+ params.md5 = message.videoMd5
+ if (message.videoFileId) params.file_id = message.videoFileId
+ } else if (kind === 'video_thumb') {
+ params.md5 = message.videoThumbMd5
+ if (message.videoThumbFileId) params.file_id = message.videoThumbFileId
+ }
+
+ await api.openChatMediaFolder(params)
+ } finally {
+ closeContextMenu()
+ }
+ }
+
+ const onEditMessageClick = async () => {
+ const message = contextMenu.value.message
+ if (!message) return
+ const mode = isLikelyTextMessage(message) ? 'content' : 'raw'
+ closeContextMenu()
+ await openMessageEditModal({ message, mode })
+ }
+
+ const onEditMessageFieldsClick = async () => {
+ const message = contextMenu.value.message
+ if (!message) return
+ closeContextMenu()
+ await openMessageFieldsModal(message)
+ }
+
+ const onResetEditedMessageClick = async () => {
+ if (!process.client) return
+ const message = contextMenu.value.message
+ const account = String(selectedAccount.value || '').trim()
+ const sessionId = String(selectedContact.value?.username || '').trim()
+ const messageId = String(message?.id || '').trim()
+ if (!message || !account || !sessionId || !messageId) return
+
+ const ok = window.confirm('确认恢复该条消息到首次快照吗?')
+ if (!ok) return
+
+ try {
+ await api.resetChatEditedMessage({ account, session_id: sessionId, message_id: messageId })
+ closeContextMenu()
+ await refreshSelectedMessages()
+ } catch (error) {
+ window.alert(error?.message || '恢复失败')
+ } finally {
+ closeContextMenu()
+ }
+ }
+
+ const onRepairMessageSenderAsMeClick = async () => {
+ if (!process.client) return
+ const message = contextMenu.value.message
+ const account = String(selectedAccount.value || '').trim()
+ const sessionId = String(selectedContact.value?.username || '').trim()
+ const messageId = String(message?.id || '').trim()
+ if (!message || !account || !sessionId || !messageId) return
+
+ const ok = window.confirm('确认将该消息修复为“我发送”吗?这会修改 real_sender_id 字段。')
+ if (!ok) return
+
+ try {
+ await api.repairChatMessageSender({ account, session_id: sessionId, message_id: messageId, mode: 'me' })
+ closeContextMenu()
+ await refreshSelectedMessages()
+ } catch (error) {
+ window.alert(error?.message || '修复失败')
+ } finally {
+ closeContextMenu()
+ }
+ }
+
+ const onFlipWechatMessageDirectionClick = async () => {
+ if (!process.client) return
+ const message = contextMenu.value.message
+ const account = String(selectedAccount.value || '').trim()
+ const sessionId = String(selectedContact.value?.username || '').trim()
+ const messageId = String(message?.id || '').trim()
+ if (!message || !account || !sessionId || !messageId) return
+
+ const ok = window.confirm(
+ '确认反转该消息在微信客户端的左右气泡位置吗?\n\n这会修改 packed_info_data 字段(有风险)。\n可通过“恢复原消息”撤销。'
+ )
+ if (!ok) return
+
+ try {
+ await api.flipChatMessageDirection({ account, session_id: sessionId, message_id: messageId })
+ closeContextMenu()
+ await refreshSelectedMessages()
+ } catch (error) {
+ window.alert(error?.message || '反转失败')
+ } finally {
+ closeContextMenu()
+ }
+ }
+
+ const onLocateQuotedMessageClick = async () => {
+ const message = contextMenu.value.message
+ if (!message?.quoteServerId) return
+ closeContextMenu()
+ const ok = await locateMessageByServerId(message.quoteServerId)
+ if (!ok && process.client) {
+ window.alert('定位引用消息失败')
+ }
+ }
+
+ return {
+ contextMenu,
+ contextMenuElement,
+ messageEditModal,
+ messageFieldsModal,
+ closeContextMenu,
+ openMediaContextMenu,
+ prettyJson,
+ isLikelyTextMessage,
+ closeMessageEditModal,
+ openMessageEditModal,
+ saveMessageEditModal,
+ closeMessageFieldsModal,
+ openMessageFieldsModal,
+ saveMessageFieldsModal,
+ copyTextToClipboard,
+ onCopyMessageTextClick,
+ onCopyMessageJsonClick,
+ onOpenFolderClick,
+ onEditMessageClick,
+ onEditMessageFieldsClick,
+ onResetEditedMessageClick,
+ onRepairMessageSenderAsMeClick,
+ onFlipWechatMessageDirectionClick,
+ onLocateQuotedMessageClick
+ }
+}
diff --git a/frontend/composables/chat/useChatExport.js b/frontend/composables/chat/useChatExport.js
new file mode 100644
index 0000000..5843f76
--- /dev/null
+++ b/frontend/composables/chat/useChatExport.js
@@ -0,0 +1,543 @@
+import { computed, ref, watch } from 'vue'
+import { reportServerErrorFromResponse } from '~/lib/server-error-logging'
+import { toUnixSeconds } from '~/lib/chat/formatters'
+
+export const useChatExport = ({ api, apiBase, contacts, selectedAccount, selectedContact, privacyMode }) => {
+ const exportModalOpen = ref(false)
+ const isExportCreating = ref(false)
+ const exportError = ref('')
+
+ const exportScope = ref('current')
+ const exportFormat = ref('json')
+ const exportDownloadRemoteMedia = ref(true)
+ const exportHtmlPageSize = ref(1000)
+ const exportMessageTypeOptions = [
+ { value: 'text', label: '文本' },
+ { value: 'image', label: '图片' },
+ { value: 'emoji', label: '表情' },
+ { value: 'video', label: '视频' },
+ { value: 'voice', label: '语音' },
+ { value: 'chatHistory', label: '聊天记录' },
+ { value: 'transfer', label: '转账' },
+ { value: 'redPacket', label: '红包' },
+ { value: 'file', label: '文件' },
+ { value: 'link', label: '链接' },
+ { value: 'quote', label: '引用' },
+ { value: 'system', label: '系统' },
+ { value: 'voip', label: '通话' }
+ ]
+ const exportMessageTypes = ref(exportMessageTypeOptions.map((item) => item.value))
+
+ const exportStartLocal = ref('')
+ const exportEndLocal = ref('')
+ const exportFileName = ref('')
+ const exportFolder = ref('')
+ const exportFolderHandle = ref(null)
+ const exportSaveBusy = ref(false)
+ const exportSaveMsg = ref('')
+ const exportAutoSavedFor = ref('')
+
+ const exportSearchQuery = ref('')
+ const exportListTab = ref('all')
+ const exportSelectedUsernames = ref([])
+
+ const exportJob = ref(null)
+ let exportPollTimer = null
+ let exportEventSource = null
+
+ const clamp01 = (value) => Math.min(1, Math.max(0, value))
+ const asNumber = (value) => {
+ const next = Number(value)
+ return Number.isFinite(next) ? next : 0
+ }
+
+ const exportOverallPercent = computed(() => {
+ const job = exportJob.value
+ const progress = job?.progress || {}
+ const total = asNumber(progress.conversationsTotal)
+ const done = asNumber(progress.conversationsDone)
+ if (total <= 0) return 0
+
+ const currentTotal = asNumber(progress.currentConversationMessagesTotal)
+ const currentDone = asNumber(progress.currentConversationMessagesExported)
+ const currentFraction = currentTotal > 0 ? clamp01(currentDone / currentTotal) : 0
+ const overall = clamp01((done + (job?.status === 'running' ? currentFraction : 0)) / total)
+ return Math.round(overall * 100)
+ })
+
+ const exportCurrentPercent = computed(() => {
+ const progress = exportJob.value?.progress || {}
+ const total = asNumber(progress.currentConversationMessagesTotal)
+ const done = asNumber(progress.currentConversationMessagesExported)
+ if (total <= 0) return null
+ return Math.round(clamp01(done / total) * 100)
+ })
+
+ const normalizeExportSelectedUsernames = (list) => {
+ const seen = new Set()
+ return (Array.isArray(list) ? list : []).reduce((acc, item) => {
+ const username = String(item || '').trim()
+ if (!username || seen.has(username)) return acc
+ seen.add(username)
+ acc.push(username)
+ return acc
+ }, [])
+ }
+
+ const getExportFilteredContacts = ({ tab = exportListTab.value, query = exportSearchQuery.value } = {}) => {
+ const normalizedQuery = String(query || '').trim().toLowerCase()
+ let list = Array.isArray(contacts.value) ? contacts.value : []
+
+ const normalizedTab = String(tab || 'all')
+ if (normalizedTab === 'groups') list = list.filter((contact) => !!contact?.isGroup)
+ if (normalizedTab === 'singles') list = list.filter((contact) => !contact?.isGroup)
+
+ if (!normalizedQuery) return list
+ return list.filter((contact) => {
+ const name = String(contact?.name || '').toLowerCase()
+ const username = String(contact?.username || '').toLowerCase()
+ return name.includes(normalizedQuery) || username.includes(normalizedQuery)
+ })
+ }
+
+ const exportFilteredContacts = computed(() => {
+ return getExportFilteredContacts()
+ })
+
+ const exportContactCounts = computed(() => {
+ const list = Array.isArray(contacts.value) ? contacts.value : []
+ const total = list.length
+ const groups = list.filter((contact) => !!contact?.isGroup).length
+ return { total, groups, singles: total - groups }
+ })
+
+ const exportSelectedUsernameSet = computed(() => {
+ return new Set(normalizeExportSelectedUsernames(exportSelectedUsernames.value))
+ })
+
+ const setExportSelectedUsernames = (list) => {
+ exportSelectedUsernames.value = normalizeExportSelectedUsernames(list)
+ }
+
+ const getExportFilteredUsernames = (tab = exportListTab.value) => {
+ return getExportFilteredContacts({ tab })
+ .map((contact) => String(contact?.username || '').trim())
+ .filter(Boolean)
+ }
+
+ const selectExportFilteredContacts = (tab = exportListTab.value) => {
+ setExportSelectedUsernames(getExportFilteredUsernames(tab))
+ }
+
+ const clearExportFilteredContacts = () => {
+ setExportSelectedUsernames([])
+ }
+
+ const areExportFilteredContactsAllSelected = (tab = exportListTab.value) => {
+ const usernames = getExportFilteredUsernames(tab)
+ if (usernames.length !== exportSelectedUsernameSet.value.size) return false
+ return usernames.every((username) => exportSelectedUsernameSet.value.has(username))
+ }
+
+ const onExportListTabClick = (tab) => {
+ const nextTab = String(tab || 'all')
+ const isSameTab = String(exportListTab.value || 'all') === nextTab
+ exportListTab.value = nextTab
+
+ if (isSameTab) {
+ if (areExportFilteredContactsAllSelected(nextTab)) {
+ clearExportFilteredContacts(nextTab)
+ } else {
+ selectExportFilteredContacts(nextTab)
+ }
+ return
+ }
+
+ selectExportFilteredContacts(nextTab)
+ }
+
+ const isExportContactSelected = (username) => {
+ return exportSelectedUsernameSet.value.has(String(username || '').trim())
+ }
+
+ const onExportBatchScopeClick = (tab) => {
+ exportScope.value = 'selected'
+ onExportListTabClick(tab)
+ }
+
+ const isDesktopExportRuntime = () => {
+ return !!(process.client && window?.wechatDesktop?.chooseDirectory)
+ }
+
+ const isWebDirectoryPickerSupported = () => {
+ return !!(process.client && typeof window.showDirectoryPicker === 'function')
+ }
+
+ const hasWebExportFolder = computed(() => {
+ return !!(isWebDirectoryPickerSupported() && exportFolderHandle.value)
+ })
+
+ const chooseExportFolder = async () => {
+ exportError.value = ''
+ exportSaveMsg.value = ''
+ try {
+ if (!process.client) {
+ exportError.value = '当前环境不支持选择导出目录'
+ return
+ }
+
+ if (isDesktopExportRuntime()) {
+ const result = await window.wechatDesktop.chooseDirectory({ title: '选择导出目录' })
+ if (result && !result.canceled && Array.isArray(result.filePaths) && result.filePaths.length > 0) {
+ exportFolder.value = String(result.filePaths[0] || '').trim()
+ exportFolderHandle.value = null
+ }
+ return
+ }
+
+ if (isWebDirectoryPickerSupported()) {
+ const handle = await window.showDirectoryPicker()
+ if (handle) {
+ exportFolderHandle.value = handle
+ exportFolder.value = `浏览器目录:${String(handle.name || '已选择')}`
+ }
+ return
+ }
+
+ exportError.value = '当前浏览器不支持目录选择,请使用桌面端或 Chromium 新版浏览器'
+ } catch (error) {
+ exportError.value = error?.message || '选择导出目录失败'
+ }
+ }
+
+ const guessExportZipName = (job) => {
+ const raw = String(job?.zipPath || '').trim()
+ if (raw) {
+ const name = raw.replace(/\\/g, '/').split('/').pop()
+ if (name && name.toLowerCase().endsWith('.zip')) return name
+ }
+ const exportId = String(job?.exportId || '').trim() || 'export'
+ return `wechat_chat_export_${exportId}.zip`
+ }
+
+ const getExportDownloadUrl = (exportId) => {
+ return `${apiBase}/chat/exports/${encodeURIComponent(String(exportId || ''))}/download`
+ }
+
+ const saveExportToSelectedFolder = async (options = {}) => {
+ const autoSave = !!options?.auto
+ exportError.value = ''
+ exportSaveMsg.value = ''
+ if (!process.client || !isWebDirectoryPickerSupported()) {
+ exportError.value = '当前环境不支持保存到浏览器目录'
+ return
+ }
+ const handle = exportFolderHandle.value
+ if (!handle || typeof handle.getFileHandle !== 'function') {
+ exportError.value = '请先选择浏览器导出目录'
+ return
+ }
+
+ const exportId = exportJob.value?.exportId
+ if (!exportId || String(exportJob.value?.status || '') !== 'done') {
+ exportError.value = '导出任务尚未完成'
+ return
+ }
+
+ exportSaveBusy.value = true
+ try {
+ const response = await fetch(getExportDownloadUrl(exportId))
+ if (!response.ok) {
+ await reportServerErrorFromResponse(response, {
+ method: 'GET',
+ requestUrl: getExportDownloadUrl(exportId),
+ message: `下载导出文件失败(${response.status})`,
+ source: 'chat.exportDownload'
+ })
+ throw new Error(`下载导出文件失败(${response.status})`)
+ }
+ const blob = await response.blob()
+ const fileName = guessExportZipName(exportJob.value)
+ const fileHandle = await handle.getFileHandle(fileName, { create: true })
+ const writable = await fileHandle.createWritable()
+ await writable.write(blob)
+ await writable.close()
+ exportAutoSavedFor.value = String(exportId)
+ exportSaveMsg.value = autoSave
+ ? `已自动保存到已选目录:${fileName}`
+ : `已保存到已选目录:${fileName}`
+ } catch (error) {
+ exportError.value = error?.message || '保存到浏览器目录失败'
+ } finally {
+ exportSaveBusy.value = false
+ }
+ }
+
+ const stopExportPolling = () => {
+ if (exportEventSource) {
+ try {
+ exportEventSource.close()
+ } catch {}
+ exportEventSource = null
+ }
+ if (exportPollTimer) {
+ clearInterval(exportPollTimer)
+ exportPollTimer = null
+ }
+ }
+
+ const startExportHttpPolling = (exportId) => {
+ if (!exportId) return
+ exportPollTimer = setInterval(async () => {
+ try {
+ const response = await api.getChatExport(exportId)
+ exportJob.value = response?.job || exportJob.value
+ const status = String(exportJob.value?.status || '')
+ if (status === 'done' || status === 'error' || status === 'cancelled') {
+ stopExportPolling()
+ }
+ } catch {}
+ }, 1200)
+ }
+
+ const startExportPolling = (exportId) => {
+ stopExportPolling()
+ if (!exportId) return
+
+ if (process.client && typeof window !== 'undefined' && typeof EventSource !== 'undefined') {
+ const url = `${apiBase}/chat/exports/${encodeURIComponent(String(exportId))}/events`
+ try {
+ exportEventSource = new EventSource(url)
+ exportEventSource.onmessage = (event) => {
+ try {
+ const next = JSON.parse(String(event.data || '{}'))
+ exportJob.value = next || exportJob.value
+ const status = String(exportJob.value?.status || '')
+ if (status === 'done' || status === 'error' || status === 'cancelled') {
+ stopExportPolling()
+ }
+ } catch {}
+ }
+ exportEventSource.onerror = () => {
+ try {
+ exportEventSource?.close()
+ } catch {}
+ exportEventSource = null
+ if (!exportPollTimer) startExportHttpPolling(exportId)
+ }
+ return
+ } catch {
+ exportEventSource = null
+ }
+ }
+
+ startExportHttpPolling(exportId)
+ }
+
+ const openExportModal = () => {
+ exportModalOpen.value = true
+ exportError.value = ''
+ exportSaveMsg.value = ''
+ exportSearchQuery.value = ''
+ exportListTab.value = 'all'
+ exportSelectedUsernames.value = []
+ exportStartLocal.value = ''
+ exportEndLocal.value = ''
+ exportMessageTypes.value = exportMessageTypeOptions.map((item) => item.value)
+ exportAutoSavedFor.value = ''
+ exportScope.value = selectedContact.value?.username ? 'current' : 'selected'
+ if (!selectedContact.value?.username) {
+ selectExportFilteredContacts('all')
+ }
+ }
+
+ const closeExportModal = () => {
+ exportModalOpen.value = false
+ exportError.value = ''
+ }
+
+ watch(exportModalOpen, (open) => {
+ if (!process.client) return
+ if (!open) {
+ stopExportPolling()
+ return
+ }
+
+ const exportId = exportJob.value?.exportId
+ const status = String(exportJob.value?.status || '')
+ if (exportId && (status === 'queued' || status === 'running')) {
+ startExportPolling(exportId)
+ }
+ })
+
+ watch(exportScope, (scope, previousScope) => {
+ if (scope !== 'selected' || previousScope === 'selected') return
+ if (exportSelectedUsernames.value.length > 0) return
+ selectExportFilteredContacts(exportListTab.value)
+ })
+
+ watch(
+ () => ({
+ exportId: String(exportJob.value?.exportId || ''),
+ status: String(exportJob.value?.status || '')
+ }),
+ async ({ exportId, status }) => {
+ if (!process.client || status !== 'done' || !exportId) return
+ if (!hasWebExportFolder.value) return
+ if (exportAutoSavedFor.value === exportId) return
+ if (exportSaveBusy.value) return
+ await saveExportToSelectedFolder({ auto: true })
+ }
+ )
+
+ const startChatExport = async () => {
+ exportError.value = ''
+ exportSaveMsg.value = ''
+ if (!selectedAccount.value) {
+ exportError.value = '未选择账号'
+ return
+ }
+
+ let scope = exportScope.value
+ let usernames = []
+ if (scope === 'current') {
+ scope = 'selected'
+ if (selectedContact.value?.username) {
+ usernames = [selectedContact.value.username]
+ }
+ } else if (scope === 'selected') {
+ usernames = Array.isArray(exportSelectedUsernames.value) ? exportSelectedUsernames.value.filter(Boolean) : []
+ }
+
+ if (scope === 'selected' && (!usernames || usernames.length === 0)) {
+ exportError.value = '请选择至少一个会话'
+ return
+ }
+
+ const hasDesktopFolder = isDesktopExportRuntime() && !!String(exportFolder.value || '').trim()
+ const hasWebFolder = !isDesktopExportRuntime() && !!exportFolderHandle.value
+ if (!hasDesktopFolder && !hasWebFolder) {
+ exportError.value = '请先选择导出目录'
+ return
+ }
+
+ const startTime = toUnixSeconds(exportStartLocal.value)
+ const endTime = toUnixSeconds(exportEndLocal.value)
+ if (startTime && endTime && startTime > endTime) {
+ exportError.value = '时间范围不合法:开始时间不能晚于结束时间'
+ return
+ }
+
+ const messageTypes = Array.isArray(exportMessageTypes.value) ? exportMessageTypes.value.filter(Boolean) : []
+ if (messageTypes.length === 0) {
+ exportError.value = '请至少勾选一个消息类型'
+ return
+ }
+
+ const selectedTypeSet = new Set(messageTypes.map((item) => String(item || '').trim()))
+ const mediaKindSet = new Set()
+ if (selectedTypeSet.has('chatHistory')) {
+ mediaKindSet.add('image')
+ mediaKindSet.add('emoji')
+ mediaKindSet.add('video')
+ mediaKindSet.add('video_thumb')
+ mediaKindSet.add('voice')
+ mediaKindSet.add('file')
+ }
+ if (selectedTypeSet.has('image')) mediaKindSet.add('image')
+ if (selectedTypeSet.has('emoji')) mediaKindSet.add('emoji')
+ if (selectedTypeSet.has('video')) {
+ mediaKindSet.add('video')
+ mediaKindSet.add('video_thumb')
+ }
+ if (selectedTypeSet.has('voice')) mediaKindSet.add('voice')
+ if (selectedTypeSet.has('file')) mediaKindSet.add('file')
+
+ const mediaKinds = Array.from(mediaKindSet)
+ const includeMedia = !privacyMode.value && mediaKinds.length > 0
+
+ isExportCreating.value = true
+ exportAutoSavedFor.value = ''
+ try {
+ const response = await api.createChatExport({
+ account: selectedAccount.value,
+ scope,
+ usernames,
+ format: exportFormat.value,
+ start_time: startTime,
+ end_time: endTime,
+ include_hidden: false,
+ include_official: false,
+ message_types: messageTypes,
+ include_media: includeMedia,
+ media_kinds: mediaKinds,
+ download_remote_media: exportFormat.value === 'html' && !!exportDownloadRemoteMedia.value,
+ html_page_size: Math.max(0, Math.floor(Number(exportHtmlPageSize.value || 1000))),
+ output_dir: isDesktopExportRuntime() ? String(exportFolder.value || '').trim() : null,
+ privacy_mode: !!privacyMode.value,
+ file_name: exportFileName.value || null
+ })
+
+ exportJob.value = response?.job || null
+ const exportId = exportJob.value?.exportId
+ if (exportId) startExportPolling(exportId)
+ } catch (error) {
+ exportError.value = error?.message || '创建导出任务失败'
+ } finally {
+ isExportCreating.value = false
+ }
+ }
+
+ const cancelCurrentExport = async () => {
+ const exportId = exportJob.value?.exportId
+ if (!exportId) return
+
+ try {
+ await api.cancelChatExport(exportId)
+ const response = await api.getChatExport(exportId)
+ exportJob.value = response?.job || exportJob.value
+ } catch (error) {
+ exportError.value = error?.message || '取消导出失败'
+ }
+ }
+
+ return {
+ exportModalOpen,
+ isExportCreating,
+ exportError,
+ exportScope,
+ exportFormat,
+ exportDownloadRemoteMedia,
+ exportHtmlPageSize,
+ exportMessageTypeOptions,
+ exportMessageTypes,
+ exportStartLocal,
+ exportEndLocal,
+ exportFileName,
+ exportFolder,
+ exportFolderHandle,
+ exportSaveBusy,
+ exportSaveMsg,
+ exportAutoSavedFor,
+ exportSearchQuery,
+ exportListTab,
+ exportSelectedUsernames,
+ exportJob,
+ exportOverallPercent,
+ exportCurrentPercent,
+ exportFilteredContacts,
+ exportContactCounts,
+ onExportBatchScopeClick,
+ onExportListTabClick,
+ isExportContactSelected,
+ hasWebExportFolder,
+ chooseExportFolder,
+ getExportDownloadUrl,
+ saveExportToSelectedFolder,
+ openExportModal,
+ closeExportModal,
+ startChatExport,
+ cancelCurrentExport,
+ stopExportPolling
+ }
+}
diff --git a/frontend/composables/chat/useChatHistoryWindows.js b/frontend/composables/chat/useChatHistoryWindows.js
new file mode 100644
index 0000000..550a32d
--- /dev/null
+++ b/frontend/composables/chat/useChatHistoryWindows.js
@@ -0,0 +1,488 @@
+import { ref } from 'vue'
+import {
+ buildChatHistoryWindowPayload,
+ createChatHistoryRecordNormalizer,
+ enhanceChatHistoryRecords,
+ formatChatHistoryVideoDuration,
+ getChatHistoryPreviewLines,
+ isChatHistoryRecordItemIncomplete,
+ normalizeChatHistoryUrl,
+ parseChatHistoryRecord,
+ pickFirstMd5,
+ stripWeChatInvisible
+} from '~/lib/chat/chat-history'
+
+export const useChatHistoryWindows = ({
+ api,
+ apiBase,
+ selectedAccount,
+ selectedContact,
+ openImagePreview,
+ openVideoPreview
+}) => {
+ const floatingWindows = ref([])
+ let floatingWindowSeq = 0
+ let floatingWindowZ = 70
+ const floatingDragState = { id: '', offsetX: 0, offsetY: 0 }
+
+ const clampNumber = (value, min, max) => Math.min(max, Math.max(min, value))
+ const normalizeRecordItem = createChatHistoryRecordNormalizer({
+ apiBase,
+ getSelectedAccount: () => selectedAccount.value,
+ getSelectedContact: () => selectedContact.value
+ })
+
+ const getFloatingWindowById = (id) => {
+ const list = Array.isArray(floatingWindows.value) ? floatingWindows.value : []
+ return list.find((item) => String(item?.id || '') === String(id || '')) || null
+ }
+
+ const focusFloatingWindow = (id) => {
+ const windowItem = getFloatingWindowById(id)
+ if (!windowItem) return
+ floatingWindowZ += 1
+ windowItem.zIndex = floatingWindowZ
+ }
+
+ const closeFloatingWindow = (id) => {
+ const key = String(id || '')
+ floatingWindows.value = (Array.isArray(floatingWindows.value) ? floatingWindows.value : []).filter((item) => String(item?.id || '') !== key)
+ if (floatingDragState.id && String(floatingDragState.id) === key) {
+ floatingDragState.id = ''
+ }
+ }
+
+ const closeTopFloatingWindow = () => {
+ const list = Array.isArray(floatingWindows.value) ? floatingWindows.value : []
+ if (!list.length) return
+ const top = [...list].sort((a, b) => Number(b?.zIndex || 0) - Number(a?.zIndex || 0))[0]
+ if (top?.id) closeFloatingWindow(top.id)
+ }
+
+ const openFloatingWindow = (payload) => {
+ if (!process.client || typeof window === 'undefined') return null
+ floatingWindowSeq += 1
+ floatingWindowZ += 1
+ const width = clampNumber(Number(payload?.width || 520), 360, Math.max(360, (window.innerWidth || 1200) - 48))
+ const height = clampNumber(Number(payload?.height || 420), 320, Math.max(320, (window.innerHeight || 900) - 48))
+ const x = clampNumber(Number(payload?.x || Math.round(((window.innerWidth || width) - width) / 2)), 16, Math.max(16, (window.innerWidth || width) - width - 16))
+ const y = clampNumber(Number(payload?.y || Math.round(((window.innerHeight || height) - height) / 2)), 16, Math.max(16, (window.innerHeight || height) - height - 16))
+
+ const windowItem = {
+ id: `chat-floating-${floatingWindowSeq}`,
+ kind: String(payload?.kind || 'chatHistory'),
+ title: String(payload?.title || ''),
+ info: payload?.info || { isChatRoom: false },
+ records: Array.isArray(payload?.records) ? payload.records : [],
+ url: String(payload?.url || ''),
+ content: String(payload?.content || ''),
+ preview: String(payload?.preview || ''),
+ from: String(payload?.from || ''),
+ fromAvatar: String(payload?.fromAvatar || ''),
+ loading: !!payload?.loading,
+ width,
+ height,
+ x,
+ y,
+ zIndex: floatingWindowZ
+ }
+ floatingWindows.value = [...floatingWindows.value, windowItem]
+ return windowItem
+ }
+
+ const startFloatingWindowDrag = (id, event) => {
+ if (!process.client) return
+ const windowItem = getFloatingWindowById(id)
+ if (!windowItem) return
+ focusFloatingWindow(id)
+ const point = 'touches' in event ? event.touches?.[0] : event
+ floatingDragState.id = id
+ floatingDragState.offsetX = Number(point?.clientX || 0) - Number(windowItem.x || 0)
+ floatingDragState.offsetY = Number(point?.clientY || 0) - Number(windowItem.y || 0)
+ }
+
+ const onFloatingWindowMouseMove = (event) => {
+ if (!process.client) return
+ if (!floatingDragState.id) return
+ const windowItem = getFloatingWindowById(floatingDragState.id)
+ if (!windowItem) return
+ const point = 'touches' in event ? event.touches?.[0] : event
+ const nextX = Number(point?.clientX || 0) - floatingDragState.offsetX
+ const nextY = Number(point?.clientY || 0) - floatingDragState.offsetY
+ windowItem.x = clampNumber(nextX, 8, Math.max(8, (window.innerWidth || nextX) - windowItem.width - 8))
+ windowItem.y = clampNumber(nextY, 8, Math.max(8, (window.innerHeight || nextY) - windowItem.height - 8))
+ }
+
+ const onFloatingWindowMouseUp = () => {
+ floatingDragState.id = ''
+ }
+
+ const chatHistoryModalVisible = ref(false)
+ const chatHistoryModalTitle = ref('')
+ const chatHistoryModalRecords = ref([])
+ const chatHistoryModalInfo = ref({ isChatRoom: false })
+ const chatHistoryModalStack = ref([])
+ const goBackChatHistoryModal = () => {}
+ const closeChatHistoryModal = () => {
+ chatHistoryModalVisible.value = false
+ chatHistoryModalTitle.value = ''
+ chatHistoryModalRecords.value = []
+ chatHistoryModalInfo.value = { isChatRoom: false }
+ chatHistoryModalStack.value = []
+ }
+
+ const onChatHistoryVideoThumbError = (record) => {
+ if (!record) return
+ const candidates = record._videoThumbCandidates
+ if (!Array.isArray(candidates) || candidates.length <= 1) {
+ record._videoThumbError = true
+ return
+ }
+ const current = Math.max(0, Number(record._videoThumbCandidateIndex || 0))
+ const next = current + 1
+ if (next < candidates.length) {
+ record._videoThumbCandidateIndex = next
+ record.videoThumbUrl = candidates[next]
+ return
+ }
+ record._videoThumbError = true
+ }
+
+ const onChatHistoryLinkPreviewError = (record) => {
+ if (!record) return
+ const candidates = record._linkPreviewCandidates
+ if (!Array.isArray(candidates) || candidates.length <= 1) {
+ record._linkPreviewError = true
+ return
+ }
+ const current = Math.max(0, Number(record._linkPreviewCandidateIndex || 0))
+ const next = current + 1
+ if (next < candidates.length) {
+ record._linkPreviewCandidateIndex = next
+ record.preview = candidates[next]
+ record._linkPreviewError = false
+ return
+ }
+ record._linkPreviewError = true
+ }
+
+ const onChatHistoryFromAvatarLoad = (record) => {
+ try {
+ if (record) {
+ record._fromAvatarImgOk = true
+ record._fromAvatarImgError = false
+ record._fromAvatarLast = String(record.fromAvatar || '').trim()
+ }
+ } catch {}
+ }
+
+ const onChatHistoryFromAvatarError = (record) => {
+ try {
+ if (record) {
+ record._fromAvatarImgOk = false
+ record._fromAvatarImgError = true
+ record._fromAvatarLast = String(record.fromAvatar || '').trim()
+ }
+ } catch {}
+ }
+
+ const onChatHistoryQuoteThumbError = (record) => {
+ if (!record || !record.quote) return
+ const candidates = record._quoteThumbCandidates
+ if (!Array.isArray(candidates) || candidates.length <= 1) {
+ record._quoteThumbError = true
+ return
+ }
+ const current = Math.max(0, Number(record._quoteThumbCandidateIndex || 0))
+ const next = current + 1
+ if (next < candidates.length) {
+ record._quoteThumbCandidateIndex = next
+ record.quote.thumbUrl = candidates[next]
+ return
+ }
+ record._quoteThumbError = true
+ }
+
+ const openChatHistoryQuote = (record) => {
+ if (!process.client) return
+ const quote = record?.quote
+ if (!quote) return
+ const kind = String(quote.kind || '')
+ const url = String(quote.url || '').trim()
+ if (!url) return
+
+ if (kind === 'video') {
+ openVideoPreview(url, quote?.thumbUrl)
+ return
+ }
+ if (kind === 'image' || kind === 'emoji') {
+ openImagePreview(url)
+ }
+ }
+
+ const getChatHistoryLinkFromText = (record) => {
+ const from = String(record?.from || '').trim()
+ if (from) return from
+ const url = String(record?.url || '').trim()
+ if (!url) return ''
+ try { return new URL(url).hostname || '' } catch { return '' }
+ }
+
+ const getChatHistoryLinkFromAvatarText = (record) => {
+ const text = String(getChatHistoryLinkFromText(record) || '').trim()
+ return text ? (Array.from(text)[0] || '') : ''
+ }
+
+ const openUrlInBrowser = (url) => {
+ const next = String(url || '').trim()
+ if (!next) return
+ try { window.open(next, '_blank', 'noopener,noreferrer') } catch {}
+ }
+
+ const resolveChatHistoryLinkRecord = async (record) => {
+ if (!process.client || !record || !selectedAccount.value) return null
+ const serverId = String(record?.fromnewmsgid || '').trim()
+ if (!serverId || record._linkResolving) return null
+
+ record._linkResolving = true
+ try {
+ const response = await api.resolveAppMsg({
+ account: selectedAccount.value,
+ server_id: serverId
+ })
+ if (response && typeof response === 'object') {
+ const title = String(response.title || '').trim()
+ const content = String(response.content || '').trim()
+ const url = String(response.url || '').trim()
+ const from = String(response.from || '').trim()
+
+ const normalizePreviewUrl = (value) => {
+ const raw = String(value || '').trim()
+ if (!raw) return ''
+ if (/^\/api\/chat\/media\//i.test(raw) || /^blob:/i.test(raw) || /^data:/i.test(raw)) return raw
+ if (!/^https?:\/\//i.test(raw)) return ''
+ try {
+ const host = new URL(raw).hostname.toLowerCase()
+ if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
+ return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
+ }
+ } catch {}
+ return raw
+ }
+
+ if (title) record.title = title
+ if (content && !stripWeChatInvisible(record.content)) record.content = content
+ if (url) record.url = url
+ if (from) record.from = from
+ if (response.linkStyle) record.linkStyle = String(response.linkStyle || '').trim()
+ if (response.linkType) record.linkType = String(response.linkType || '').trim()
+
+ const fromUsername = String(response.fromUsername || '').trim()
+ if (fromUsername) record.fromUsername = fromUsername
+ const fromAvatarUrl = fromUsername
+ ? `${apiBase}/chat/avatar?account=${encodeURIComponent(selectedAccount.value || '')}&username=${encodeURIComponent(fromUsername)}`
+ : (url ? `${apiBase}/chat/media/favicon?url=${encodeURIComponent(url)}` : '')
+ if (fromAvatarUrl) {
+ const last = String(record._fromAvatarLast || '').trim()
+ record.fromAvatar = fromAvatarUrl
+ if (String(fromAvatarUrl).trim() !== last) {
+ record._fromAvatarLast = String(fromAvatarUrl).trim()
+ record._fromAvatarImgOk = false
+ record._fromAvatarImgError = false
+ }
+ }
+
+ const style = String(response.linkStyle || '').trim()
+ const thumb = String(response.thumbUrl || '').trim()
+ const cover = String(response.coverUrl || '').trim()
+ const picked = style === 'cover' ? (cover || thumb) : (thumb || cover)
+ const previewResolved = normalizePreviewUrl(picked)
+ if (previewResolved) {
+ const currentPreview = String(record.preview || '').trim()
+ const candidates = Array.isArray(record._linkPreviewCandidates) ? record._linkPreviewCandidates.slice() : []
+ if (currentPreview && !candidates.includes(currentPreview)) candidates.push(currentPreview)
+ if (!candidates.includes(previewResolved)) candidates.push(previewResolved)
+ record._linkPreviewCandidates = candidates
+ if (!currentPreview || record._linkPreviewError) {
+ record.preview = previewResolved
+ record._linkPreviewCandidateIndex = candidates.indexOf(previewResolved)
+ record._linkPreviewError = false
+ }
+ }
+ return response
+ }
+ } catch {}
+ finally {
+ try { record._linkResolving = false } catch {}
+ }
+ return null
+ }
+
+ const resolveChatHistoryLinkRecords = (windowItem) => {
+ if (!process.client) return
+ const records = Array.isArray(windowItem?.records) ? windowItem.records : []
+ const targets = records.filter((record) => {
+ if (!record) return false
+ if (String(record.renderType || '') !== 'link') return false
+ if (!String(record.fromnewmsgid || '').trim()) return false
+ const fromMissing = String(record.from || '').trim() === ''
+ const previewMissing = !String(record.preview || '').trim()
+ const urlMissing = !String(record.url || '').trim()
+ const fromAvatarMissing = !String(record.fromAvatar || '').trim()
+ return fromMissing || previewMissing || urlMissing || fromAvatarMissing
+ })
+ if (!targets.length) return
+ ;(async () => {
+ for (const target of targets.slice(0, 12)) {
+ await resolveChatHistoryLinkRecord(target)
+ }
+ })()
+ }
+
+ const openChatHistoryLinkWindow = (record) => {
+ if (!process.client) return
+ const title = String(record?.title || record?.content || '链接').trim()
+ const url = String(record?.url || '').trim()
+ const preview = String(record?.preview || '').trim()
+ const from = String(record?.from || '').trim()
+ const fromAvatar = String(record?.fromAvatar || '').trim()
+ const needResolve = !!String(record?.fromnewmsgid || '').trim() && (!url || !from || !preview || !fromAvatar)
+ const windowItem = openFloatingWindow({
+ kind: 'link',
+ title: title || '链接',
+ url,
+ content: String(record?.content || '').trim(),
+ preview,
+ from,
+ fromAvatar,
+ width: 520,
+ height: 420,
+ loading: needResolve
+ })
+ if (!windowItem) return
+ focusFloatingWindow(windowItem.id)
+ try {
+ windowItem._linkPreviewCandidates = Array.isArray(record?._linkPreviewCandidates) ? record._linkPreviewCandidates.slice() : (preview ? [preview] : [])
+ windowItem._linkPreviewCandidateIndex = Math.max(0, Number(record?._linkPreviewCandidateIndex || 0))
+ windowItem._linkPreviewError = false
+ windowItem._fromAvatarLast = fromAvatar
+ windowItem._fromAvatarImgOk = !!record?._fromAvatarImgOk
+ windowItem._fromAvatarImgError = !!record?._fromAvatarImgError
+ windowItem.fromnewmsgid = String(record?.fromnewmsgid || '').trim()
+ } catch {}
+ if (needResolve) {
+ ;(async () => {
+ await resolveChatHistoryLinkRecord(windowItem)
+ windowItem.loading = false
+ })()
+ }
+ }
+
+ const openChatHistoryModal = (message) => {
+ if (!process.client) return
+ const { title0, info0, records0 } = buildChatHistoryWindowPayload(message, normalizeRecordItem)
+ const windowItem = openFloatingWindow({
+ kind: 'chatHistory',
+ title: title0 || '聊天记录',
+ info: info0,
+ records: records0,
+ width: 560,
+ height: Math.round(Math.max(420, (window.innerHeight || 700) * 0.78))
+ })
+ if (!windowItem) return
+ try { resolveChatHistoryLinkRecords(windowItem) } catch {}
+ }
+
+ const openNestedChatHistory = (record) => {
+ if (!process.client) return
+ const title = String(record?.title || '聊天记录')
+ const content = String(record?.content || '')
+ const recordItem = String(record?.recordItem || '').trim()
+ const serverId = String(record?.fromnewmsgid || '').trim()
+
+ const { info0, records0 } = buildChatHistoryWindowPayload({ title, content, recordItem }, normalizeRecordItem)
+ const windowItem = openFloatingWindow({
+ kind: 'chatHistory',
+ title: title || '聊天记录',
+ info: info0,
+ records: records0,
+ width: 560,
+ height: Math.round(Math.max(420, (window.innerHeight || 700) * 0.78)),
+ loading: false
+ })
+ if (!windowItem) return
+ try { resolveChatHistoryLinkRecords(windowItem) } catch {}
+
+ if (!serverId || !selectedAccount.value || record?._nestedResolving || !isChatHistoryRecordItemIncomplete(recordItem)) return
+ record._nestedResolving = true
+ windowItem.loading = true
+
+ ;(async () => {
+ try {
+ const response = await api.resolveNestedChatHistory({
+ account: selectedAccount.value,
+ server_id: serverId
+ })
+ const resolved = String(response?.recordItem || '').trim()
+ if (!resolved) return
+ windowItem.title = String(response?.title || title || '聊天记录')
+ const parsed = parseChatHistoryRecord(resolved)
+ windowItem.info = parsed?.info || { isChatRoom: false, count: 0 }
+ const items = Array.isArray(parsed?.items) ? parsed.items : []
+ windowItem.records = items.length ? enhanceChatHistoryRecords(items.map(normalizeRecordItem)) : []
+ if (!windowItem.records.length) {
+ const lines = String(response?.content || content || '').trim().split(/\r?\n/).map((item) => item.trim()).filter(Boolean)
+ windowItem.info = { isChatRoom: false, count: 0 }
+ windowItem.records = lines.map((line, idx) => normalizeRecordItem({
+ id: String(idx),
+ datatype: '1',
+ sourcename: '',
+ sourcetime: '',
+ content: line,
+ renderType: 'text'
+ }))
+ }
+ try { resolveChatHistoryLinkRecords(windowItem) } catch {}
+ } catch {}
+ finally {
+ windowItem.loading = false
+ try { record._nestedResolving = false } catch {}
+ }
+ })()
+ }
+
+ return {
+ floatingWindows,
+ chatHistoryModalVisible,
+ chatHistoryModalTitle,
+ chatHistoryModalRecords,
+ chatHistoryModalInfo,
+ chatHistoryModalStack,
+ goBackChatHistoryModal,
+ closeChatHistoryModal,
+ getFloatingWindowById,
+ focusFloatingWindow,
+ closeFloatingWindow,
+ closeTopFloatingWindow,
+ openFloatingWindow,
+ startFloatingWindowDrag,
+ onFloatingWindowMouseMove,
+ onFloatingWindowMouseUp,
+ formatChatHistoryVideoDuration,
+ getChatHistoryPreviewLines,
+ onChatHistoryVideoThumbError,
+ onChatHistoryLinkPreviewError,
+ onChatHistoryFromAvatarLoad,
+ onChatHistoryFromAvatarError,
+ onChatHistoryQuoteThumbError,
+ openChatHistoryQuote,
+ getChatHistoryLinkFromText,
+ getChatHistoryLinkFromAvatarText,
+ openUrlInBrowser,
+ resolveChatHistoryLinkRecord,
+ resolveChatHistoryLinkRecords,
+ openChatHistoryLinkWindow,
+ openChatHistoryModal,
+ openNestedChatHistory
+ }
+}
diff --git a/frontend/composables/chat/useChatMessages.js b/frontend/composables/chat/useChatMessages.js
new file mode 100644
index 0000000..d1919b5
--- /dev/null
+++ b/frontend/composables/chat/useChatMessages.js
@@ -0,0 +1,967 @@
+import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
+import {
+ formatFileSize,
+ formatTimeDivider,
+ getVoiceDurationInSeconds,
+ getVoiceWidth
+} from '~/lib/chat/formatters'
+import { createMessageNormalizer, dedupeMessagesById } from '~/lib/chat/message-normalizer'
+
+export const useChatMessages = ({
+ api,
+ apiBase,
+ selectedAccount,
+ selectedContact,
+ realtimeStore,
+ realtimeEnabled,
+ desktopAutoRealtime,
+ privacyMode,
+ searchContext
+}) => {
+ const messagePageSize = 50
+
+ const allMessages = ref({})
+ const messagesMeta = ref({})
+ const isLoadingMessages = ref(false)
+ const messagesError = ref('')
+ const messageContainerRef = ref(null)
+ const activeMessagesFor = ref('')
+ const showJumpToBottom = ref(false)
+ let lastRenderMessagesFingerprint = ''
+
+ const isDesktopRenderer = () => {
+ if (!process.client || typeof window === 'undefined') return false
+ return !!window.wechatDesktop?.__brand
+ }
+
+ const logMessagePhase = (phase, details = {}) => {
+ const payload = {
+ account: String(selectedAccount.value || '').trim(),
+ selectedUsername: String(selectedContact.value?.username || '').trim(),
+ activeMessagesFor: String(activeMessagesFor.value || '').trim(),
+ ...details
+ }
+
+ if (isDesktopRenderer()) {
+ try {
+ window.wechatDesktop?.logDebug?.('chat-messages', phase, payload)
+ } catch {}
+ }
+
+ console.info(`[chat-messages] ${phase}`, payload)
+ }
+
+ const summarizeRenderTypes = (list) => {
+ const counts = {}
+ for (const item of Array.isArray(list) ? list : []) {
+ const key = String(item?.renderType || 'unknown').trim() || 'unknown'
+ counts[key] = Number(counts[key] || 0) + 1
+ }
+ return counts
+ }
+
+ const previewImageUrl = ref(null)
+ const previewVideoUrl = ref(null)
+ const previewVideoPosterUrl = ref('')
+ const previewVideoError = ref('')
+
+ const voiceRefs = new Map()
+ const currentPlayingVoice = ref(null)
+ const playingVoiceId = ref(null)
+
+ const highlightServerIdStr = ref('')
+ const highlightMessageId = ref('')
+ let highlightTimer = null
+
+ const messageTypeFilter = ref('all')
+ const localMediaVersion = ref(0)
+ const messageTypeFilterOptions = [
+ { value: 'all', label: '全部' },
+ { value: 'text', label: '文本' },
+ { value: 'image', label: '图片' },
+ { value: 'emoji', label: '表情' },
+ { value: 'video', label: '视频' },
+ { value: 'voice', label: '语音' },
+ { value: 'file', label: '文件' },
+ { value: 'link', label: '链接' },
+ { value: 'quote', label: '引用' },
+ { value: 'chatHistory', label: '聊天记录' },
+ { value: 'transfer', label: '转账' },
+ { value: 'redPacket', label: '红包' },
+ { value: 'location', label: '位置' },
+ { value: 'voip', label: '通话' },
+ { value: 'system', label: '系统' }
+ ]
+
+ const normalizeMessage = createMessageNormalizer({
+ apiBase,
+ getSelectedAccount: () => selectedAccount.value,
+ getSelectedContact: () => selectedContact.value,
+ getLocalMediaVersion: () => localMediaVersion.value
+ })
+
+ const bumpLocalMediaVersion = () => {
+ localMediaVersion.value = (localMediaVersion.value + 1) % 1000000000
+ return localMediaVersion.value
+ }
+
+ const renormalizeLoadedMessages = (username) => {
+ const key = String(username || '').trim()
+ if (!key) return
+ const existing = allMessages.value[key]
+ if (!Array.isArray(existing) || !existing.length) return
+
+ const refreshed = dedupeMessagesById(existing.map((message) => {
+ const normalized = normalizeMessage(message)
+ return {
+ ...message,
+ ...normalized,
+ _emojiDownloading: !!message?._emojiDownloading,
+ _emojiDownloaded: typeof message?._emojiDownloaded === 'boolean' ? message._emojiDownloaded : normalized._emojiDownloaded,
+ _quoteImageError: false,
+ _quoteThumbError: false
+ }
+ }))
+
+ allMessages.value = {
+ ...allMessages.value,
+ [key]: refreshed
+ }
+ }
+
+ const messages = computed(() => {
+ if (!selectedContact.value) return []
+ return allMessages.value[selectedContact.value.username] || []
+ })
+
+ const hasMoreMessages = computed(() => {
+ if (!selectedContact.value) return false
+ const key = selectedContact.value.username
+ const meta = messagesMeta.value[key]
+ if (!meta) return false
+ if (meta.hasMore != null) return !!meta.hasMore
+ const total = Number(meta.total || 0)
+ const loaded = messages.value.length
+ return total > loaded
+ })
+
+ const reverseMessageSides = ref(false)
+ const reverseSidesStorageKey = computed(() => {
+ const account = String(selectedAccount.value || '').trim()
+ const username = String(selectedContact.value?.username || '').trim()
+ if (account && username) return `wechatda:reverse_message_sides:${account}:${username}`
+ return 'wechatda:reverse_message_sides:global'
+ })
+
+ const loadReverseMessageSides = () => {
+ if (!process.client) return
+ try {
+ const value = localStorage.getItem(reverseSidesStorageKey.value)
+ reverseMessageSides.value = value === '1'
+ } catch {}
+ }
+
+ watch(reverseSidesStorageKey, () => loadReverseMessageSides(), { immediate: true })
+ watch(reverseMessageSides, (value) => {
+ if (!process.client) return
+ try {
+ localStorage.setItem(reverseSidesStorageKey.value, value ? '1' : '0')
+ } catch {}
+ })
+
+ const toggleReverseMessageSides = () => {
+ reverseMessageSides.value = !reverseMessageSides.value
+ }
+
+ const renderMessages = computed(() => {
+ const list = messages.value || []
+ const reverseSides = !!reverseMessageSides.value
+ const fingerprint = `${String(selectedContact.value?.username || '').trim()}:${list.length}:${reverseSides ? '1' : '0'}`
+ const shouldLogRender = isDesktopRenderer() && fingerprint !== lastRenderMessagesFingerprint
+ if (shouldLogRender) {
+ logMessagePhase('renderMessages:start', {
+ count: list.length,
+ reverseSides
+ })
+ }
+ let previousTs = 0
+ const rendered = list.map((message) => {
+ const ts = Number(message.createTime || 0)
+ const show = !previousTs || (ts && Math.abs(ts - previousTs) >= 300)
+ if (ts) previousTs = ts
+ const originalIsSent = !!message?.isSent
+ return {
+ ...message,
+ _originalIsSent: originalIsSent,
+ isSent: reverseSides ? !originalIsSent : originalIsSent,
+ showTimeDivider: !!show,
+ timeDivider: formatTimeDivider(ts)
+ }
+ })
+ if (shouldLogRender) {
+ lastRenderMessagesFingerprint = fingerprint
+ logMessagePhase('renderMessages:end', {
+ count: rendered.length,
+ reverseSides
+ })
+ }
+ return rendered
+ })
+
+ const updateJumpToBottomState = () => {
+ const container = messageContainerRef.value
+ if (!container) {
+ showJumpToBottom.value = false
+ return
+ }
+ const distance = container.scrollHeight - container.scrollTop - container.clientHeight
+ showJumpToBottom.value = distance > 160
+ }
+
+ const scrollToBottom = () => {
+ const container = messageContainerRef.value
+ if (!container) return
+ container.scrollTop = container.scrollHeight
+ updateJumpToBottomState()
+ }
+
+ const flashMessage = (id) => {
+ highlightMessageId.value = String(id || '').trim()
+ if (highlightTimer) clearTimeout(highlightTimer)
+ highlightTimer = setTimeout(() => {
+ highlightMessageId.value = ''
+ highlightServerIdStr.value = ''
+ highlightTimer = null
+ }, 2200)
+ }
+
+ const scrollToMessageId = async (id) => {
+ const target = String(id || '').trim()
+ if (!target) return false
+ await nextTick()
+ const container = messageContainerRef.value
+ const element = container?.querySelector?.(`[data-msg-id="${CSS.escape(target)}"]`)
+ if (!element || typeof element.scrollIntoView !== 'function') return false
+ element.scrollIntoView({ block: 'center', behavior: 'smooth' })
+ return true
+ }
+
+ const openImagePreview = (url) => {
+ previewImageUrl.value = String(url || '').trim() || null
+ }
+
+ const closeImagePreview = () => {
+ previewImageUrl.value = null
+ }
+
+ const openVideoPreview = (url, poster) => {
+ previewVideoUrl.value = String(url || '').trim() || null
+ previewVideoPosterUrl.value = String(poster || '').trim()
+ previewVideoError.value = ''
+ }
+
+ const closeVideoPreview = () => {
+ previewVideoUrl.value = null
+ previewVideoPosterUrl.value = ''
+ previewVideoError.value = ''
+ }
+
+ const onPreviewVideoError = () => {
+ previewVideoError.value = '视频加载失败,可能是资源不存在或无法访问。'
+ }
+
+ const setVoiceRef = (id, element) => {
+ const key = String(id || '').trim()
+ if (!key) return
+ if (element) {
+ voiceRefs.set(key, element)
+ } else {
+ voiceRefs.delete(key)
+ }
+ }
+
+ const playVoiceById = async (voiceId) => {
+ const key = String(voiceId || '').trim()
+ if (!key) return
+ const audio = voiceRefs.get(key)
+ if (!audio) return
+
+ try {
+ if (currentPlayingVoice.value && currentPlayingVoice.value !== audio) {
+ currentPlayingVoice.value.pause()
+ currentPlayingVoice.value.currentTime = 0
+ }
+ } catch {}
+
+ if (currentPlayingVoice.value === audio && !audio.paused) {
+ try {
+ audio.pause()
+ audio.currentTime = 0
+ } catch {}
+ currentPlayingVoice.value = null
+ playingVoiceId.value = null
+ return
+ }
+
+ try {
+ await audio.play()
+ currentPlayingVoice.value = audio
+ playingVoiceId.value = key
+ audio.onended = () => {
+ if (playingVoiceId.value === key) {
+ currentPlayingVoice.value = null
+ playingVoiceId.value = null
+ }
+ }
+ } catch {}
+ }
+
+ const playVoice = async (message) => {
+ await playVoiceById(message?.id)
+ }
+
+ const getQuoteVoiceId = (message) => `quote-${String(message?.quoteServerId || message?.id || '')}`
+
+ const playQuoteVoice = async (message) => {
+ await playVoiceById(getQuoteVoiceId(message))
+ }
+
+ const isQuotedVoice = (message) => String(message?.quoteType || '').trim() === '34'
+ const isQuotedImage = (message) => {
+ return !!String(message?.quoteImageUrl || '').trim() || String(message?.quoteContent || '').trim() === '[图片]'
+ }
+ const isQuotedLink = (message) => {
+ return String(message?.quoteType || '').trim() === '5' || !!String(message?.quoteThumbUrl || '').trim()
+ }
+ const getQuotedLinkText = (message) => {
+ const title = String(message?.quoteTitle || '').trim()
+ const content = String(message?.quoteContent || '').trim()
+ return content || title || ''
+ }
+
+ const onQuoteImageError = (message) => {
+ if (message) message._quoteImageError = true
+ }
+
+ const onQuoteThumbError = (message) => {
+ if (message) message._quoteThumbError = true
+ }
+
+ const onAvatarError = (event, target) => {
+ try { event?.target && (event.target.style.display = 'none') } catch {}
+ try { if (target) target.avatar = null } catch {}
+ }
+
+ const shouldShowEmojiDownload = (message) => {
+ if (!message?.emojiMd5) return false
+ const url = String(message?.emojiRemoteUrl || '').trim()
+ if (!url) return false
+ if (!/^https?:\/\//i.test(url)) return false
+ return true
+ }
+
+ const onEmojiDownloadClick = async (message) => {
+ if (!process.client) return
+ if (!message?.emojiMd5) return
+ if (!selectedAccount.value) return
+
+ const emojiUrl = String(message?.emojiRemoteUrl || '').trim()
+ if (!emojiUrl) {
+ window.alert('该表情没有可用的下载地址')
+ return
+ }
+ if (message._emojiDownloading) return
+
+ message._emojiDownloading = true
+ try {
+ await api.downloadChatEmoji({
+ account: selectedAccount.value,
+ md5: message.emojiMd5,
+ emoji_url: emojiUrl,
+ force: false
+ })
+ message._emojiDownloaded = true
+ if (message.emojiLocalUrl) {
+ message.emojiUrl = message.emojiLocalUrl
+ }
+ } catch (error) {
+ window.alert(error?.message || '下载失败')
+ } finally {
+ message._emojiDownloading = false
+ }
+ }
+
+ const onFileClick = async (message) => {
+ if (!message?.fileMd5) return
+ try {
+ if (!selectedAccount.value) return
+ if (!selectedContact.value?.username) return
+ await api.openChatMediaFolder({
+ account: selectedAccount.value,
+ username: selectedContact.value.username,
+ kind: 'file',
+ md5: message.fileMd5
+ })
+ } catch (error) {
+ console.error('打开文件夹失败:', error)
+ }
+ }
+
+ const loadMessages = async ({ username, reset }) => {
+ if (!username || !selectedAccount.value) return
+
+ logMessagePhase('loadMessages:enter', {
+ username,
+ reset
+ })
+ messagesError.value = ''
+ isLoadingMessages.value = true
+ activeMessagesFor.value = username
+
+ try {
+ const existing = allMessages.value[username] || []
+ const container = messageContainerRef.value
+ const beforeScrollHeight = container ? container.scrollHeight : 0
+ const beforeScrollTop = container ? container.scrollTop : 0
+ const offset = reset ? 0 : existing.length
+
+ const params = {
+ account: selectedAccount.value,
+ username,
+ limit: messagePageSize,
+ offset,
+ order: 'asc'
+ }
+ if (messageTypeFilter.value && messageTypeFilter.value !== 'all') {
+ params.render_types = messageTypeFilter.value
+ }
+ if (realtimeEnabled.value) {
+ params.source = 'realtime'
+ }
+ logMessagePhase('loadMessages:request:start', {
+ username,
+ reset,
+ offset,
+ existingCount: existing.length,
+ renderTypeFilter: messageTypeFilter.value,
+ realtime: !!realtimeEnabled.value
+ })
+ const response = await api.listChatMessages(params)
+ logMessagePhase('loadMessages:request:end', {
+ username,
+ reset,
+ rawCount: Array.isArray(response?.messages) ? response.messages.length : 0,
+ total: Number(response?.total || 0),
+ hasMore: response?.hasMore
+ })
+
+ const raw = response?.messages || []
+ logMessagePhase('loadMessages:normalize:start', {
+ username,
+ rawCount: raw.length
+ })
+ const mapped = dedupeMessagesById(raw.map(normalizeMessage))
+ logMessagePhase('loadMessages:normalize:end', {
+ username,
+ mappedCount: mapped.length,
+ renderTypeCounts: summarizeRenderTypes(mapped)
+ })
+
+ if (activeMessagesFor.value !== username) {
+ logMessagePhase('loadMessages:abort-stale', {
+ username,
+ activeMessagesFor: activeMessagesFor.value
+ })
+ return
+ }
+
+ logMessagePhase('loadMessages:state-commit:start', {
+ username,
+ reset,
+ mappedCount: mapped.length
+ })
+ if (reset) {
+ allMessages.value = { ...allMessages.value, [username]: mapped }
+ } else {
+ const existingIds = new Set(existing.map((message) => String(message?.id || '')))
+ const older = mapped.filter((message) => {
+ const id = String(message?.id || '')
+ if (!id) return true
+ if (existingIds.has(id)) return false
+ existingIds.add(id)
+ return true
+ })
+ allMessages.value = {
+ ...allMessages.value,
+ [username]: [...older, ...existing]
+ }
+ }
+ logMessagePhase('loadMessages:state-commit:end', {
+ username,
+ storedCount: (allMessages.value[username] || []).length
+ })
+
+ messagesMeta.value = {
+ ...messagesMeta.value,
+ [username]: {
+ total: Number(response?.total || 0),
+ hasMore: response?.hasMore
+ }
+ }
+ logMessagePhase('loadMessages:meta-commit:end', {
+ username,
+ total: Number(response?.total || 0),
+ hasMore: response?.hasMore
+ })
+
+ logMessagePhase('loadMessages:nextTick:start', {
+ username
+ })
+ await nextTick()
+ logMessagePhase('loadMessages:nextTick:end', {
+ username,
+ renderedCount: (allMessages.value[username] || []).length
+ })
+ const nextContainer = messageContainerRef.value
+ if (nextContainer) {
+ if (reset) {
+ nextContainer.scrollTop = nextContainer.scrollHeight
+ } else {
+ const afterScrollHeight = nextContainer.scrollHeight
+ nextContainer.scrollTop = beforeScrollTop + (afterScrollHeight - beforeScrollHeight)
+ }
+ }
+ updateJumpToBottomState()
+ logMessagePhase('loadMessages:scroll:end', {
+ username,
+ hasContainer: !!nextContainer,
+ scrollTop: nextContainer ? nextContainer.scrollTop : null,
+ scrollHeight: nextContainer ? nextContainer.scrollHeight : null
+ })
+ } catch (error) {
+ console.error('[chat-messages] loadMessages:error', {
+ account: String(selectedAccount.value || '').trim(),
+ username: String(username || '').trim(),
+ reset: !!reset,
+ error
+ })
+ messagesError.value = error?.message || '加载聊天记录失败'
+ } finally {
+ isLoadingMessages.value = false
+ logMessagePhase('loadMessages:exit', {
+ username,
+ reset,
+ loading: isLoadingMessages.value,
+ error: messagesError.value
+ })
+ }
+ }
+
+ const loadMoreMessages = async () => {
+ if (!selectedContact.value) return
+ if (searchContext.value?.active) return
+ await loadMessages({ username: selectedContact.value.username, reset: false })
+ }
+
+ const refreshSelectedMessages = async () => {
+ if (!selectedContact.value) return
+ bumpLocalMediaVersion()
+ await loadMessages({ username: selectedContact.value.username, reset: true })
+ }
+
+ const refreshCurrentMessageMedia = async () => {
+ if (!selectedContact.value?.username) return
+ bumpLocalMediaVersion()
+ renormalizeLoadedMessages(selectedContact.value.username)
+ await nextTick()
+ }
+
+ const refreshRealtimeIncremental = async () => {
+ if (!realtimeEnabled.value || !selectedAccount.value || !selectedContact.value?.username) return
+ if (searchContext.value?.active || isLoadingMessages.value) return
+
+ const username = selectedContact.value.username
+ const existing = allMessages.value[username] || []
+ if (!existing.length) return
+
+ const container = messageContainerRef.value
+ const atBottom = !!container && (container.scrollHeight - container.scrollTop - container.clientHeight) < 80
+
+ const params = {
+ account: selectedAccount.value,
+ username,
+ limit: 30,
+ offset: 0,
+ order: 'asc',
+ source: 'realtime'
+ }
+ if (messageTypeFilter.value && messageTypeFilter.value !== 'all') {
+ params.render_types = messageTypeFilter.value
+ }
+
+ try {
+ const response = await api.listChatMessages(params)
+ if (selectedContact.value?.username !== username) return
+
+ const rawMessages = response?.messages || []
+ const latest = rawMessages.map(normalizeMessage)
+
+ const seenIds = new Set(existing.map((message) => String(message?.id || '')))
+ const newOnes = []
+ for (const message of latest) {
+ const id = String(message?.id || '')
+ if (!id || seenIds.has(id)) continue
+ seenIds.add(id)
+ newOnes.push(message)
+ }
+ if (!newOnes.length) return
+
+ allMessages.value = { ...allMessages.value, [username]: [...existing, ...newOnes] }
+
+ await nextTick()
+ const nextContainer = messageContainerRef.value
+ if (nextContainer && atBottom) {
+ nextContainer.scrollTop = nextContainer.scrollHeight
+ }
+ updateJumpToBottomState()
+ } catch (error) {
+ console.error('[chat-messages] refreshRealtimeIncremental:error', {
+ account: String(selectedAccount.value || '').trim(),
+ username: String(username || '').trim(),
+ error
+ })
+ }
+ }
+
+ let realtimeRefreshFuture = null
+ let realtimeRefreshQueued = false
+
+ const queueRealtimeRefresh = () => {
+ if (realtimeRefreshFuture) {
+ realtimeRefreshQueued = true
+ return
+ }
+
+ realtimeRefreshFuture = refreshRealtimeIncremental().finally(() => {
+ realtimeRefreshFuture = null
+ if (realtimeRefreshQueued) {
+ realtimeRefreshQueued = false
+ queueRealtimeRefresh()
+ }
+ })
+ }
+
+ const tryEnableRealtimeAuto = async () => {
+ if (!process.client || typeof window === 'undefined') return
+ if (!desktopAutoRealtime.value || realtimeEnabled.value || !selectedAccount.value) return
+ try {
+ await realtimeStore.enable({ silent: true })
+ } catch {}
+ }
+
+ const clearVoicePlaybackState = () => {
+ try {
+ currentPlayingVoice.value?.pause?.()
+ if (currentPlayingVoice.value) currentPlayingVoice.value.currentTime = 0
+ } catch {}
+ currentPlayingVoice.value = null
+ playingVoiceId.value = null
+ voiceRefs.clear()
+ }
+
+ const resetMessageState = () => {
+ clearVoicePlaybackState()
+ allMessages.value = {}
+ messagesMeta.value = {}
+ messagesError.value = ''
+ highlightMessageId.value = ''
+ highlightServerIdStr.value = ''
+ }
+
+ const contactProfileCardOpen = ref(false)
+ const contactProfileCardMessageId = ref('')
+ const contactProfileLoading = ref(false)
+ const contactProfileError = ref('')
+ const contactProfileData = ref(null)
+ let contactProfileHoverHideTimer = null
+
+ const contactProfileResolvedName = computed(() => {
+ const profile = contactProfileData.value || {}
+ const displayName = String(profile?.displayName || '').trim()
+ if (displayName) return displayName
+ const contactName = String(selectedContact.value?.name || '').trim()
+ if (contactName) return contactName
+ return String(profile?.username || selectedContact.value?.username || '').trim()
+ })
+
+ const contactProfileResolvedUsername = computed(() => {
+ const profile = contactProfileData.value || {}
+ return String(profile?.username || selectedContact.value?.username || '').trim()
+ })
+
+ const contactProfileResolvedNickname = computed(() => String(contactProfileData.value?.nickname || '').trim())
+ const contactProfileResolvedAlias = computed(() => String(contactProfileData.value?.alias || '').trim())
+ const contactProfileResolvedRegion = computed(() => String(contactProfileData.value?.region || '').trim())
+ const contactProfileResolvedRemark = computed(() => String(contactProfileData.value?.remark || '').trim())
+ const contactProfileResolvedSignature = computed(() => String(contactProfileData.value?.signature || '').trim())
+ const contactProfileResolvedSource = computed(() => String(contactProfileData.value?.source || '').trim())
+ const contactProfileResolvedAvatar = computed(() => {
+ const avatar = String(contactProfileData.value?.avatar || '').trim()
+ if (avatar) return avatar
+ return String(selectedContact.value?.avatar || '').trim()
+ })
+
+ const contactProfileResolvedGender = computed(() => {
+ const value = contactProfileData.value?.gender
+ if (value == null || value === '') return ''
+ const gender = Number(value)
+ if (!Number.isFinite(gender)) return ''
+ if (gender === 1) return '男'
+ if (gender === 2) return '女'
+ if (gender === 0) return '未知'
+ return String(gender)
+ })
+
+ const contactProfileResolvedSourceScene = computed(() => {
+ const value = contactProfileData.value?.sourceScene
+ if (value == null || value === '') return null
+ const scene = Number(value)
+ return Number.isFinite(scene) ? scene : null
+ })
+
+ const fetchContactProfile = async (options = {}) => {
+ const username = String(options?.username || contactProfileData.value?.username || selectedContact.value?.username || '').trim()
+ const displayNameFallback = String(options?.displayName || '').trim()
+ const avatarFallback = String(options?.avatar || '').trim()
+ const account = String(selectedAccount.value || '').trim()
+ if (!username || !account) {
+ contactProfileData.value = null
+ return
+ }
+
+ contactProfileLoading.value = true
+ contactProfileError.value = ''
+ try {
+ const response = await api.listChatContacts({
+ account,
+ include_friends: true,
+ include_groups: true,
+ include_officials: true
+ })
+ const list = Array.isArray(response?.contacts) ? response.contacts : []
+ const matched = list.find((item) => String(item?.username || '').trim() === username)
+ if (matched) {
+ const normalized = { ...matched, username }
+ if (!String(normalized.displayName || '').trim() && displayNameFallback) {
+ normalized.displayName = displayNameFallback
+ }
+ if (!String(normalized.avatar || '').trim() && avatarFallback) {
+ normalized.avatar = avatarFallback
+ }
+ contactProfileData.value = normalized
+ } else {
+ contactProfileData.value = {
+ username,
+ displayName: displayNameFallback || selectedContact.value?.name || username,
+ avatar: avatarFallback || selectedContact.value?.avatar || '',
+ nickname: '',
+ alias: '',
+ gender: null,
+ region: '',
+ remark: '',
+ signature: '',
+ source: '',
+ sourceScene: null
+ }
+ }
+ } catch (error) {
+ contactProfileData.value = {
+ username,
+ displayName: displayNameFallback || selectedContact.value?.name || username,
+ avatar: avatarFallback || selectedContact.value?.avatar || '',
+ nickname: '',
+ alias: '',
+ gender: null,
+ region: '',
+ remark: '',
+ signature: '',
+ source: '',
+ sourceScene: null
+ }
+ contactProfileError.value = error?.message || '加载联系人资料失败'
+ } finally {
+ contactProfileLoading.value = false
+ }
+ }
+
+ const clearContactProfileHoverHideTimer = () => {
+ if (contactProfileHoverHideTimer) {
+ clearTimeout(contactProfileHoverHideTimer)
+ contactProfileHoverHideTimer = null
+ }
+ }
+
+ const closeContactProfileCard = () => {
+ contactProfileCardOpen.value = false
+ contactProfileCardMessageId.value = ''
+ }
+
+ const onMessageAvatarMouseEnter = async (message) => {
+ if (!!message?.isSent) return
+ const messageId = String(message?.id ?? '').trim()
+ if (!messageId) return
+ const username = String(message?.senderUsername || '').trim()
+ if (!username || username === 'self') return
+
+ const senderName = String(message?.senderDisplayName || message?.sender || '').trim()
+ const senderAvatar = String(message?.avatar || '').trim()
+ if (!contactProfileData.value || String(contactProfileData.value?.username || '').trim() !== username) {
+ contactProfileData.value = {
+ username,
+ displayName: senderName || username,
+ avatar: senderAvatar,
+ nickname: '',
+ alias: '',
+ gender: null,
+ region: '',
+ remark: '',
+ signature: '',
+ source: '',
+ sourceScene: null
+ }
+ } else {
+ if (!String(contactProfileData.value?.displayName || '').trim() && senderName) {
+ contactProfileData.value.displayName = senderName
+ }
+ if (!String(contactProfileData.value?.avatar || '').trim() && senderAvatar) {
+ contactProfileData.value.avatar = senderAvatar
+ }
+ }
+
+ clearContactProfileHoverHideTimer()
+ contactProfileCardMessageId.value = messageId
+ contactProfileCardOpen.value = true
+ await fetchContactProfile({ username, displayName: senderName, avatar: senderAvatar })
+ }
+
+ const onMessageAvatarMouseLeave = () => {
+ clearContactProfileHoverHideTimer()
+ contactProfileHoverHideTimer = setTimeout(() => {
+ closeContactProfileCard()
+ }, 120)
+ }
+
+ const onContactCardMouseEnter = () => {
+ clearContactProfileHoverHideTimer()
+ }
+
+ watch(
+ () => selectedContact.value?.username,
+ () => {
+ clearContactProfileHoverHideTimer()
+ closeContactProfileCard()
+ contactProfileError.value = ''
+ contactProfileData.value = null
+ }
+ )
+
+ watch(
+ () => selectedAccount.value,
+ () => {
+ clearContactProfileHoverHideTimer()
+ closeContactProfileCard()
+ contactProfileError.value = ''
+ contactProfileData.value = null
+ }
+ )
+
+ onUnmounted(() => {
+ if (highlightTimer) clearTimeout(highlightTimer)
+ highlightTimer = null
+ clearContactProfileHoverHideTimer()
+ clearVoicePlaybackState()
+ })
+
+ return {
+ allMessages,
+ messagesMeta,
+ messages,
+ renderMessages,
+ hasMoreMessages,
+ isLoadingMessages,
+ messagesError,
+ messageContainerRef,
+ showJumpToBottom,
+ messagePageSize,
+ messageTypeFilter,
+ messageTypeFilterOptions,
+ reverseMessageSides,
+ previewImageUrl,
+ previewVideoUrl,
+ previewVideoPosterUrl,
+ previewVideoError,
+ voiceRefs,
+ currentPlayingVoice,
+ playingVoiceId,
+ highlightServerIdStr,
+ highlightMessageId,
+ contactProfileCardOpen,
+ contactProfileCardMessageId,
+ contactProfileLoading,
+ contactProfileError,
+ contactProfileData,
+ contactProfileResolvedName,
+ contactProfileResolvedUsername,
+ contactProfileResolvedNickname,
+ contactProfileResolvedAlias,
+ contactProfileResolvedGender,
+ contactProfileResolvedRegion,
+ contactProfileResolvedRemark,
+ contactProfileResolvedSignature,
+ contactProfileResolvedSource,
+ contactProfileResolvedSourceScene,
+ contactProfileResolvedAvatar,
+ normalizeMessage,
+ updateJumpToBottomState,
+ scrollToBottom,
+ flashMessage,
+ scrollToMessageId,
+ openImagePreview,
+ closeImagePreview,
+ openVideoPreview,
+ closeVideoPreview,
+ onPreviewVideoError,
+ setVoiceRef,
+ playVoice,
+ playQuoteVoice,
+ getQuoteVoiceId,
+ getVoiceDurationInSeconds,
+ getVoiceWidth,
+ isQuotedVoice,
+ isQuotedImage,
+ isQuotedLink,
+ getQuotedLinkText,
+ onQuoteImageError,
+ onQuoteThumbError,
+ onAvatarError,
+ shouldShowEmojiDownload,
+ onEmojiDownloadClick,
+ onFileClick,
+ toggleReverseMessageSides,
+ loadMessages,
+ loadMoreMessages,
+ refreshSelectedMessages,
+ refreshCurrentMessageMedia,
+ refreshRealtimeIncremental,
+ queueRealtimeRefresh,
+ tryEnableRealtimeAuto,
+ resetMessageState,
+ fetchContactProfile,
+ clearContactProfileHoverHideTimer,
+ closeContactProfileCard,
+ onMessageAvatarMouseEnter,
+ onMessageAvatarMouseLeave,
+ onContactCardMouseEnter,
+ formatFileSize
+ }
+}
diff --git a/frontend/composables/chat/useChatSearch.js b/frontend/composables/chat/useChatSearch.js
new file mode 100644
index 0000000..8a92e75
--- /dev/null
+++ b/frontend/composables/chat/useChatSearch.js
@@ -0,0 +1,1678 @@
+import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
+import {
+ dateToUnixSeconds,
+ formatMessageFullTime,
+ highlightKeyword
+} from '~/lib/chat/formatters'
+
+export const createEmptySearchContext = () => ({
+ active: false,
+ kind: 'search',
+ label: '',
+ username: '',
+ anchorId: '',
+ anchorIndex: -1,
+ hasMoreBefore: false,
+ hasMoreAfter: false,
+ loadingBefore: false,
+ loadingAfter: false,
+ savedMessages: null,
+ savedMeta: null
+})
+
+export const useChatSearch = ({
+ api,
+ heatColor,
+ contacts,
+ selectedAccount,
+ selectedContact,
+ privacyMode,
+ allMessages,
+ messagesMeta,
+ messages,
+ messageContainerRef,
+ messagePageSize,
+ hasMoreMessages,
+ isLoadingMessages,
+ normalizeMessage,
+ updateJumpToBottomState,
+ scrollToMessageId,
+ flashMessage,
+ highlightMessageId,
+ searchContext,
+ selectContact,
+ loadMoreMessages
+}) => {
+const messageSearchOpen = ref(false)
+const messageSearchQuery = ref('')
+const messageSearchScope = ref('global') // conversation | global
+const messageSearchRangeDays = ref('') // empty means no time filter
+const messageSearchSessionType = ref('') // empty means all (global only): group | single
+const messageSearchSender = ref('') // 发送者筛选
+const messageSearchSenderOptions = ref([])
+const messageSearchSenderLoading = ref(false)
+const messageSearchSenderError = ref('')
+const messageSearchSenderOptionsKey = ref('')
+const messageSearchSenderDropdownOpen = ref(false)
+const messageSearchSenderDropdownRef = ref(null)
+const messageSearchSenderDropdownInputRef = ref(null)
+const messageSearchSenderDropdownQuery = ref('')
+const messageSearchStartDate = ref('') // 自定义开始日期
+const messageSearchEndDate = ref('') // 自定义结束日期
+const messageSearchResults = ref([])
+const messageSearchLoading = ref(false)
+const messageSearchError = ref('')
+const messageSearchBackendStatus = ref('')
+const messageSearchIndexInfo = ref(null)
+const messageSearchHasMore = ref(false)
+const messageSearchOffset = ref(0)
+const messageSearchLimit = 50
+const messageSearchTotal = ref(0)
+const messageSearchSelectedIndex = ref(-1)
+const messageSearchInputRef = ref(null)
+let messageSearchDebounceTimer = null
+let messageSearchIndexPollTimer = null
+
+// 搜索UI增强
+const searchInputFocused = ref(false)
+const showAdvancedFilters = ref(false)
+const searchHistory = ref([])
+const SEARCH_HISTORY_KEY = 'wechat_search_history'
+const MAX_SEARCH_HISTORY = 10
+
+// 加载搜索历史
+const loadSearchHistory = () => {
+if (!process.client) return
+try {
+ const saved = localStorage.getItem(SEARCH_HISTORY_KEY)
+ if (saved) {
+ searchHistory.value = JSON.parse(saved) || []
+ }
+} catch (e) {
+ searchHistory.value = []
+}
+}
+
+// 保存搜索历史
+const saveSearchHistory = (query) => {
+if (!process.client) return
+if (!query || !query.trim()) return
+const q = query.trim()
+try {
+ let history = [...searchHistory.value]
+ // 移除重复项
+ history = history.filter(item => item !== q)
+ // 添加到开头
+ history.unshift(q)
+ // 限制数量
+ if (history.length > MAX_SEARCH_HISTORY) {
+ history = history.slice(0, MAX_SEARCH_HISTORY)
+ }
+ searchHistory.value = history
+ localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(history))
+} catch (e) {
+ // ignore
+}
+}
+
+// 清空搜索历史
+const clearSearchHistory = () => {
+if (!process.client) return
+searchHistory.value = []
+try {
+ localStorage.removeItem(SEARCH_HISTORY_KEY)
+} catch (e) {
+ // ignore
+}
+}
+
+// 应用搜索历史
+const applySearchHistory = async (query) => {
+messageSearchQuery.value = query
+await runMessageSearch({ reset: true })
+}
+
+const messageSearchIndexExists = computed(() => !!messageSearchIndexInfo.value?.exists)
+const messageSearchIndexReady = computed(() => !!messageSearchIndexInfo.value?.ready)
+const messageSearchIndexBuildStatus = computed(() => String(messageSearchIndexInfo.value?.build?.status || ''))
+const messageSearchIndexBuildIndexed = computed(() => Number(messageSearchIndexInfo.value?.build?.indexedMessages || 0))
+const messageSearchIndexMetaCount = computed(() => {
+const meta = messageSearchIndexInfo.value?.meta || {}
+const v = meta.message_count ?? meta.messageCount ?? meta.message_count ?? 0
+return Number(v || 0)
+})
+
+const messageSearchIndexProgressText = computed(() => {
+if (messageSearchIndexBuildStatus.value !== 'building') return ''
+const n = Number(messageSearchIndexBuildIndexed.value || 0)
+return n > 0 ? `已索引 ${n.toLocaleString()} 条` : '准备中...'
+})
+
+const messageSearchIndexText = computed(() => {
+if (!messageSearchIndexInfo.value) return ''
+if (!messageSearchIndexExists.value) return '索引未建立'
+if (messageSearchIndexBuildStatus.value === 'error') return '索引异常'
+if (!messageSearchIndexReady.value) return '索引未完成,需重建'
+const n = Number(messageSearchIndexMetaCount.value || 0)
+return n > 0 ? `索引已就绪(${n.toLocaleString()} 条)` : '索引已就绪'
+})
+
+const messageSearchIndexActionText = computed(() => {
+if (messageSearchIndexBuildStatus.value === 'building') return '建立中'
+return messageSearchIndexExists.value ? '重建索引' : '建立索引'
+})
+
+const messageSearchIndexActionDisabled = computed(() => {
+return messageSearchIndexBuildStatus.value === 'building' || messageSearchLoading.value
+})
+
+const formatCount = (n) => {
+const v = Number(n || 0)
+if (!Number.isFinite(v) || v <= 0) return ''
+try {
+ return v.toLocaleString()
+} catch {
+ return String(v)
+}
+}
+
+const messageSearchSenderDisabled = computed(() => {
+if (!selectedAccount.value) return true
+const scope = String(messageSearchScope.value || 'conversation')
+if (scope === 'conversation') {
+ return !selectedContact.value?.username
+}
+const q = String(messageSearchQuery.value || '').trim()
+if (q.length >= 2) return false
+return !String(messageSearchSender.value || '').trim()
+})
+
+const messageSearchSelectedSenderInfo = computed(() => {
+const u = String(messageSearchSender.value || '').trim()
+if (!u) return null
+const list = Array.isArray(messageSearchSenderOptions.value) ? messageSearchSenderOptions.value : []
+const found = list.find((s) => String(s?.username || '').trim() === u)
+if (found) return found
+return { username: u, displayName: u, avatar: null, count: null }
+})
+
+const messageSearchSelectedSenderInitial = computed(() => {
+const info = messageSearchSelectedSenderInfo.value
+if (!info) return '人'
+const n = String(info.displayName || info.username || '').trim()
+return n ? n.charAt(0) : '人'
+})
+
+const messageSearchSenderLabel = computed(() => {
+const cur = String(messageSearchSender.value || '').trim()
+if (!cur) {
+ if (String(messageSearchScope.value || '') === 'global' && String(messageSearchQuery.value || '').trim().length < 2) {
+ return '发送者'
+ }
+ return '不限发送者'
+}
+const info = messageSearchSelectedSenderInfo.value
+return String(info?.displayName || info?.username || cur)
+})
+
+const filteredMessageSearchSenderOptions = computed(() => {
+const list = Array.isArray(messageSearchSenderOptions.value) ? messageSearchSenderOptions.value : []
+const q = String(messageSearchSenderDropdownQuery.value || '').trim().toLowerCase()
+if (!q) return list
+return list.filter((s) => {
+ const u = String(s?.username || '').toLowerCase()
+ const n = String(s?.displayName || '').toLowerCase()
+ return u.includes(q) || n.includes(q)
+})
+})
+
+const closeMessageSearchSenderDropdown = () => {
+messageSearchSenderDropdownOpen.value = false
+messageSearchSenderDropdownQuery.value = ''
+}
+
+const getMessageSearchSenderFacetKey = () => {
+const acc = String(selectedAccount.value || '').trim()
+if (!acc) return ''
+const scope = String(messageSearchScope.value || 'conversation')
+const conv = scope === 'conversation' ? String(selectedContact.value?.username || '') : ''
+const q = String(messageSearchQuery.value || '').trim()
+const range = String(messageSearchRangeDays.value || '')
+const sd = String(messageSearchStartDate.value || '')
+const ed = String(messageSearchEndDate.value || '')
+const st = scope === 'global' ? String(messageSearchSessionType.value || '').trim() : ''
+return [acc, scope, conv, q, range, sd, ed, st].join('|')
+}
+
+const ensureMessageSearchSendersLoaded = async () => {
+const key = getMessageSearchSenderFacetKey()
+if (!key) return
+if (messageSearchSenderOptionsKey.value === key && !messageSearchSenderLoading.value) return
+const list = await fetchMessageSearchSenders()
+messageSearchSenderOptionsKey.value = key
+return list
+}
+
+const toggleMessageSearchSenderDropdown = async () => {
+if (messageSearchSenderDisabled.value) return
+if (messageSearchSenderDropdownOpen.value) {
+ closeMessageSearchSenderDropdown()
+ return
+}
+messageSearchSenderDropdownOpen.value = true
+await ensureMessageSearchSendersLoaded()
+await nextTick()
+try {
+ messageSearchSenderDropdownInputRef.value?.focus?.()
+} catch {}
+}
+
+const selectMessageSearchSender = (username) => {
+messageSearchSender.value = String(username || '')
+closeMessageSearchSenderDropdown()
+}
+
+const fetchMessageSearchIndexStatus = async () => {
+if (!selectedAccount.value) return null
+try {
+ const resp = await api.getChatSearchIndexStatus({ account: selectedAccount.value })
+ messageSearchIndexInfo.value = resp?.index || null
+ return messageSearchIndexInfo.value
+} catch (e) {
+ return null
+}
+}
+
+const fetchMessageSearchSenders = async () => {
+messageSearchSenderError.value = ''
+if (!selectedAccount.value) {
+ messageSearchSenderOptions.value = []
+ messageSearchSenderOptionsKey.value = ''
+ return []
+}
+
+const scope = String(messageSearchScope.value || 'conversation')
+const msgQ = String(messageSearchQuery.value || '').trim()
+
+const params = {
+ account: selectedAccount.value,
+ limit: 200
+}
+
+if (scope === 'conversation') {
+ if (!selectedContact.value?.username) {
+ messageSearchSenderOptions.value = []
+ messageSearchSenderOptionsKey.value = ''
+ return []
+ }
+ params.username = selectedContact.value.username
+} else {
+ if (msgQ.length < 2) {
+ messageSearchSenderOptions.value = []
+ messageSearchSenderOptionsKey.value = ''
+ return []
+ }
+}
+
+if (msgQ) {
+ params.message_q = msgQ
+}
+
+params.render_types = 'text'
+
+const range = String(messageSearchRangeDays.value || '')
+if (range === 'custom') {
+ const start = dateToUnixSeconds(messageSearchStartDate.value, false)
+ const end = dateToUnixSeconds(messageSearchEndDate.value, true)
+ if (start != null) params.start_time = start
+ if (end != null) params.end_time = end
+ if (start != null && end != null && start > end) {
+ messageSearchSenderError.value = '时间范围不合法:开始日期不能晚于结束日期'
+ messageSearchSenderOptions.value = []
+ messageSearchSenderOptionsKey.value = ''
+ return []
+ }
+} else {
+ const days = Number(range || 0)
+ if (days > 0 && Number.isFinite(days)) {
+ const end = Math.floor(Date.now() / 1000)
+ const start = Math.max(0, end - Math.floor(days * 24 * 3600))
+ params.start_time = start
+ params.end_time = end
+ }
+}
+
+if (scope === 'global') {
+ const st = String(messageSearchSessionType.value || '').trim()
+ if (st) params.session_type = st
+}
+
+messageSearchSenderLoading.value = true
+try {
+ const resp = await api.listChatSearchSenders(params)
+ const status = String(resp?.status || 'success')
+ if (status !== 'success') {
+ if (status !== 'index_building') {
+ messageSearchSenderError.value = String(resp?.message || '加载发送者失败')
+ }
+ messageSearchSenderOptions.value = []
+ messageSearchSenderOptionsKey.value = ''
+ return []
+ }
+ const list = Array.isArray(resp?.senders) ? resp.senders : []
+ messageSearchSenderOptions.value = list
+ messageSearchSenderOptionsKey.value = getMessageSearchSenderFacetKey()
+ const cur = String(messageSearchSender.value || '').trim()
+ if (cur && !list.some((s) => String(s?.username || '').trim() === cur)) {
+ messageSearchSender.value = ''
+ }
+ return list
+} catch (e) {
+ messageSearchSenderError.value = e?.message || '加载发送者失败'
+ messageSearchSenderOptions.value = []
+ messageSearchSenderOptionsKey.value = ''
+ return []
+} finally {
+ messageSearchSenderLoading.value = false
+}
+}
+
+const stopMessageSearchIndexPolling = () => {
+if (messageSearchIndexPollTimer) clearInterval(messageSearchIndexPollTimer)
+messageSearchIndexPollTimer = null
+}
+
+const ensureMessageSearchIndexPolling = () => {
+if (messageSearchIndexPollTimer) return
+messageSearchIndexPollTimer = setInterval(async () => {
+ if (!messageSearchOpen.value) {
+ stopMessageSearchIndexPolling()
+ return
+ }
+
+ const info = await fetchMessageSearchIndexStatus()
+ const exists = !!info?.exists
+ const ready = !!info?.ready
+ const bs = String(info?.build?.status || '')
+ const done = exists && ready && bs !== 'building'
+ if (done) {
+ stopMessageSearchIndexPolling()
+ if (String(messageSearchScope.value || '') === 'conversation') {
+ await fetchMessageSearchSenders()
+ }
+ if (String(messageSearchQuery.value || '').trim()) {
+ await runMessageSearch({ reset: true })
+ }
+ }
+}, 1200)
+}
+
+const onMessageSearchIndexAction = async () => {
+if (!selectedAccount.value) return
+const rebuild = messageSearchIndexExists.value
+try {
+ const resp = await api.buildChatSearchIndex({ account: selectedAccount.value, rebuild })
+ messageSearchIndexInfo.value = resp?.index || null
+ messageSearchBackendStatus.value = 'index_building'
+ ensureMessageSearchIndexPolling()
+} catch (e) {
+ messageSearchError.value = e?.message || '建立索引失败'
+}
+}
+const getMessageSearchHitAvatarUrl = (hit) => {
+if (!hit) return ''
+const scope = String(messageSearchScope.value || '')
+const url =
+ scope === 'global'
+ ? (hit.conversationAvatar || hit.senderAvatar || '')
+ : (hit.senderAvatar || hit.conversationAvatar || '')
+return String(url || '').trim()
+}
+
+const getMessageSearchHitAvatarAlt = (hit) => {
+if (!hit) return '头像'
+const scope = String(messageSearchScope.value || '')
+if (scope === 'global') {
+ const name = String(hit.conversationName || hit.username || '').trim()
+ return name ? `${name} 头像` : '头像'
+}
+let name = String(hit.senderDisplayName || '').trim()
+if (!name) {
+ name = hit.isSent ? '我' : String(hit.senderUsername || '').trim()
+}
+return name ? `${name} 头像` : '头像'
+}
+
+const getMessageSearchHitAvatarInitial = (hit) => {
+if (!hit) return '?'
+const scope = String(messageSearchScope.value || '')
+let text = ''
+if (scope === 'global') {
+ text = String(hit.conversationName || hit.username || '').trim()
+} else {
+ text = String(hit.senderDisplayName || '').trim()
+ if (!text) {
+ text = hit.isSent ? '我' : String(hit.senderUsername || '').trim()
+ }
+}
+return (text.charAt(0) || '?').toString()
+}
+const searchContextBannerText = computed(() => {
+if (!searchContext.value?.active) return ''
+const kind = String(searchContext.value.kind || 'search')
+if (kind === 'date') {
+ const label = String(searchContext.value.label || '').trim()
+ return label ? `已定位到 ${label}(上下文模式)` : '已定位到指定日期(上下文模式)'
+}
+if (kind === 'first') {
+ return '已定位到会话顶部(上下文模式)'
+}
+return '已定位到搜索结果(上下文模式)'
+})
+
+// 回到最新按钮
+const showJumpToBottom = ref(false)
+
+// 时间侧边栏(按日期定位)
+const timeSidebarOpen = ref(false)
+const timeSidebarYear = ref(null)
+const timeSidebarMonth = ref(null) // 1-12
+const timeSidebarCounts = ref({}) // { 'YYYY-MM-DD': count }
+const timeSidebarMax = ref(0)
+const timeSidebarTotal = ref(0)
+const timeSidebarLoading = ref(false)
+const timeSidebarError = ref('')
+const timeSidebarSelectedDate = ref('') // YYYY-MM-DD (current/selected day)
+// Simple in-memory cache per (account|username|YYYY-MM)
+const timeSidebarCache = ref({})
+const timeSidebarWeekdays = ['一', '二', '三', '四', '五', '六', '日']
+
+const timeSidebarMonthLabel = computed(() => {
+const y = Number(timeSidebarYear.value || 0)
+const m = Number(timeSidebarMonth.value || 0)
+if (!y || !m) return ''
+return `${y}年${m}月`
+})
+
+const timeSidebarYearOptions = computed(() => {
+// WeChat history normally starts after 2011, but keep a broader range for safety.
+const nowY = new Date().getFullYear()
+const minY = 2000
+const maxY = Math.max(nowY, Number(timeSidebarYear.value || 0) || nowY)
+const years = []
+for (let y = maxY; y >= minY; y--) years.push(y)
+return years
+})
+
+const timeSidebarActiveDays = computed(() => {
+const counts = timeSidebarCounts.value || {}
+const keys = Object.keys(counts)
+return keys.length
+})
+
+const _pad2 = (n) => String(n).padStart(2, '0')
+
+const _dateStrFromEpochSeconds = (ts) => {
+const t = Number(ts || 0)
+if (!t) return ''
+try {
+ const d = new Date(t * 1000)
+ return `${d.getFullYear()}-${_pad2(d.getMonth() + 1)}-${_pad2(d.getDate())}`
+} catch {
+ return ''
+}
+}
+
+// Calendar heatmap color: reuse Wrapped heat palette, but bucket to Wrapped-like legend levels
+// so ">=1 message" is always visibly tinted (instead of being almost white when max is huge).
+const _calendarHeatColor = (count, maxV) => {
+const v = Math.max(0, Number(count || 0))
+const m = Math.max(0, Number(maxV || 0))
+if (!(v > 0)) return ''
+if (!(m > 0)) return heatColor(1, 1)
+const levels = 6
+const ratio = Math.max(0, Math.min(1, v / m))
+const level = Math.min(levels, Math.max(1, Math.ceil(ratio * levels)))
+const valueForLevel = Math.max(1, Math.round(level * (m / levels)))
+return heatColor(valueForLevel, m)
+}
+
+const timeSidebarCalendarCells = computed(() => {
+const y = Number(timeSidebarYear.value || 0)
+const m = Number(timeSidebarMonth.value || 0) // 1-12
+if (!y || !m) return []
+
+const daysInMonth = new Date(y, m, 0).getDate()
+const firstDow = new Date(y, m - 1, 1).getDay() // 0=Sun..6=Sat
+const offset = (firstDow + 6) % 7 // Monday=0
+
+const maxV = Math.max(0, Number(timeSidebarMax.value || 0))
+const counts = timeSidebarCounts.value || {}
+const selected = String(timeSidebarSelectedDate.value || '').trim()
+
+const out = []
+for (let i = 0; i < 42; i++) {
+ const dayNum = i - offset + 1
+ const inMonth = dayNum >= 1 && dayNum <= daysInMonth
+ if (!inMonth) {
+ out.push({
+ key: `e:${y}-${m}:${i}`,
+ day: '',
+ dateStr: '',
+ count: 0,
+ disabled: true,
+ className: 'calendar-day-outside',
+ style: null,
+ title: ''
+ })
+ continue
+ }
+
+ const dateStr = `${y}-${_pad2(m)}-${_pad2(dayNum)}`
+ const count = Math.max(0, Number(counts[dateStr] || 0))
+ const disabled = count <= 0
+
+ const style = !disabled
+ ? { backgroundColor: _calendarHeatColor(count, Math.max(maxV, count)) }
+ : null
+
+ const className = [
+ disabled ? 'calendar-day-empty' : '',
+ (selected && dateStr === selected) ? 'calendar-day-selected' : ''
+ ].filter(Boolean).join(' ')
+
+ out.push({
+ key: dateStr,
+ day: String(dayNum),
+ dateStr,
+ count,
+ disabled,
+ // NOTE: heatmap bg color is applied via inline style (reusing Wrapped heatmap palette).
+ // Dynamic class names like `calendar-day-l${level}` may be purged by Tailwind and lead to no bg color.
+ className,
+ style,
+ title: `${dateStr}:${count} 条`
+ })
+}
+return out
+})
+const closeMessageSearch = () => {
+messageSearchOpen.value = false
+closeMessageSearchSenderDropdown()
+messageSearchError.value = ''
+messageSearchLoading.value = false
+messageSearchBackendStatus.value = ''
+stopMessageSearchIndexPolling()
+if (messageSearchDebounceTimer) clearTimeout(messageSearchDebounceTimer)
+messageSearchDebounceTimer = null
+}
+
+let timeSidebarReqId = 0
+
+const closeTimeSidebar = () => {
+timeSidebarOpen.value = false
+timeSidebarError.value = ''
+}
+
+const _timeSidebarCacheKey = ({ account, username, year, month }) => {
+const acc = String(account || '').trim()
+const u = String(username || '').trim()
+const y = Number(year || 0)
+const m = Number(month || 0)
+return `${acc}|${u}|${y}-${_pad2(m)}`
+}
+
+const _applyTimeSidebarMonthData = (data) => {
+const counts = (data && typeof data.counts === 'object' && !Array.isArray(data.counts)) ? data.counts : {}
+timeSidebarCounts.value = counts
+timeSidebarMax.value = Math.max(0, Number(data?.max || 0))
+timeSidebarTotal.value = Math.max(0, Number(data?.total || 0))
+}
+
+const loadTimeSidebarMonth = async ({ year, month, force } = {}) => {
+if (!selectedAccount.value) return
+if (!selectedContact.value?.username) return
+
+const y = Number(year || timeSidebarYear.value || 0)
+const m = Number(month || timeSidebarMonth.value || 0)
+if (!y || !m) return
+
+timeSidebarYear.value = y
+timeSidebarMonth.value = m
+
+const key = _timeSidebarCacheKey({
+ account: selectedAccount.value,
+ username: selectedContact.value.username,
+ year: y,
+ month: m
+})
+
+if (!force) {
+ const cached = timeSidebarCache.value[key]
+ if (cached) {
+ timeSidebarError.value = ''
+ _applyTimeSidebarMonthData(cached)
+ return
+ }
+}
+
+const reqId = ++timeSidebarReqId
+timeSidebarLoading.value = true
+timeSidebarError.value = ''
+
+try {
+ const resp = await api.getChatMessageDailyCounts({
+ account: selectedAccount.value,
+ username: selectedContact.value.username,
+ year: y,
+ month: m
+ })
+ if (reqId !== timeSidebarReqId) return
+ if (String(resp?.status || '') !== 'success') {
+ throw new Error(String(resp?.message || '加载日历失败'))
+ }
+
+ const data = {
+ counts: resp?.counts || {},
+ max: Number(resp?.max || 0),
+ total: Number(resp?.total || 0)
+ }
+
+ _applyTimeSidebarMonthData(data)
+ timeSidebarCache.value = { ...timeSidebarCache.value, [key]: data }
+} catch (e) {
+ if (reqId !== timeSidebarReqId) return
+ timeSidebarError.value = e?.message || '加载日历失败'
+ _applyTimeSidebarMonthData({ counts: {}, max: 0, total: 0 })
+} finally {
+ if (reqId === timeSidebarReqId) {
+ timeSidebarLoading.value = false
+ }
+}
+}
+
+const _pickTimeSidebarInitialYearMonth = () => {
+const list = messages.value || []
+const last = Array.isArray(list) && list.length ? list[list.length - 1] : null
+const ts = Number(last?.createTime || 0)
+const d = ts ? new Date(ts * 1000) : new Date()
+return { year: d.getFullYear(), month: d.getMonth() + 1 }
+}
+
+const _applyTimeSidebarSelectedDate = async (dateStr, { syncMonth } = {}) => {
+const ds = String(dateStr || '').trim()
+if (!ds) return
+if (timeSidebarSelectedDate.value !== ds) {
+ timeSidebarSelectedDate.value = ds
+}
+if (!syncMonth || !timeSidebarOpen.value) return
+
+const parts = ds.split('-')
+const y = Number(parts?.[0] || 0)
+const m = Number(parts?.[1] || 0)
+if (!y || !m) return
+
+if (Number(timeSidebarYear.value || 0) !== y || Number(timeSidebarMonth.value || 0) !== m) {
+ timeSidebarYear.value = y
+ timeSidebarMonth.value = m
+ // Fire and forget; request id guard + cache inside loadTimeSidebarMonth will handle racing.
+ await loadTimeSidebarMonth({ year: y, month: m, force: false })
+}
+}
+
+const toggleTimeSidebar = async () => {
+timeSidebarOpen.value = !timeSidebarOpen.value
+if (!timeSidebarOpen.value) return
+closeMessageSearch()
+
+const { year, month } = _pickTimeSidebarInitialYearMonth()
+timeSidebarYear.value = year
+timeSidebarMonth.value = month
+
+// Default selected day: current viewport's latest loaded message day (usually "latest").
+const list = messages.value || []
+const last = Array.isArray(list) && list.length ? list[list.length - 1] : null
+const ds = _dateStrFromEpochSeconds(Number(last?.createTime || 0))
+if (ds) await _applyTimeSidebarSelectedDate(ds, { syncMonth: false })
+
+await loadTimeSidebarMonth({ year, month, force: false })
+}
+
+const prevTimeSidebarMonth = async () => {
+const y0 = Number(timeSidebarYear.value || 0)
+const m0 = Number(timeSidebarMonth.value || 0)
+if (!y0 || !m0) return
+const y = m0 === 1 ? (y0 - 1) : y0
+const m = m0 === 1 ? 12 : (m0 - 1)
+await loadTimeSidebarMonth({ year: y, month: m, force: false })
+}
+
+const nextTimeSidebarMonth = async () => {
+const y0 = Number(timeSidebarYear.value || 0)
+const m0 = Number(timeSidebarMonth.value || 0)
+if (!y0 || !m0) return
+const y = m0 === 12 ? (y0 + 1) : y0
+const m = m0 === 12 ? 1 : (m0 + 1)
+await loadTimeSidebarMonth({ year: y, month: m, force: false })
+}
+
+const onTimeSidebarYearMonthChange = async () => {
+if (!timeSidebarOpen.value) return
+const y = Number(timeSidebarYear.value || 0)
+const m = Number(timeSidebarMonth.value || 0)
+if (!y || !m) return
+await loadTimeSidebarMonth({ year: y, month: m, force: false })
+}
+
+const ensureMessageSearchScopeValid = () => {
+if (messageSearchScope.value === 'conversation' && !selectedContact.value) {
+ messageSearchScope.value = 'global'
+}
+}
+
+const toggleMessageSearch = async () => {
+messageSearchOpen.value = !messageSearchOpen.value
+ensureMessageSearchScopeValid()
+if (!messageSearchOpen.value) return
+closeTimeSidebar()
+await nextTick()
+try {
+ messageSearchInputRef.value?.focus?.()
+} catch {}
+await fetchMessageSearchIndexStatus()
+await fetchMessageSearchSenders()
+if (String(messageSearchQuery.value || '').trim()) {
+ await runMessageSearch({ reset: true })
+}
+}
+
+let messageSearchReqId = 0
+
+const runMessageSearch = async ({ reset } = {}) => {
+if (!selectedAccount.value) return
+ensureMessageSearchScopeValid()
+
+const q = String(messageSearchQuery.value || '').trim()
+if (!q) {
+ messageSearchResults.value = []
+ messageSearchHasMore.value = false
+ messageSearchError.value = ''
+ messageSearchSelectedIndex.value = -1
+ messageSearchBackendStatus.value = ''
+ messageSearchTotal.value = 0
+ stopMessageSearchIndexPolling()
+ return
+}
+
+if (reset) {
+ messageSearchOffset.value = 0
+ messageSearchResults.value = []
+ messageSearchSelectedIndex.value = -1
+}
+
+const reqId = ++messageSearchReqId
+messageSearchLoading.value = true
+messageSearchError.value = ''
+messageSearchBackendStatus.value = ''
+
+const scope = String(messageSearchScope.value || 'conversation')
+
+const params = {
+ account: selectedAccount.value,
+ q,
+ limit: messageSearchLimit,
+ offset: messageSearchOffset.value
+}
+
+params.render_types = 'text'
+
+const range = String(messageSearchRangeDays.value || '')
+if (range === 'custom') {
+ const start = dateToUnixSeconds(messageSearchStartDate.value, false)
+ const end = dateToUnixSeconds(messageSearchEndDate.value, true)
+ if (start != null) params.start_time = start
+ if (end != null) params.end_time = end
+ if (start != null && end != null && start > end) {
+ messageSearchLoading.value = false
+ messageSearchError.value = '时间范围不合法:开始日期不能晚于结束日期'
+ return
+ }
+} else {
+ const days = Number(range || 0)
+ if (days > 0 && Number.isFinite(days)) {
+ const end = Math.floor(Date.now() / 1000)
+ const start = Math.max(0, end - Math.floor(days * 24 * 3600))
+ params.start_time = start
+ params.end_time = end
+ }
+}
+
+if (scope === 'global') {
+ const st = String(messageSearchSessionType.value || '').trim()
+ if (st) params.session_type = st
+}
+
+if (String(messageSearchSender.value || '').trim()) {
+ params.sender = String(messageSearchSender.value || '').trim()
+}
+
+if (scope === 'conversation') {
+ if (!selectedContact.value?.username) {
+ messageSearchLoading.value = false
+ messageSearchError.value = '请选择一个会话再搜索'
+ return
+ }
+ params.username = selectedContact.value.username
+}
+
+try {
+ const resp = await api.searchChatMessages(params)
+ if (reqId !== messageSearchReqId) return
+
+ if (resp?.index) {
+ messageSearchIndexInfo.value = resp.index
+ }
+
+ const status = String(resp?.status || 'success')
+ messageSearchBackendStatus.value = status
+
+ if (status === 'index_building') {
+ if (reset) {
+ messageSearchResults.value = []
+ messageSearchSelectedIndex.value = -1
+ }
+ messageSearchHasMore.value = false
+ messageSearchTotal.value = 0
+ ensureMessageSearchIndexPolling()
+ return
+ }
+
+ if (status === 'index_error') {
+ if (reset) {
+ messageSearchResults.value = []
+ messageSearchSelectedIndex.value = -1
+ }
+ messageSearchHasMore.value = false
+ messageSearchTotal.value = 0
+ messageSearchError.value = String(resp?.message || '索引错误')
+ stopMessageSearchIndexPolling()
+ return
+ }
+
+ if (status !== 'success') {
+ if (reset) {
+ messageSearchResults.value = []
+ messageSearchSelectedIndex.value = -1
+ }
+ messageSearchHasMore.value = false
+ messageSearchTotal.value = 0
+ messageSearchError.value = String(resp?.message || '搜索失败')
+ stopMessageSearchIndexPolling()
+ return
+ }
+
+ const hits = Array.isArray(resp?.hits) ? resp.hits : []
+ if (reset) {
+ messageSearchResults.value = hits
+ } else {
+ messageSearchResults.value = [...messageSearchResults.value, ...hits]
+ }
+ messageSearchHasMore.value = !!resp?.hasMore
+ messageSearchTotal.value = Number(resp?.total ?? resp?.totalInScan ?? 0)
+ stopMessageSearchIndexPolling()
+
+ if (messageSearchSelectedIndex.value < 0 && messageSearchResults.value.length) {
+ messageSearchSelectedIndex.value = 0
+ }
+
+ // 保存搜索历史(仅在有结果时保存)
+ if (!privacyMode.value && reset && hits.length > 0) {
+ saveSearchHistory(q)
+ }
+} catch (e) {
+ if (reqId !== messageSearchReqId) return
+ messageSearchError.value = e?.message || '搜索失败'
+} finally {
+ if (reqId === messageSearchReqId) {
+ messageSearchLoading.value = false
+ }
+}
+}
+
+const loadMoreSearchResults = async () => {
+if (!messageSearchHasMore.value) return
+if (messageSearchLoading.value) return
+messageSearchOffset.value = Number(messageSearchOffset.value || 0) + messageSearchLimit
+await runMessageSearch({ reset: false })
+}
+
+const exitSearchContext = async () => {
+if (!searchContext.value?.active) return
+const u = String(searchContext.value.username || '').trim()
+const saved = searchContext.value.savedMessages
+const savedMeta = searchContext.value.savedMeta
+
+if (u && saved) {
+ allMessages.value = { ...allMessages.value, [u]: saved }
+}
+if (u && savedMeta) {
+ messagesMeta.value = { ...messagesMeta.value, [u]: savedMeta }
+}
+
+searchContext.value = {
+ active: false,
+ kind: 'search',
+ label: '',
+ username: '',
+ anchorId: '',
+ anchorIndex: -1,
+ hasMoreBefore: false,
+ hasMoreAfter: false,
+ loadingBefore: false,
+ loadingAfter: false,
+ savedMessages: null,
+ savedMeta: null
+}
+highlightMessageId.value = ''
+await nextTick()
+updateJumpToBottomState()
+}
+
+const locateSearchHit = async (hit) => {
+if (!process.client) return
+if (!selectedAccount.value) return
+if (!hit?.id) return
+
+const targetUsername = String(hit?.username || selectedContact.value?.username || '').trim()
+if (!targetUsername) return
+
+const targetContact = contacts.value.find((c) => c?.username === targetUsername)
+if (targetContact && selectedContact.value?.username !== targetUsername) {
+ await selectContact(targetContact, { skipLoadMessages: true })
+}
+
+if (searchContext.value?.active && searchContext.value.username !== targetUsername) {
+ await exitSearchContext()
+}
+
+if (!searchContext.value?.active) {
+ searchContext.value = {
+ active: true,
+ kind: 'search',
+ label: '',
+ username: targetUsername,
+ anchorId: String(hit.id),
+ anchorIndex: -1,
+ hasMoreBefore: true,
+ hasMoreAfter: true,
+ loadingBefore: false,
+ loadingAfter: false,
+ savedMessages: allMessages.value[targetUsername] || [],
+ savedMeta: messagesMeta.value[targetUsername] || null
+ }
+} else {
+ searchContext.value.kind = 'search'
+ searchContext.value.label = ''
+ searchContext.value.anchorId = String(hit.id)
+ searchContext.value.hasMoreBefore = true
+ searchContext.value.hasMoreAfter = true
+ searchContext.value.loadingBefore = false
+ searchContext.value.loadingAfter = false
+}
+
+try {
+ const resp = await api.getChatMessagesAround({
+ account: selectedAccount.value,
+ username: targetUsername,
+ anchor_id: String(hit.id),
+ before: 35,
+ after: 35
+ })
+
+ const raw = resp?.messages || []
+ const mapped = raw.map(normalizeMessage)
+ allMessages.value = { ...allMessages.value, [targetUsername]: mapped }
+ messagesMeta.value = { ...messagesMeta.value, [targetUsername]: { total: mapped.length, hasMore: false } }
+
+ searchContext.value.anchorId = String(resp?.anchorId || hit.id)
+ searchContext.value.anchorIndex = Number(resp?.anchorIndex ?? -1)
+
+ const ok = await scrollToMessageId(searchContext.value.anchorId)
+ if (ok) flashMessage(searchContext.value.anchorId)
+} catch (e) {
+ window.alert(e?.message || '定位失败')
+}
+}
+
+const locateByAnchorId = async ({ targetUsername, anchorId, kind, label } = {}) => {
+if (!process.client) return
+if (!selectedAccount.value) return
+const u = String(targetUsername || selectedContact.value?.username || '').trim()
+const anchor = String(anchorId || '').trim()
+if (!u || !anchor) return
+
+const targetContact = contacts.value.find((c) => c?.username === u)
+if (targetContact && selectedContact.value?.username !== u) {
+ await selectContact(targetContact, { skipLoadMessages: true })
+}
+
+if (searchContext.value?.active && searchContext.value.username !== u) {
+ await exitSearchContext()
+}
+
+const kindNorm = String(kind || 'search').trim() || 'search'
+const labelNorm = String(label || '').trim()
+const hasMoreBeforeInit = kindNorm === 'first' ? false : true
+
+if (!searchContext.value?.active) {
+ searchContext.value = {
+ active: true,
+ kind: kindNorm,
+ label: labelNorm,
+ username: u,
+ anchorId: anchor,
+ anchorIndex: -1,
+ hasMoreBefore: hasMoreBeforeInit,
+ hasMoreAfter: true,
+ loadingBefore: false,
+ loadingAfter: false,
+ savedMessages: allMessages.value[u] || [],
+ savedMeta: messagesMeta.value[u] || null
+ }
+} else {
+ searchContext.value.kind = kindNorm
+ searchContext.value.label = labelNorm
+ searchContext.value.anchorId = anchor
+ searchContext.value.username = u
+ searchContext.value.hasMoreBefore = hasMoreBeforeInit
+ searchContext.value.hasMoreAfter = true
+ searchContext.value.loadingBefore = false
+ searchContext.value.loadingAfter = false
+}
+
+try {
+ const resp = await api.getChatMessagesAround({
+ account: selectedAccount.value,
+ username: u,
+ anchor_id: anchor,
+ before: 35,
+ after: 35
+ })
+
+ const raw = resp?.messages || []
+ const mapped = raw.map(normalizeMessage)
+ allMessages.value = { ...allMessages.value, [u]: mapped }
+ messagesMeta.value = { ...messagesMeta.value, [u]: { total: mapped.length, hasMore: false } }
+
+ searchContext.value.anchorId = String(resp?.anchorId || anchor)
+ searchContext.value.anchorIndex = Number(resp?.anchorIndex ?? -1)
+
+ const ok = await scrollToMessageId(searchContext.value.anchorId)
+ if (ok) flashMessage(searchContext.value.anchorId)
+} catch (e) {
+ window.alert(e?.message || '定位失败')
+}
+}
+
+const locateByDate = async (dateStr) => {
+if (!process.client) return
+if (!selectedAccount.value) return
+if (!selectedContact.value?.username) return
+
+const ds = String(dateStr || '').trim()
+if (!ds) return
+await _applyTimeSidebarSelectedDate(ds, { syncMonth: true })
+
+try {
+ const resp = await api.getChatMessageAnchor({
+ account: selectedAccount.value,
+ username: selectedContact.value.username,
+ kind: 'day',
+ date: ds
+ })
+ const status = String(resp?.status || '')
+ const anchorId = String(resp?.anchorId || '').trim()
+ if (status !== 'success' || !anchorId) {
+ window.alert('当日暂无聊天记录')
+ return
+ }
+ await locateByAnchorId({ targetUsername: selectedContact.value.username, anchorId, kind: 'date', label: ds })
+} catch (e) {
+ window.alert(e?.message || '定位失败')
+}
+}
+
+const jumpToConversationFirst = async () => {
+if (!process.client) return
+if (!selectedAccount.value) return
+if (!selectedContact.value?.username) return
+
+try {
+ const resp = await api.getChatMessageAnchor({
+ account: selectedAccount.value,
+ username: selectedContact.value.username,
+ kind: 'first'
+ })
+ const status = String(resp?.status || '')
+ const anchorId = String(resp?.anchorId || '').trim()
+ if (status !== 'success' || !anchorId) {
+ window.alert('暂无聊天记录')
+ return
+ }
+ const ds = _dateStrFromEpochSeconds(Number(resp?.createTime || 0))
+ if (ds) await _applyTimeSidebarSelectedDate(ds, { syncMonth: true })
+ await locateByAnchorId({ targetUsername: selectedContact.value.username, anchorId, kind: 'first', label: '' })
+} catch (e) {
+ window.alert(e?.message || '定位失败')
+}
+}
+
+const onTimeSidebarDayClick = async (cell) => {
+if (!cell || cell.disabled) return
+const ds = String(cell.dateStr || '').trim()
+if (!ds) return
+await locateByDate(ds)
+}
+
+const _mergeContextMessages = (username, nextList) => {
+const u = String(username || '').trim()
+if (!u) return
+const list = Array.isArray(nextList) ? nextList : []
+allMessages.value = { ...allMessages.value, [u]: list }
+// Keep meta aligned; context mode doesn't rely on hasMore from meta.
+const prevMeta = messagesMeta.value[u] || null
+messagesMeta.value = {
+ ...messagesMeta.value,
+ [u]: {
+ total: Math.max(Number(prevMeta?.total || 0), list.length),
+ hasMore: false
+ }
+}
+}
+
+const loadMoreSearchContextAfter = async () => {
+if (!process.client) return
+if (!selectedAccount.value) return
+if (!searchContext.value?.active) return
+if (searchContext.value.loadingAfter) return
+if (!searchContext.value.hasMoreAfter) return
+
+const u = String(searchContext.value.username || selectedContact.value?.username || '').trim()
+if (!u) return
+const existing = allMessages.value[u] || []
+const last = Array.isArray(existing) && existing.length ? existing[existing.length - 1] : null
+const anchorId = String(last?.id || '').trim()
+if (!anchorId) {
+ searchContext.value.hasMoreAfter = false
+ return
+}
+
+const ctxUsername = u
+searchContext.value.loadingAfter = true
+try {
+ const resp = await api.getChatMessagesAround({
+ account: selectedAccount.value,
+ username: ctxUsername,
+ anchor_id: anchorId,
+ before: 0,
+ after: messagePageSize
+ })
+
+ if (!searchContext.value?.active || String(searchContext.value.username || '').trim() !== ctxUsername) return
+
+ const raw = resp?.messages || []
+ const mapped = raw.map(normalizeMessage)
+
+ const existingIds = new Set(existing.map((m) => String(m?.id || '')))
+ const appended = []
+ for (const m of mapped) {
+ const id = String(m?.id || '').trim()
+ if (!id) continue
+ if (existingIds.has(id)) continue
+ existingIds.add(id)
+ appended.push(m)
+ }
+
+ if (!appended.length) {
+ searchContext.value.hasMoreAfter = false
+ return
+ }
+
+ _mergeContextMessages(ctxUsername, [...existing, ...appended])
+} catch (e) {
+ window.alert(e?.message || '加载更多消息失败')
+} finally {
+ if (searchContext.value?.active && String(searchContext.value.username || '').trim() === ctxUsername) {
+ searchContext.value.loadingAfter = false
+ }
+}
+}
+
+const loadMoreSearchContextBefore = async () => {
+if (!process.client) return
+if (!selectedAccount.value) return
+if (!searchContext.value?.active) return
+if (searchContext.value.loadingBefore) return
+if (!searchContext.value.hasMoreBefore) return
+
+const u = String(searchContext.value.username || selectedContact.value?.username || '').trim()
+if (!u) return
+const existing = allMessages.value[u] || []
+const first = Array.isArray(existing) && existing.length ? existing[0] : null
+const anchorId = String(first?.id || '').trim()
+if (!anchorId) {
+ searchContext.value.hasMoreBefore = false
+ return
+}
+
+const c = messageContainerRef.value
+const beforeScrollHeight = c ? c.scrollHeight : 0
+const beforeScrollTop = c ? c.scrollTop : 0
+
+const ctxUsername = u
+searchContext.value.loadingBefore = true
+try {
+ const resp = await api.getChatMessagesAround({
+ account: selectedAccount.value,
+ username: ctxUsername,
+ anchor_id: anchorId,
+ before: messagePageSize,
+ after: 0
+ })
+
+ if (!searchContext.value?.active || String(searchContext.value.username || '').trim() !== ctxUsername) return
+
+ const raw = resp?.messages || []
+ const mapped = raw.map(normalizeMessage)
+
+ const existingIds = new Set(existing.map((m) => String(m?.id || '')))
+ const prepended = []
+ for (const m of mapped) {
+ const id = String(m?.id || '').trim()
+ if (!id) continue
+ if (existingIds.has(id)) continue
+ existingIds.add(id)
+ prepended.push(m)
+ }
+
+ if (!prepended.length) {
+ searchContext.value.hasMoreBefore = false
+ return
+ }
+
+ _mergeContextMessages(ctxUsername, [...prepended, ...existing])
+
+ await nextTick()
+ const c2 = messageContainerRef.value
+ if (c2) {
+ const afterScrollHeight = c2.scrollHeight
+ c2.scrollTop = beforeScrollTop + (afterScrollHeight - beforeScrollHeight)
+ }
+} catch (e) {
+ window.alert(e?.message || '加载更多消息失败')
+} finally {
+ if (searchContext.value?.active && String(searchContext.value.username || '').trim() === ctxUsername) {
+ searchContext.value.loadingBefore = false
+ }
+}
+}
+
+const onSearchHitClick = async (hit, idx) => {
+messageSearchSelectedIndex.value = Number(idx || 0)
+await locateSearchHit(hit)
+}
+
+const onSearchNext = async () => {
+const q = String(messageSearchQuery.value || '').trim()
+if (!q) return
+
+if (!messageSearchResults.value.length && !messageSearchLoading.value) {
+ await runMessageSearch({ reset: true })
+}
+if (!messageSearchResults.value.length) return
+
+const cur = Number(messageSearchSelectedIndex.value || 0)
+const next = (cur + 1) % messageSearchResults.value.length
+messageSearchSelectedIndex.value = next
+await locateSearchHit(messageSearchResults.value[next])
+}
+
+const onSearchPrev = async () => {
+const q = String(messageSearchQuery.value || '').trim()
+if (!q) return
+
+if (!messageSearchResults.value.length && !messageSearchLoading.value) {
+ await runMessageSearch({ reset: true })
+}
+if (!messageSearchResults.value.length) return
+
+const cur = Number(messageSearchSelectedIndex.value || 0)
+const prev = (cur - 1 + messageSearchResults.value.length) % messageSearchResults.value.length
+messageSearchSelectedIndex.value = prev
+await locateSearchHit(messageSearchResults.value[prev])
+}
+const openMessageSearch = async () => {
+closeTimeSidebar()
+messageSearchOpen.value = true
+ensureMessageSearchScopeValid()
+await nextTick()
+try {
+ messageSearchInputRef.value?.focus?.()
+} catch {}
+await fetchMessageSearchIndexStatus()
+}
+watch(messageSearchScope, async () => {
+if (!messageSearchOpen.value) return
+ensureMessageSearchScopeValid()
+closeMessageSearchSenderDropdown()
+messageSearchSender.value = ''
+messageSearchSenderOptions.value = []
+messageSearchSenderOptionsKey.value = ''
+await fetchMessageSearchSenders()
+messageSearchOffset.value = 0
+messageSearchResults.value = []
+messageSearchSelectedIndex.value = -1
+if (String(messageSearchQuery.value || '').trim()) {
+ await runMessageSearch({ reset: true })
+}
+})
+
+watch(messageSearchRangeDays, async () => {
+if (!messageSearchOpen.value) return
+closeMessageSearchSenderDropdown()
+messageSearchOffset.value = 0
+messageSearchResults.value = []
+messageSearchSelectedIndex.value = -1
+if (String(messageSearchQuery.value || '').trim()) {
+ await runMessageSearch({ reset: true })
+}
+})
+
+watch(messageSearchSessionType, async () => {
+if (!messageSearchOpen.value) return
+if (String(messageSearchScope.value || '') !== 'global') return
+closeMessageSearchSenderDropdown()
+messageSearchSender.value = ''
+messageSearchSenderOptions.value = []
+messageSearchSenderOptionsKey.value = ''
+await fetchMessageSearchSenders()
+messageSearchOffset.value = 0
+messageSearchResults.value = []
+messageSearchSelectedIndex.value = -1
+if (String(messageSearchQuery.value || '').trim()) {
+ await runMessageSearch({ reset: true })
+}
+})
+
+watch([messageSearchStartDate, messageSearchEndDate], async () => {
+if (!messageSearchOpen.value) return
+if (String(messageSearchRangeDays.value || '') !== 'custom') return
+closeMessageSearchSenderDropdown()
+messageSearchOffset.value = 0
+messageSearchResults.value = []
+messageSearchSelectedIndex.value = -1
+if (String(messageSearchQuery.value || '').trim()) {
+ await runMessageSearch({ reset: true })
+}
+})
+
+watch(messageSearchSender, async () => {
+if (!messageSearchOpen.value) return
+messageSearchOffset.value = 0
+messageSearchResults.value = []
+messageSearchSelectedIndex.value = -1
+if (String(messageSearchQuery.value || '').trim()) {
+ await runMessageSearch({ reset: true })
+}
+})
+
+watch(messageSearchQuery, () => {
+if (!messageSearchOpen.value) return
+if (messageSearchDebounceTimer) clearTimeout(messageSearchDebounceTimer)
+messageSearchDebounceTimer = null
+const q = String(messageSearchQuery.value || '').trim()
+if (q.length < 2) return
+messageSearchDebounceTimer = setTimeout(() => {
+ runMessageSearch({ reset: true })
+}, 280)
+})
+
+watch(
+() => selectedContact.value?.username,
+async () => {
+ if (!messageSearchOpen.value) return
+ if (String(messageSearchScope.value || '') !== 'conversation') return
+ closeMessageSearchSenderDropdown()
+ messageSearchSender.value = ''
+ messageSearchSenderOptions.value = []
+ messageSearchSenderOptionsKey.value = ''
+ await fetchMessageSearchSenders()
+ if (String(messageSearchQuery.value || '').trim()) {
+ await runMessageSearch({ reset: true })
+ }
+}
+)
+
+const autoLoadReady = ref(true)
+
+let timeSidebarScrollSyncRaf = null
+const syncTimeSidebarSelectedDateFromScroll = () => {
+if (!process.client) return
+if (!timeSidebarOpen.value) return
+if (!selectedContact.value) return
+
+const c = messageContainerRef.value
+if (!c) return
+
+if (timeSidebarScrollSyncRaf) return
+timeSidebarScrollSyncRaf = requestAnimationFrame(() => {
+ timeSidebarScrollSyncRaf = null
+ try {
+ const containerRect = c.getBoundingClientRect()
+ const targetY = containerRect.top + 24
+ const els = c.querySelectorAll?.('[data-msg-id][data-create-time]') || []
+ if (!els || !els.length) return
+
+ let chosen = null
+ for (const el of els) {
+ const r = el.getBoundingClientRect?.()
+ if (!r) continue
+ if (r.bottom >= targetY) {
+ chosen = el
+ break
+ }
+ }
+ if (!chosen) chosen = els[els.length - 1]
+ const ts = Number(chosen?.getAttribute?.('data-create-time') || 0)
+ const ds = _dateStrFromEpochSeconds(ts)
+ if (!ds) return
+ // Don't await inside rAF; keep scroll handler snappy.
+ _applyTimeSidebarSelectedDate(ds, { syncMonth: true })
+ } catch {}
+})
+}
+
+const contextAutoLoadTopReady = ref(true)
+const contextAutoLoadBottomReady = ref(true)
+
+const onMessageScrollInContextMode = async () => {
+const c = messageContainerRef.value
+if (!c) return
+if (!searchContext.value?.active) return
+
+const distBottom = c.scrollHeight - c.scrollTop - c.clientHeight
+
+// Reset "ready" gates when user scrolls away from edges.
+if (c.scrollTop > 160) contextAutoLoadTopReady.value = true
+if (distBottom > 160) contextAutoLoadBottomReady.value = true
+
+if (c.scrollTop <= 60 && contextAutoLoadTopReady.value && searchContext.value.hasMoreBefore && !searchContext.value.loadingBefore) {
+ contextAutoLoadTopReady.value = false
+ await loadMoreSearchContextBefore()
+ return
+}
+
+if (distBottom <= 80 && contextAutoLoadBottomReady.value && searchContext.value.hasMoreAfter && !searchContext.value.loadingAfter) {
+ contextAutoLoadBottomReady.value = false
+ await loadMoreSearchContextAfter()
+}
+}
+
+const onMessageScroll = async () => {
+const c = messageContainerRef.value
+if (!c) return
+updateJumpToBottomState()
+if (!selectedContact.value) return
+
+// Keep the time sidebar selection in sync with the current viewport.
+syncTimeSidebarSelectedDateFromScroll()
+
+if (searchContext.value?.active) {
+ await onMessageScrollInContextMode()
+ return
+}
+
+if (c.scrollTop > 120) {
+ autoLoadReady.value = true
+ return
+}
+
+if (c.scrollTop <= 60 && autoLoadReady.value && hasMoreMessages.value && !isLoadingMessages.value) {
+ autoLoadReady.value = false
+ await loadMoreMessages()
+}
+}
+
+ const resetSearchState = () => {
+ closeMessageSearch()
+ closeTimeSidebar()
+ timeSidebarYear.value = null
+ timeSidebarMonth.value = null
+ _applyTimeSidebarMonthData({ counts: {}, max: 0, total: 0 })
+ timeSidebarError.value = ''
+ timeSidebarSelectedDate.value = ''
+ messageSearchResults.value = []
+ messageSearchOffset.value = 0
+ messageSearchHasMore.value = false
+ messageSearchBackendStatus.value = ''
+ messageSearchTotal.value = 0
+ messageSearchIndexInfo.value = null
+ messageSearchSelectedIndex.value = -1
+ searchContext.value = createEmptySearchContext()
+ highlightMessageId.value = ''
+ }
+
+ onMounted(() => {
+ loadSearchHistory()
+ })
+
+ onUnmounted(() => {
+ if (messageSearchDebounceTimer) clearTimeout(messageSearchDebounceTimer)
+ messageSearchDebounceTimer = null
+ stopMessageSearchIndexPolling()
+ if (timeSidebarScrollSyncRaf) {
+ cancelAnimationFrame(timeSidebarScrollSyncRaf)
+ timeSidebarScrollSyncRaf = null
+ }
+ })
+
+ return {
+ messageSearchOpen,
+ messageSearchQuery,
+ messageSearchScope,
+ messageSearchRangeDays,
+ messageSearchSessionType,
+ messageSearchSender,
+ messageSearchSenderOptions,
+ messageSearchSenderLoading,
+ messageSearchSenderError,
+ messageSearchSenderOptionsKey,
+ messageSearchSenderDropdownOpen,
+ messageSearchSenderDropdownRef,
+ messageSearchSenderDropdownInputRef,
+ messageSearchSenderDropdownQuery,
+ messageSearchStartDate,
+ messageSearchEndDate,
+ messageSearchResults,
+ messageSearchLoading,
+ messageSearchError,
+ messageSearchBackendStatus,
+ messageSearchIndexInfo,
+ messageSearchHasMore,
+ messageSearchOffset,
+ messageSearchTotal,
+ messageSearchSelectedIndex,
+ messageSearchInputRef,
+ searchInputFocused,
+ showAdvancedFilters,
+ searchHistory,
+ messageSearchIndexExists,
+ messageSearchIndexReady,
+ messageSearchIndexBuildStatus,
+ messageSearchIndexBuildIndexed,
+ messageSearchIndexMetaCount,
+ messageSearchIndexProgressText,
+ messageSearchIndexText,
+ messageSearchIndexActionText,
+ messageSearchIndexActionDisabled,
+ messageSearchSenderDisabled,
+ messageSearchSelectedSenderInfo,
+ messageSearchSelectedSenderInitial,
+ messageSearchSenderLabel,
+ filteredMessageSearchSenderOptions,
+ searchContextBannerText,
+ timeSidebarOpen,
+ timeSidebarYear,
+ timeSidebarMonth,
+ timeSidebarCounts,
+ timeSidebarMax,
+ timeSidebarTotal,
+ timeSidebarLoading,
+ timeSidebarError,
+ timeSidebarSelectedDate,
+ timeSidebarWeekdays,
+ timeSidebarMonthLabel,
+ timeSidebarYearOptions,
+ timeSidebarActiveDays,
+ timeSidebarCalendarCells,
+ getMessageSearchHitAvatarUrl,
+ getMessageSearchHitAvatarAlt,
+ getMessageSearchHitAvatarInitial,
+ closeMessageSearchSenderDropdown,
+ ensureMessageSearchSendersLoaded,
+ toggleMessageSearchSenderDropdown,
+ selectMessageSearchSender,
+ fetchMessageSearchIndexStatus,
+ fetchMessageSearchSenders,
+ onMessageSearchIndexAction,
+ closeMessageSearch,
+ closeTimeSidebar,
+ loadTimeSidebarMonth,
+ toggleTimeSidebar,
+ prevTimeSidebarMonth,
+ nextTimeSidebarMonth,
+ onTimeSidebarYearMonthChange,
+ toggleMessageSearch,
+ openMessageSearch,
+ runMessageSearch,
+ loadMoreSearchResults,
+ exitSearchContext,
+ locateSearchHit,
+ locateByAnchorId,
+ locateByDate,
+ jumpToConversationFirst,
+ onTimeSidebarDayClick,
+ loadMoreSearchContextAfter,
+ loadMoreSearchContextBefore,
+ onSearchHitClick,
+ onSearchNext,
+ onSearchPrev,
+ syncTimeSidebarSelectedDateFromScroll,
+ onMessageScrollInContextMode,
+ onMessageScroll,
+ clearSearchHistory,
+ applySearchHistory,
+ ensureMessageSearchScopeValid,
+ resetSearchState
+ }
+}
diff --git a/frontend/composables/chat/useChatSessions.js b/frontend/composables/chat/useChatSessions.js
new file mode 100644
index 0000000..5df2802
--- /dev/null
+++ b/frontend/composables/chat/useChatSessions.js
@@ -0,0 +1,293 @@
+import { computed, onMounted, ref } from 'vue'
+import { normalizeSessionPreview } from '~/lib/chat/formatters'
+
+const SESSION_LIST_WIDTH_KEY = 'ui.chat.session_list_width_physical'
+const SESSION_LIST_WIDTH_KEY_LEGACY = 'ui.chat.session_list_width'
+const SESSION_LIST_WIDTH_DEFAULT = 295
+const SESSION_LIST_WIDTH_MIN = 220
+const SESSION_LIST_WIDTH_MAX = 520
+
+export const useChatSessions = ({ chatAccounts, selectedAccount, realtimeEnabled, api }) => {
+ const showSearchAccountSwitcher = false
+
+ const contacts = ref([])
+ const selectedContact = ref(null)
+ const searchQuery = ref('')
+ const isLoadingContacts = ref(false)
+ const contactsError = ref('')
+
+ const sessionListWidth = ref(SESSION_LIST_WIDTH_DEFAULT)
+ const sessionListResizing = ref(false)
+
+ let sessionListResizeStartX = 0
+ let sessionListResizeStartWidth = SESSION_LIST_WIDTH_DEFAULT
+ let sessionListResizeStartDpr = 1
+ let sessionListResizePrevCursor = ''
+ let sessionListResizePrevUserSelect = ''
+
+ const availableAccounts = computed(() => {
+ return Array.isArray(chatAccounts?.accounts) ? chatAccounts.accounts : []
+ })
+
+ const clampSessionListWidth = (value) => {
+ const next = Number.isFinite(value) ? value : SESSION_LIST_WIDTH_DEFAULT
+ return Math.min(SESSION_LIST_WIDTH_MAX, Math.max(SESSION_LIST_WIDTH_MIN, Math.round(next)))
+ }
+
+ const loadSessionListWidth = () => {
+ if (!process.client) return
+ try {
+ const raw = localStorage.getItem(SESSION_LIST_WIDTH_KEY)
+ const value = parseInt(String(raw || ''), 10)
+ if (!Number.isNaN(value)) {
+ sessionListWidth.value = clampSessionListWidth(value)
+ return
+ }
+
+ const legacy = localStorage.getItem(SESSION_LIST_WIDTH_KEY_LEGACY)
+ const legacyValue = parseInt(String(legacy || ''), 10)
+ if (!Number.isNaN(legacyValue)) {
+ const dpr = window.devicePixelRatio || 1
+ const converted = clampSessionListWidth(legacyValue * dpr)
+ sessionListWidth.value = converted
+ try {
+ localStorage.setItem(SESSION_LIST_WIDTH_KEY, String(converted))
+ localStorage.removeItem(SESSION_LIST_WIDTH_KEY_LEGACY)
+ } catch {}
+ }
+ } catch {}
+ }
+
+ const saveSessionListWidth = () => {
+ if (!process.client) return
+ try {
+ localStorage.setItem(SESSION_LIST_WIDTH_KEY, String(clampSessionListWidth(sessionListWidth.value)))
+ } catch {}
+ }
+
+ const setSessionListResizingActive = (active) => {
+ if (!process.client) return
+ try {
+ const body = document.body
+ if (!body) return
+ if (active) {
+ sessionListResizePrevCursor = body.style.cursor || ''
+ sessionListResizePrevUserSelect = body.style.userSelect || ''
+ body.style.cursor = 'col-resize'
+ body.style.userSelect = 'none'
+ } else {
+ body.style.cursor = sessionListResizePrevCursor
+ body.style.userSelect = sessionListResizePrevUserSelect
+ sessionListResizePrevCursor = ''
+ sessionListResizePrevUserSelect = ''
+ }
+ } catch {}
+ }
+
+ const onSessionListResizerPointerMove = (event) => {
+ if (!sessionListResizing.value) return
+ const clientX = Number(event?.clientX || 0)
+ sessionListWidth.value = clampSessionListWidth(
+ sessionListResizeStartWidth + (clientX - sessionListResizeStartX) * (sessionListResizeStartDpr || 1)
+ )
+ }
+
+ const stopSessionListResize = () => {
+ if (!process.client) return
+ if (!sessionListResizing.value) return
+ sessionListResizing.value = false
+ setSessionListResizingActive(false)
+ try {
+ window.removeEventListener('pointermove', onSessionListResizerPointerMove)
+ } catch {}
+ saveSessionListWidth()
+ }
+
+ const onSessionListResizerPointerUp = () => {
+ stopSessionListResize()
+ }
+
+ const onSessionListResizerPointerDown = (event) => {
+ if (!process.client) return
+ try {
+ event?.preventDefault?.()
+ } catch {}
+
+ sessionListResizing.value = true
+ sessionListResizeStartX = Number(event?.clientX || 0)
+ sessionListResizeStartWidth = Number(sessionListWidth.value || SESSION_LIST_WIDTH_DEFAULT)
+ sessionListResizeStartDpr = window.devicePixelRatio || 1
+ setSessionListResizingActive(true)
+
+ try {
+ window.addEventListener('pointermove', onSessionListResizerPointerMove)
+ window.addEventListener('pointerup', onSessionListResizerPointerUp, { once: true })
+ } catch {}
+ }
+
+ const resetSessionListWidth = () => {
+ sessionListWidth.value = SESSION_LIST_WIDTH_DEFAULT
+ saveSessionListWidth()
+ }
+
+ onMounted(() => {
+ loadSessionListWidth()
+ })
+
+ const filteredContacts = computed(() => {
+ const query = String(searchQuery.value || '').trim().toLowerCase()
+ if (!query) return contacts.value
+ return contacts.value.filter((contact) => {
+ const name = String(contact?.name || '').toLowerCase()
+ const username = String(contact?.username || '').toLowerCase()
+ return name.includes(query) || username.includes(query)
+ })
+ })
+
+ const mapSessions = (sessions) => {
+ return sessions.map((session) => ({
+ id: session.id,
+ name: session.name || session.username || session.id,
+ avatar: session.avatar || null,
+ lastMessage: normalizeSessionPreview(session.lastMessage || ''),
+ lastMessageTime: session.lastMessageTime || '',
+ unreadCount: session.unreadCount || 0,
+ isGroup: !!session.isGroup,
+ isTop: !!session.isTop,
+ username: session.username
+ }))
+ }
+
+ const clearContactsState = (errorMessage = '') => {
+ contacts.value = []
+ selectedContact.value = null
+ contactsError.value = errorMessage
+ }
+
+ const loadSessionsForSelectedAccount = async () => {
+ if (!selectedAccount.value) {
+ clearContactsState('')
+ return []
+ }
+
+ const fetchSessions = async (source) => {
+ const params = {
+ account: selectedAccount.value,
+ limit: 400,
+ include_hidden: false,
+ include_official: false
+ }
+ if (source) params.source = source
+ return api.listChatSessions(params)
+ }
+
+ let sessionsResp = null
+ if (realtimeEnabled?.value) {
+ try {
+ sessionsResp = await fetchSessions('realtime')
+ } catch {
+ sessionsResp = null
+ }
+ }
+ if (!sessionsResp) {
+ sessionsResp = await fetchSessions('')
+ }
+
+ const sessions = Array.isArray(sessionsResp?.sessions) ? sessionsResp.sessions : []
+ contacts.value = mapSessions(sessions)
+ contactsError.value = ''
+ return contacts.value
+ }
+
+ const refreshSessionsForSelectedAccount = async ({ sourceOverride } = {}) => {
+ if (!process.client || typeof window === 'undefined') return
+ if (!selectedAccount.value) return
+ if (isLoadingContacts.value) return
+
+ const previousUsername = selectedContact.value?.username || ''
+ const desiredSource = (sourceOverride != null)
+ ? String(sourceOverride || '').trim()
+ : (realtimeEnabled?.value ? 'realtime' : '')
+
+ const params = {
+ account: selectedAccount.value,
+ limit: 400,
+ include_hidden: false,
+ include_official: false
+ }
+
+ let sessionsResp = null
+ if (desiredSource) {
+ try {
+ sessionsResp = await api.listChatSessions({ ...params, source: desiredSource })
+ } catch {
+ sessionsResp = null
+ }
+ }
+ if (!sessionsResp) {
+ try {
+ sessionsResp = await api.listChatSessions(params)
+ } catch {
+ return
+ }
+ }
+
+ const sessions = Array.isArray(sessionsResp?.sessions) ? sessionsResp.sessions : []
+ const nextContacts = mapSessions(sessions)
+ contacts.value = nextContacts
+
+ if (previousUsername) {
+ const matched = nextContacts.find((contact) => contact.username === previousUsername)
+ if (matched) selectedContact.value = matched
+ }
+ }
+
+ const loadContacts = async () => {
+ if (contacts.value.length && !isLoadingContacts.value) {
+ return { usedPrefetched: true }
+ }
+
+ isLoadingContacts.value = true
+ contactsError.value = ''
+ try {
+ const hadLoadedAccountSnapshot = !!chatAccounts.loaded
+ await chatAccounts.ensureLoaded()
+ if (!selectedAccount.value && hadLoadedAccountSnapshot) {
+ await chatAccounts.ensureLoaded({ force: true })
+ }
+
+ if (!selectedAccount.value) {
+ clearContactsState(chatAccounts.error || '未检测到已解密账号,请先解密数据库。')
+ return { usedPrefetched: false }
+ }
+
+ await loadSessionsForSelectedAccount()
+ return { usedPrefetched: false }
+ } catch (error) {
+ clearContactsState(error?.message || '加载联系人失败')
+ return { usedPrefetched: false }
+ } finally {
+ isLoadingContacts.value = false
+ }
+ }
+
+ return {
+ showSearchAccountSwitcher,
+ availableAccounts,
+ contacts,
+ selectedContact,
+ searchQuery,
+ filteredContacts,
+ isLoadingContacts,
+ contactsError,
+ sessionListWidth,
+ sessionListResizing,
+ clearContactsState,
+ loadContacts,
+ loadSessionsForSelectedAccount,
+ refreshSessionsForSelectedAccount,
+ onSessionListResizerPointerDown,
+ stopSessionListResize,
+ resetSessionListWidth
+ }
+}
diff --git a/frontend/composables/useApi.js b/frontend/composables/useApi.js
index 9c4aa57..13efb89 100644
--- a/frontend/composables/useApi.js
+++ b/frontend/composables/useApi.js
@@ -1,20 +1,28 @@
+import { reportServerError } from '~/lib/server-error-logging'
+
// API请求组合式函数
export const useApi = () => {
- const config = useRuntimeConfig()
+ const baseURL = useApiBase()
// 基础请求函数
const request = async (url, options = {}) => {
try {
- // 在客户端使用完整的API路径
- const baseURL = process.client ? 'http://localhost:8000/api' : '/api'
-
const response = await $fetch(url, {
baseURL,
...options,
- onResponseError({ response }) {
+ async onResponseError({ response }) {
if (response.status === 400) {
throw new Error(response._data?.detail || '请求参数错误')
- } else if (response.status === 500) {
+ } else if (response.status >= 500) {
+ await reportServerError({
+ status: response.status,
+ method: options?.method || 'GET',
+ requestUrl: url,
+ message: '服务器错误,请稍后重试',
+ backendDetail: response._data?.detail || '',
+ source: 'useApi',
+ apiBase: baseURL,
+ })
throw new Error('服务器错误,请稍后重试')
}
}
@@ -63,6 +71,22 @@ export const useApi = () => {
return await request('/chat/accounts')
}
+ const getChatAccountInfo = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ const url = '/chat/account_info' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ const deleteChatAccount = async (params = {}) => {
+ const account = String(params?.account || '').trim()
+ if (!account) throw new Error('Missing account')
+ const query = new URLSearchParams()
+ query.set('account', account)
+ const url = '/chat/account' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url, { method: 'DELETE' })
+ }
+
const listChatSessions = async (params = {}) => {
const query = new URLSearchParams()
if (params && params.account) query.set('account', params.account)
@@ -87,6 +111,75 @@ export const useApi = () => {
return await request(url)
}
+ const getChatMessageRaw = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.username) query.set('username', params.username)
+ if (params && params.message_id) query.set('message_id', params.message_id)
+ const url = '/chat/messages/raw' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ const editChatMessage = async (payload = {}) => {
+ return await request('/chat/messages/edit', {
+ method: 'POST',
+ body: payload
+ })
+ }
+
+ const repairChatMessageSender = async (payload = {}) => {
+ return await request('/chat/messages/repair_sender', {
+ method: 'POST',
+ body: payload
+ })
+ }
+
+ // Flip message direction in the WeChat client by swapping packed_info_data (unsafe, but undoable via reset).
+ const flipChatMessageDirection = async (payload = {}) => {
+ return await request('/chat/messages/flip_direction', {
+ method: 'POST',
+ body: payload
+ })
+ }
+
+ const listChatEditedSessions = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ const url = '/chat/edits/sessions' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ const listChatEditedMessages = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.username) query.set('username', params.username)
+ const url = '/chat/edits/messages' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ const getChatEditStatus = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.username) query.set('username', params.username)
+ if (params && params.message_id) query.set('message_id', params.message_id)
+ const url = '/chat/edits/message_status' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ const resetChatEditedMessage = async (payload = {}) => {
+ return await request('/chat/edits/reset_message', {
+ method: 'POST',
+ body: payload
+ })
+ }
+
+ const resetChatEditedSession = async (payload = {}) => {
+ return await request('/chat/edits/reset_session', {
+ method: 'POST',
+ body: payload
+ })
+ }
+
const getChatRealtimeStatus = async (params = {}) => {
const query = new URLSearchParams()
if (params && params.account) query.set('account', params.account)
@@ -99,6 +192,7 @@ export const useApi = () => {
if (params && params.account) query.set('account', params.account)
if (params && params.username) query.set('username', params.username)
if (params && params.max_scan != null) query.set('max_scan', String(params.max_scan))
+ if (params && params.backfill_limit != null) query.set('backfill_limit', String(params.backfill_limit))
const url = '/chat/realtime/sync' + (query.toString() ? `?${query.toString()}` : '')
return await request(url, { method: 'POST' })
}
@@ -111,6 +205,8 @@ export const useApi = () => {
if (params && params.priority_max_scan != null) query.set('priority_max_scan', String(params.priority_max_scan))
if (params && params.include_hidden != null) query.set('include_hidden', String(!!params.include_hidden))
if (params && params.include_official != null) query.set('include_official', String(!!params.include_official))
+ if (params && params.only_official != null) query.set('only_official', String(!!params.only_official))
+ if (params && params.backfill_limit != null) query.set('backfill_limit', String(params.backfill_limit))
const url = '/chat/realtime/sync_all' + (query.toString() ? `?${query.toString()}` : '')
return await request(url, { method: 'POST' })
}
@@ -179,6 +275,96 @@ export const useApi = () => {
return await request(url)
}
+ // 聊天记录日历热力图:某月每日消息数
+ const getChatMessageDailyCounts = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.username) query.set('username', params.username)
+ if (params && params.year != null) query.set('year', String(params.year))
+ if (params && params.month != null) query.set('month', String(params.month))
+ const url = '/chat/messages/daily_counts' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // 聊天记录定位锚点:某日第一条 / 会话最早一条
+ const getChatMessageAnchor = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.username) query.set('username', params.username)
+ if (params && params.kind) query.set('kind', String(params.kind))
+ if (params && params.date) query.set('date', String(params.date))
+ const url = '/chat/messages/anchor' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // 解析嵌套合并转发聊天记录(通过 server_id)
+ const resolveNestedChatHistory = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.server_id != null) query.set('server_id', String(params.server_id))
+ const url = '/chat/chat_history/resolve' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // 解析卡片/小程序等 App 消息(通过 server_id)
+ const resolveAppMsg = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.server_id != null) query.set('server_id', String(params.server_id))
+ const url = '/chat/appmsg/resolve' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // 朋友圈时间线
+ const listSnsTimeline = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.limit != null) query.set('limit', String(params.limit))
+ if (params && params.offset != null) query.set('offset', String(params.offset))
+ if (params && params.usernames && Array.isArray(params.usernames) && params.usernames.length > 0) {
+ query.set('usernames', params.usernames.join(','))
+ } else if (params && params.usernames && typeof params.usernames === 'string') {
+ query.set('usernames', params.usernames)
+ }
+ if (params && params.keyword) query.set('keyword', params.keyword)
+ const url = '/sns/timeline' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // 朋友圈联系人列表(按发圈数统计)
+ const listSnsUsers = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.keyword) query.set('keyword', String(params.keyword))
+ if (params && params.limit != null) query.set('limit', String(params.limit))
+ const url = '/sns/users' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // 朋友圈图片本地缓存候选(用于错图时手动选择)
+ const listSnsMediaCandidates = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.create_time != null) query.set('create_time', String(params.create_time))
+ if (params && params.width != null) query.set('width', String(params.width))
+ if (params && params.height != null) query.set('height', String(params.height))
+ if (params && params.limit != null) query.set('limit', String(params.limit))
+ if (params && params.offset != null) query.set('offset', String(params.offset))
+ const url = '/sns/media_candidates' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // 保存朋友圈图片手动匹配结果(本机)
+ const saveSnsMediaPicks = async (data = {}) => {
+ return await request('/sns/media_picks', {
+ method: 'POST',
+ body: {
+ account: data.account || null,
+ picks: (data && data.picks && typeof data.picks === 'object' && !Array.isArray(data.picks)) ? data.picks : {}
+ }
+ })
+ }
+
const openChatMediaFolder = async (params = {}) => {
const query = new URLSearchParams()
if (params && params.account) query.set('account', params.account)
@@ -251,7 +437,10 @@ export const useApi = () => {
message_types: Array.isArray(data.message_types) ? data.message_types : [],
include_media: data.include_media == null ? true : !!data.include_media,
media_kinds: Array.isArray(data.media_kinds) ? data.media_kinds : ['image', 'emoji', 'video', 'video_thumb', 'voice', 'file'],
+ output_dir: data.output_dir == null ? null : String(data.output_dir || '').trim(),
allow_process_key_extract: !!data.allow_process_key_extract,
+ download_remote_media: !!data.download_remote_media,
+ html_page_size: data.html_page_size != null ? Number(data.html_page_size) : 1000,
privacy_mode: !!data.privacy_mode,
file_name: data.file_name || null
}
@@ -271,15 +460,166 @@ export const useApi = () => {
if (!exportId) throw new Error('Missing exportId')
return await request(`/chat/exports/${encodeURIComponent(String(exportId))}`, { method: 'DELETE' })
}
-
+
+ // 朋友圈导出(离线 HTML zip)
+ const createSnsExport = async (data = {}) => {
+ return await request('/sns/exports', {
+ method: 'POST',
+ body: {
+ account: data.account || null,
+ scope: data.scope || 'selected',
+ usernames: Array.isArray(data.usernames) ? data.usernames : [],
+ use_cache: data.use_cache == null ? true : !!data.use_cache,
+ output_dir: data.output_dir == null ? null : String(data.output_dir || '').trim(),
+ file_name: data.file_name || null
+ }
+ })
+ }
+
+ const getSnsExport = async (exportId) => {
+ if (!exportId) throw new Error('Missing exportId')
+ return await request(`/sns/exports/${encodeURIComponent(String(exportId))}`)
+ }
+
+ const cancelSnsExport = async (exportId) => {
+ if (!exportId) throw new Error('Missing exportId')
+ return await request(`/sns/exports/${encodeURIComponent(String(exportId))}`, { method: 'DELETE' })
+ }
+
+ // 联系人
+ const listChatContacts = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.keyword) query.set('keyword', params.keyword)
+ if (params && params.include_friends != null) query.set('include_friends', String(!!params.include_friends))
+ if (params && params.include_groups != null) query.set('include_groups', String(!!params.include_groups))
+ if (params && params.include_officials != null) query.set('include_officials', String(!!params.include_officials))
+ const url = '/chat/contacts' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ const exportChatContacts = async (payload = {}) => {
+ return await request('/chat/contacts/export', {
+ method: 'POST',
+ body: {
+ account: payload.account || null,
+ output_dir: payload.output_dir || '',
+ format: payload.format || 'json',
+ include_avatar_link: payload.include_avatar_link == null ? true : !!payload.include_avatar_link,
+ keyword: payload.keyword || null,
+ contact_types: {
+ friends: payload?.contact_types?.friends == null ? true : !!payload.contact_types.friends,
+ groups: payload?.contact_types?.groups == null ? true : !!payload.contact_types.groups,
+ officials: payload?.contact_types?.officials == null ? true : !!payload.contact_types.officials,
+ }
+ }
+ })
+ }
+
+ // WeChat Wrapped(年度总结)
+ const getWrappedAnnual = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.year != null) query.set('year', String(params.year))
+ if (params && params.account) query.set('account', String(params.account))
+ if (params && params.refresh != null) query.set('refresh', String(!!params.refresh))
+ const url = '/wrapped/annual' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // WeChat Wrapped(年度总结)- 目录/元信息(轻量,用于按页懒加载)
+ const getWrappedAnnualMeta = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.year != null) query.set('year', String(params.year))
+ if (params && params.account) query.set('account', String(params.account))
+ if (params && params.refresh != null) query.set('refresh', String(!!params.refresh))
+ const url = '/wrapped/annual/meta' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // WeChat Wrapped(年度总结)- 单张卡片(按页加载)
+ const getWrappedAnnualCard = async (cardId, params = {}) => {
+ if (cardId == null) throw new Error('Missing cardId')
+ const query = new URLSearchParams()
+ if (params && params.year != null) query.set('year', String(params.year))
+ if (params && params.account) query.set('account', String(params.account))
+ if (params && params.refresh != null) query.set('refresh', String(!!params.refresh))
+ const safeId = encodeURIComponent(String(cardId))
+ const url = `/wrapped/annual/cards/${safeId}` + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // 获取微信进程状态
+ const getWxStatus = async () => {
+ return await request('/wechat/status')
+ }
+
+ // 获取数据库密钥
+ const getKeys = async () => {
+ return await request('/get_keys')
+ }
+
+ // 获取图片密钥
+ const getImageKey = async () => {
+ return await request('/get_image_key')
+ }
+
+ // 枚举服务号信息
+ const listBizAccounts = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ const url = '/biz/list' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // 获取普通服务号消息
+ const listBizMessages = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.username) query.set('username', params.username)
+ if (params && params.limit != null) query.set('limit', String(params.limit))
+ if (params && params.offset != null) query.set('offset', String(params.offset))
+ const url = '/biz/messages' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ // 获取微信支付记录
+ const listBizPayRecords = async (params = {}) => {
+ const query = new URLSearchParams()
+ if (params && params.account) query.set('account', params.account)
+ if (params && params.limit != null) query.set('limit', String(params.limit))
+ if (params && params.offset != null) query.set('offset', String(params.offset))
+ const url = '/biz/pay_records' + (query.toString() ? `?${query.toString()}` : '')
+ return await request(url)
+ }
+
+ const getBizProxyImageUrl = (url) => {
+ if (!url) return ''
+ if (url.startsWith('data:')) return url // 如果已经是 base64,不处理
+ const query = new URLSearchParams()
+ query.set('url', url)
+ const base = baseURL ? baseURL.replace(/\/$/, '') : ''
+ return `${base}/biz/proxy_image?${query.toString()}`
+ }
+
return {
detectWechat,
detectCurrentAccount,
decryptDatabase,
healthCheck,
listChatAccounts,
+ getChatAccountInfo,
+ deleteChatAccount,
listChatSessions,
listChatMessages,
+ getChatMessageRaw,
+ editChatMessage,
+ repairChatMessageSender,
+ flipChatMessageDirection,
+ listChatEditedSessions,
+ listChatEditedMessages,
+ getChatEditStatus,
+ resetChatEditedMessage,
+ resetChatEditedSession,
getChatRealtimeStatus,
syncChatRealtimeMessages,
syncChatRealtimeAll,
@@ -288,6 +628,14 @@ export const useApi = () => {
buildChatSearchIndex,
listChatSearchSenders,
getChatMessagesAround,
+ getChatMessageDailyCounts,
+ getChatMessageAnchor,
+ resolveNestedChatHistory,
+ resolveAppMsg,
+ listSnsTimeline,
+ listSnsUsers,
+ listSnsMediaCandidates,
+ saveSnsMediaPicks,
openChatMediaFolder,
downloadChatEmoji,
saveMediaKeys,
@@ -296,6 +644,21 @@ export const useApi = () => {
createChatExport,
getChatExport,
listChatExports,
- cancelChatExport
+ cancelChatExport,
+ createSnsExport,
+ getSnsExport,
+ cancelSnsExport,
+ listChatContacts,
+ exportChatContacts,
+ getWrappedAnnual,
+ getWrappedAnnualMeta,
+ getWrappedAnnualCard,
+ getKeys,
+ getImageKey,
+ getWxStatus,
+ listBizAccounts,
+ listBizMessages,
+ listBizPayRecords,
+ getBizProxyImageUrl,
}
}
diff --git a/frontend/composables/useApiBase.js b/frontend/composables/useApiBase.js
new file mode 100644
index 0000000..f18dbc0
--- /dev/null
+++ b/frontend/composables/useApiBase.js
@@ -0,0 +1,45 @@
+import { normalizeApiBase, readApiBaseOverride } from '~/lib/api-settings'
+
+// Client-side cache so that useApiBase() can be called safely outside
+// the Nuxt composable context (e.g. inside async callbacks / onMounted chains).
+let _clientCache = ''
+
+const shouldIgnoreStoredOverride = () => {
+ if (!process.client || !import.meta.dev) return false
+ return typeof window !== 'undefined' && !!window.wechatDesktop?.__brand
+}
+
+export const useApiBase = () => {
+ if (process.client && _clientCache) return _clientCache
+
+ // useRuntimeConfig() requires the Nuxt app context, which is only
+ // guaranteed during synchronous setup. On the client we cache the
+ // result so later (context-less) calls still work.
+ let config
+ try {
+ config = useRuntimeConfig()
+ } catch {
+ // Context unavailable – fall back to cached value or default.
+ return _clientCache || '/api'
+ }
+
+ // Default to same-origin `/api` so Nuxt devProxy / backend-mounted UI both work.
+ // Override priority:
+ // 1) Local UI setting (web + desktop)
+ // 2) NUXT_PUBLIC_API_BASE env/runtime config
+ // 3) `/api`
+ const override = process.client && !shouldIgnoreStoredOverride() ? readApiBaseOverride() : ''
+ const runtime = String(config?.public?.apiBase || '').trim()
+ const result = normalizeApiBase(override || runtime || '/api')
+
+ if (process.client) _clientCache = result
+ return result
+}
+
+/**
+ * Call this when the user changes the API base override in settings
+ * so the cached value is refreshed.
+ */
+export const invalidateApiBaseCache = () => {
+ _clientCache = ''
+}
diff --git a/frontend/composables/useDesktopUpdate.js b/frontend/composables/useDesktopUpdate.js
new file mode 100644
index 0000000..676682e
--- /dev/null
+++ b/frontend/composables/useDesktopUpdate.js
@@ -0,0 +1,236 @@
+let listenersInitialized = false;
+let removeListeners = [];
+
+const getDesktopApi = () => {
+ if (!process.client) return null;
+ if (typeof window === "undefined") return null;
+ return window?.wechatDesktop || null;
+};
+
+const isDesktopShell = () => !!getDesktopApi();
+
+const isUpdaterSupported = () => {
+ const api = getDesktopApi();
+ if (!api) return false;
+
+ // If the bridge exposes a brand marker, ensure it's our Electron shell.
+ if (api.__brand && api.__brand !== "WeChatDataAnalysisDesktop") return false;
+
+ // Require updater IPC to avoid showing update UI in the pure web build.
+ return (
+ typeof api.getVersion === "function" &&
+ typeof api.checkForUpdates === "function" &&
+ typeof api.downloadAndInstall === "function"
+ );
+};
+
+export const useDesktopUpdate = () => {
+ const info = useState("desktopUpdate.info", () => null);
+ const open = useState("desktopUpdate.open", () => false);
+ const isDownloading = useState("desktopUpdate.isDownloading", () => false);
+ const readyToInstall = useState("desktopUpdate.readyToInstall", () => false);
+ const progress = useState("desktopUpdate.progress", () => ({ percent: 0 }));
+ const error = useState("desktopUpdate.error", () => "");
+ const currentVersion = useState("desktopUpdate.currentVersion", () => "");
+
+ const manualCheckLoading = useState("desktopUpdate.manualCheckLoading", () => false);
+ const lastCheckMessage = useState("desktopUpdate.lastCheckMessage", () => "");
+ const lastCheckAt = useState("desktopUpdate.lastCheckAt", () => 0);
+
+ const setUpdateInfo = (payload) => {
+ if (!payload) return;
+ const version = String(payload?.version || "").trim();
+ const releaseNotes = String(payload?.releaseNotes || "");
+ if (!version) return;
+ info.value = { version, releaseNotes };
+ readyToInstall.value = false;
+ };
+
+ const dismiss = () => {
+ open.value = false;
+ };
+
+ const refreshVersion = async () => {
+ if (!isUpdaterSupported()) return "";
+ try {
+ const v = await getDesktopApi()?.getVersion?.();
+ currentVersion.value = String(v || "");
+ return currentVersion.value;
+ } catch {
+ return currentVersion.value || "";
+ }
+ };
+
+ const initListeners = async () => {
+ if (!isUpdaterSupported()) return;
+ if (listenersInitialized) return;
+ listenersInitialized = true;
+
+ await refreshVersion();
+
+ const unsubs = [];
+
+ const unUpdate = window.wechatDesktop?.onUpdateAvailable?.((payload) => {
+ error.value = "";
+ isDownloading.value = false;
+ readyToInstall.value = false;
+ progress.value = { percent: 0 };
+ setUpdateInfo(payload);
+ open.value = true;
+ });
+ if (typeof unUpdate === "function") unsubs.push(unUpdate);
+
+ const unProgress = window.wechatDesktop?.onDownloadProgress?.((p) => {
+ progress.value = p || { percent: 0 };
+ const percent = Number(progress.value?.percent || 0);
+ if (Number.isFinite(percent) && percent > 0) {
+ isDownloading.value = true;
+ }
+ });
+ if (typeof unProgress === "function") unsubs.push(unProgress);
+
+ const unDownloaded = window.wechatDesktop?.onUpdateDownloaded?.((payload) => {
+ // Download finished. Keep the dialog open and let the user decide when to install.
+ setUpdateInfo(payload || info.value || {});
+ isDownloading.value = false;
+ readyToInstall.value = true;
+ progress.value = { ...(progress.value || {}), percent: 100 };
+ open.value = true;
+ });
+ if (typeof unDownloaded === "function") unsubs.push(unDownloaded);
+
+ const unError = window.wechatDesktop?.onUpdateError?.((payload) => {
+ const msg = String(payload?.message || "");
+ if (msg) error.value = msg;
+ isDownloading.value = false;
+ readyToInstall.value = false;
+ });
+ if (typeof unError === "function") unsubs.push(unError);
+
+ removeListeners = unsubs;
+ };
+
+ const startUpdate = async () => {
+ if (!isUpdaterSupported()) return;
+
+ error.value = "";
+ isDownloading.value = true;
+ readyToInstall.value = false;
+ progress.value = { percent: 0 };
+
+ try {
+ await getDesktopApi()?.downloadAndInstall?.();
+ } catch (e) {
+ const msg = e?.message || String(e);
+ error.value = msg;
+ isDownloading.value = false;
+ }
+ };
+
+ const installUpdate = async () => {
+ if (!isUpdaterSupported()) return;
+ if (!getDesktopApi()?.installUpdate) return;
+
+ error.value = "";
+ try {
+ await getDesktopApi()?.installUpdate?.();
+ } catch (e) {
+ const msg = e?.message || String(e);
+ error.value = msg;
+ }
+ };
+
+ const ignore = async () => {
+ if (!isUpdaterSupported()) return;
+ const version = String(info.value?.version || "").trim();
+ if (!version) return;
+
+ try {
+ await getDesktopApi()?.ignoreUpdate?.(version);
+ } catch (e) {
+ const msg = e?.message || String(e);
+ error.value = msg;
+ } finally {
+ // Hide the dialog locally; startup auto-check will also respect the ignore.
+ open.value = false;
+ info.value = null;
+ }
+ };
+
+ const manualCheck = async () => {
+ if (!isDesktopShell()) {
+ lastCheckMessage.value = "仅桌面端可用。";
+ return { hasUpdate: false };
+ }
+ if (!isUpdaterSupported()) {
+ lastCheckMessage.value = "当前桌面端版本不支持自动更新。";
+ return { hasUpdate: false };
+ }
+
+ manualCheckLoading.value = true;
+ error.value = "";
+ lastCheckMessage.value = "";
+
+ try {
+ await refreshVersion();
+
+ const res = await getDesktopApi()?.checkForUpdates?.();
+ lastCheckAt.value = Date.now();
+
+ if (res?.enabled === false) {
+ lastCheckMessage.value = "自动更新已禁用(仅打包版本可用)。";
+ return res;
+ }
+
+ if (res?.error) {
+ lastCheckMessage.value = `检查更新失败:${String(res.error)}`;
+ return res;
+ }
+
+ if (res?.hasUpdate && res?.version) {
+ setUpdateInfo({ version: res.version, releaseNotes: res.releaseNotes || "" });
+ open.value = true;
+ lastCheckMessage.value = `发现新版本:${String(res.version)}`;
+ return res;
+ }
+
+ lastCheckMessage.value = "当前已是最新版本。";
+ return res;
+ } catch (e) {
+ const msg = e?.message || String(e);
+ lastCheckMessage.value = `检查更新失败:${msg}`;
+ return { hasUpdate: false, error: msg };
+ } finally {
+ manualCheckLoading.value = false;
+ }
+ };
+
+ const cleanup = () => {
+ try {
+ for (const fn of removeListeners) fn?.();
+ } catch {}
+ removeListeners = [];
+ listenersInitialized = false;
+ };
+
+ return {
+ info,
+ open,
+ isDownloading,
+ readyToInstall,
+ progress,
+ error,
+ currentVersion,
+ manualCheckLoading,
+ lastCheckMessage,
+ lastCheckAt,
+ initListeners,
+ refreshVersion,
+ manualCheck,
+ startUpdate,
+ installUpdate,
+ ignore,
+ dismiss,
+ cleanup,
+ };
+};
diff --git a/frontend/composables/useSettingsDialog.js b/frontend/composables/useSettingsDialog.js
new file mode 100644
index 0000000..6727c56
--- /dev/null
+++ b/frontend/composables/useSettingsDialog.js
@@ -0,0 +1,17 @@
+export const useSettingsDialog = () => {
+ const open = useState('settings-dialog-open', () => false)
+
+ const openDialog = () => {
+ open.value = true
+ }
+
+ const closeDialog = () => {
+ open.value = false
+ }
+
+ return {
+ open,
+ openDialog,
+ closeDialog,
+ }
+}
diff --git a/frontend/lib/api-settings.js b/frontend/lib/api-settings.js
new file mode 100644
index 0000000..a333d5d
--- /dev/null
+++ b/frontend/lib/api-settings.js
@@ -0,0 +1,35 @@
+export const API_BASE_OVERRIDE_KEY = 'ui.apiBaseOverride'
+
+export const readApiBaseOverride = () => {
+ if (!process.client) return ''
+ try {
+ const raw = localStorage.getItem(API_BASE_OVERRIDE_KEY)
+ return String(raw || '').trim()
+ } catch {
+ return ''
+ }
+}
+
+export const writeApiBaseOverride = (value) => {
+ if (!process.client) return
+ try {
+ const v = String(value || '').trim()
+ if (!v) localStorage.removeItem(API_BASE_OVERRIDE_KEY)
+ else localStorage.setItem(API_BASE_OVERRIDE_KEY, v)
+ } catch {}
+}
+
+export const normalizeApiBase = (value) => {
+ const raw = String(value || '').trim()
+ if (!raw) return '/api'
+
+ let v = raw.replace(/\/$/, '')
+
+ // If a full origin is provided, auto-append `/api` when missing.
+ if (/^https?:\/\//i.test(v) && !/\/api$/i.test(v)) {
+ v = `${v}/api`
+ }
+
+ return v.replace(/\/$/, '')
+}
+
diff --git a/frontend/lib/chat/chat-history.js b/frontend/lib/chat/chat-history.js
new file mode 100644
index 0000000..6d1d388
--- /dev/null
+++ b/frontend/lib/chat/chat-history.js
@@ -0,0 +1,474 @@
+import { getChatHistoryPreviewLines } from '~/lib/chat/formatters'
+
+export const isMaybeMd5 = (value) => /^[0-9a-f]{32}$/i.test(String(value || '').trim())
+
+export const pickFirstMd5 = (...values) => {
+ for (const value of values) {
+ const text = String(value || '').trim()
+ if (isMaybeMd5(text)) return text.toLowerCase()
+ }
+ return ''
+}
+
+export const normalizeChatHistoryUrl = (value) => String(value || '').trim().replace(/\s+/g, '')
+
+export const stripWeChatInvisible = (value) => {
+ return String(value || '').replace(/[\u3164\u2800]/g, '').trim()
+}
+
+export const parseChatHistoryRecord = (recordItemXml) => {
+ if (!process.client) return { info: null, items: [] }
+ const xml = String(recordItemXml || '').trim()
+ if (!xml) return { info: null, items: [] }
+
+ const normalized = xml
+ .replace(/ /g, ' ')
+ .replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g, '')
+ .replace(/&(?!amp;|lt;|gt;|quot;|apos;|#\d+;|#x[\da-fA-F]+;)/g, '&')
+
+ let doc
+ try {
+ doc = new DOMParser().parseFromString(normalized, 'text/xml')
+ } catch {
+ return { info: null, items: [] }
+ }
+
+ const parserErrors = doc.getElementsByTagName('parsererror')
+ if (parserErrors && parserErrors.length) return { info: null, items: [] }
+
+ const getText = (node, tag) => {
+ try {
+ if (!node) return ''
+ const elements = Array.from(node.getElementsByTagName(tag) || [])
+ const direct = elements.find((el) => el && el.parentNode === node)
+ const target = direct || elements[0]
+ return String(target?.textContent || '').trim()
+ } catch {
+ return ''
+ }
+ }
+
+ const getDirectChildXml = (node, tag) => {
+ try {
+ if (!node) return ''
+ const children = Array.from(node.children || [])
+ const target = children.find((child) => String(child?.tagName || '').toLowerCase() === String(tag || '').toLowerCase())
+ if (!target) return ''
+ const raw = String(target.textContent || '').trim()
+ if (raw && raw.startsWith('<') && raw.endsWith('>')) return raw
+ if (typeof XMLSerializer !== 'undefined') {
+ return new XMLSerializer().serializeToString(target)
+ }
+ } catch {}
+ return ''
+ }
+
+ const getAnyXml = (node, tag) => {
+ try {
+ if (!node) return ''
+ const elements = Array.from(node.getElementsByTagName(tag) || [])
+ const direct = elements.find((el) => el && el.parentNode === node)
+ const target = direct || elements[0]
+ if (!target) return ''
+ const raw = String(target.textContent || '').trim()
+ if (raw && raw.startsWith('<') && raw.endsWith('>')) return raw
+ if (typeof XMLSerializer !== 'undefined') return new XMLSerializer().serializeToString(target)
+ } catch {}
+ return ''
+ }
+
+ const sameTag = (element, tag) => String(element?.tagName || '').toLowerCase() === String(tag || '').toLowerCase()
+
+ const closestAncestorByTag = (node, tag) => {
+ const lower = String(tag || '').toLowerCase()
+ let current = node
+ while (current) {
+ if (current.nodeType === 1 && String(current.tagName || '').toLowerCase() === lower) return current
+ current = current.parentNode
+ }
+ return null
+ }
+
+ const root = doc?.documentElement
+ const isChatRoom = String(getText(root, 'isChatRoom') || '').trim() === '1'
+ const title = getText(root, 'title')
+ const desc = getText(root, 'desc') || getText(root, 'info')
+
+ const datalist = (() => {
+ try {
+ const all = Array.from(doc.getElementsByTagName('datalist') || [])
+ const top = root ? all.find((el) => closestAncestorByTag(el, 'recorditem') === root) : null
+ return top || all[0] || null
+ } catch {
+ return null
+ }
+ })()
+
+ const datalistCount = (() => {
+ try {
+ if (!datalist) return 0
+ const value = String(datalist.getAttribute('count') || '').trim()
+ return Math.max(0, parseInt(value, 10) || 0)
+ } catch {
+ return 0
+ }
+ })()
+
+ const itemNodes = (() => {
+ if (datalist) return Array.from(datalist.children || []).filter((el) => sameTag(el, 'dataitem'))
+ return Array.from(root?.children || []).filter((el) => sameTag(el, 'dataitem'))
+ })()
+
+ const parsed = itemNodes.map((node, idx) => {
+ const datatype = String(node.getAttribute('datatype') || getText(node, 'datatype') || '').trim()
+ const dataid = String(node.getAttribute('dataid') || getText(node, 'dataid') || '').trim() || String(idx)
+
+ const sourcename = getText(node, 'sourcename')
+ const sourcetime = getText(node, 'sourcetime')
+ const sourceheadurl = normalizeChatHistoryUrl(getText(node, 'sourceheadurl'))
+ const datatitle = getText(node, 'datatitle')
+ const datadesc = getText(node, 'datadesc')
+ const link = normalizeChatHistoryUrl(getText(node, 'link') || getText(node, 'dataurl') || getText(node, 'url'))
+ const datafmt = getText(node, 'datafmt')
+ const duration = getText(node, 'duration')
+
+ const fullmd5 = getText(node, 'fullmd5')
+ const thumbfullmd5 = getText(node, 'thumbfullmd5')
+ const md5 = getText(node, 'md5') || getText(node, 'emoticonmd5') || getText(node, 'emojiMd5')
+ const fromnewmsgid = getText(node, 'fromnewmsgid')
+ const srcMsgLocalid = getText(node, 'srcMsgLocalid') || getText(node, 'srcMsgLocalId')
+ const srcMsgCreateTime = getText(node, 'srcMsgCreateTime')
+ const cdnurlstring = normalizeChatHistoryUrl(getText(node, 'cdnurlstring'))
+ const encrypturlstring = normalizeChatHistoryUrl(getText(node, 'encrypturlstring'))
+ const externurl = normalizeChatHistoryUrl(getText(node, 'externurl'))
+ const aeskey = getText(node, 'aeskey')
+ const nestedRecordItem = getAnyXml(node, 'recorditem') || getDirectChildXml(node, 'recorditem') || getText(node, 'recorditem')
+
+ let content = datatitle || datadesc
+ if (!content) {
+ if (datatype === '4') content = '[视频]'
+ else if (datatype === '2' || datatype === '3') content = '[图片]'
+ else if (datatype === '47' || datatype === '37') content = '[表情]'
+ else if (datatype) content = `[消息 ${datatype}]`
+ else content = '[消息]'
+ }
+
+ const fmt = String(datafmt || '').trim().toLowerCase().replace(/^\./, '')
+ const imageFormats = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'heic', 'heif'])
+
+ let renderType = 'text'
+ if (datatype === '17') {
+ renderType = 'chatHistory'
+ } else if (datatype === '5' || link) {
+ renderType = 'link'
+ } else if (datatype === '4' || String(duration || '').trim() || fmt === 'mp4') {
+ renderType = 'video'
+ } else if (datatype === '47' || datatype === '37') {
+ renderType = 'emoji'
+ } else if (
+ datatype === '2'
+ || datatype === '3'
+ || imageFormats.has(fmt)
+ || (datatype !== '1' && isMaybeMd5(fullmd5))
+ ) {
+ renderType = 'image'
+ } else if (isMaybeMd5(md5) && /表情/.test(String(content || ''))) {
+ renderType = 'emoji'
+ }
+
+ let outTitle = ''
+ let outUrl = ''
+ let recordItem = ''
+ if (renderType === 'chatHistory') {
+ outTitle = datatitle || content || '聊天记录'
+ content = datadesc || ''
+ recordItem = nestedRecordItem
+ } else if (renderType === 'link') {
+ outTitle = datatitle || content || ''
+ outUrl = link || externurl || ''
+ const cleanDesc = stripWeChatInvisible(datadesc)
+ const cleanTitle = stripWeChatInvisible(outTitle)
+ if (!cleanDesc || (cleanTitle && cleanDesc === cleanTitle)) {
+ content = ''
+ } else {
+ content = String(datadesc || '').trim()
+ }
+ }
+
+ return {
+ id: dataid,
+ datatype,
+ sourcename,
+ sourcetime,
+ sourceheadurl,
+ datafmt,
+ duration,
+ fullmd5,
+ thumbfullmd5,
+ md5,
+ fromnewmsgid,
+ srcMsgLocalid,
+ srcMsgCreateTime,
+ cdnurlstring,
+ encrypturlstring,
+ externurl,
+ aeskey,
+ renderType,
+ title: outTitle,
+ recordItem,
+ url: outUrl,
+ content
+ }
+ })
+
+ return {
+ info: { isChatRoom, title, desc, count: datalistCount },
+ items: parsed
+ }
+}
+
+export const formatChatHistoryVideoDuration = (value) => {
+ const total = Math.max(0, parseInt(String(value || '').trim(), 10) || 0)
+ const minutes = Math.floor(total / 60)
+ const seconds = total % 60
+ if (minutes <= 0) return `0:${String(seconds).padStart(2, '0')}`
+ return `${minutes}:${String(seconds).padStart(2, '0')}`
+}
+
+export const createChatHistoryRecordNormalizer = ({ apiBase, getSelectedAccount, getSelectedContact }) => {
+ return (record) => {
+ const account = encodeURIComponent(String(getSelectedAccount?.() || '').trim())
+ const username = encodeURIComponent(String(getSelectedContact?.()?.username || '').trim())
+ const output = { ...(record || {}) }
+
+ output.senderDisplayName = String(output.sourcename || '').trim()
+ output.senderAvatar = normalizeChatHistoryUrl(output.sourceheadurl)
+ output.fullTime = String(output.sourcetime || '').trim()
+
+ if (output.renderType === 'link') {
+ const linkUrl = String(output.url || output.externurl || '').trim()
+ output.url = linkUrl
+ output.from = String(output.from || '').trim()
+ const previewCandidates = []
+ const fileId = (() => {
+ const localId = parseInt(String(output.srcMsgLocalid || '').trim(), 10) || 0
+ const createTime = parseInt(String(output.srcMsgCreateTime || '').trim(), 10) || 0
+ if (localId > 0 && createTime > 0) return `${localId}_${createTime}`
+ return ''
+ })()
+ if (fileId) {
+ previewCandidates.push(
+ `${apiBase}/chat/media/image?account=${account}&file_id=${encodeURIComponent(fileId)}&username=${username}`
+ )
+ }
+
+ output.previewMd5 = pickFirstMd5(output.fullmd5, output.thumbfullmd5, output.md5)
+ const srcServerId = String(output.fromnewmsgid || '').trim()
+ if (output.previewMd5) {
+ const previewParts = [
+ `account=${account}`,
+ `md5=${encodeURIComponent(output.previewMd5)}`,
+ srcServerId ? `server_id=${encodeURIComponent(srcServerId)}` : '',
+ `username=${username}`
+ ].filter(Boolean)
+ previewCandidates.push(`${apiBase}/chat/media/image?${previewParts.join('&')}`)
+ }
+
+ output._linkPreviewCandidates = previewCandidates
+ output._linkPreviewCandidateIndex = 0
+ output._linkPreviewError = false
+ output.preview = previewCandidates[0] || ''
+
+ const fromUsername = String(output.fromUsername || '').trim()
+ output.fromUsername = fromUsername
+ output.fromAvatar = fromUsername
+ ? `${apiBase}/chat/avatar?account=${account}&username=${encodeURIComponent(fromUsername)}`
+ : (linkUrl ? `${apiBase}/chat/media/favicon?url=${encodeURIComponent(linkUrl)}` : '')
+ output._fromAvatarLast = output.fromAvatar
+ output._fromAvatarImgOk = false
+ output._fromAvatarImgError = false
+ } else if (output.renderType === 'video') {
+ output.videoMd5 = pickFirstMd5(output.fullmd5, output.md5)
+ output.videoThumbMd5 = pickFirstMd5(output.thumbfullmd5)
+ output.videoDuration = String(output.duration || '').trim()
+ const thumbCandidates = []
+ if (output.videoMd5) {
+ thumbCandidates.push(`${apiBase}/chat/media/video_thumb?account=${account}&md5=${encodeURIComponent(output.videoMd5)}&username=${username}`)
+ }
+ if (output.videoThumbMd5 && output.videoThumbMd5 !== output.videoMd5) {
+ thumbCandidates.push(`${apiBase}/chat/media/video_thumb?account=${account}&md5=${encodeURIComponent(output.videoThumbMd5)}&username=${username}`)
+ }
+ output._videoThumbCandidates = thumbCandidates
+ output._videoThumbCandidateIndex = 0
+ output._videoThumbError = false
+ output.videoThumbUrl = thumbCandidates[0] || ''
+ output.videoUrl = output.videoMd5
+ ? `${apiBase}/chat/media/video?account=${account}&md5=${encodeURIComponent(output.videoMd5)}&username=${username}`
+ : ''
+ if (!output.content || /^\[.+\]$/.test(String(output.content || '').trim())) output.content = '[视频]'
+ } else if (output.renderType === 'emoji') {
+ output.emojiMd5 = pickFirstMd5(output.md5, output.fullmd5, output.thumbfullmd5)
+ const remoteEmojiUrl = String(output.cdnurlstring || output.externurl || output.encrypturlstring || '').trim()
+ const remoteAesKey = String(output.aeskey || '').trim()
+ output.emojiRemoteUrl = remoteEmojiUrl
+ output.emojiUrl = output.emojiMd5
+ ? `${apiBase}/chat/media/emoji?account=${account}&md5=${encodeURIComponent(output.emojiMd5)}&username=${username}${remoteEmojiUrl ? `&emoji_url=${encodeURIComponent(remoteEmojiUrl)}` : ''}${remoteAesKey ? `&aes_key=${encodeURIComponent(remoteAesKey)}` : ''}`
+ : ''
+ if (!output.content || /^\[.+\]$/.test(String(output.content || '').trim())) output.content = '[表情]'
+ } else if (output.renderType === 'image') {
+ output.imageMd5 = pickFirstMd5(output.fullmd5, output.thumbfullmd5, output.md5)
+ const srcServerId = String(output.fromnewmsgid || '').trim()
+ const imageParts = [
+ `account=${account}`,
+ output.imageMd5 ? `md5=${encodeURIComponent(output.imageMd5)}` : '',
+ srcServerId ? `server_id=${encodeURIComponent(srcServerId)}` : '',
+ `username=${username}`
+ ].filter(Boolean)
+ output.imageUrl = imageParts.length ? `${apiBase}/chat/media/image?${imageParts.join('&')}` : ''
+ if (!output.content || /^\[.+\]$/.test(String(output.content || '').trim())) output.content = '[图片]'
+ }
+
+ return output
+ }
+}
+
+export const enhanceChatHistoryRecords = (records) => {
+ const list = Array.isArray(records) ? records : []
+ const videoByThumbMd5 = new Map()
+ const videoByMd5 = new Map()
+ const imageByMd5 = new Map()
+ const emojiByMd5 = new Map()
+
+ for (const record of list) {
+ if (!record) continue
+ if (record.renderType === 'video' && record.videoThumbMd5) {
+ videoByThumbMd5.set(String(record.videoThumbMd5).toLowerCase(), record)
+ }
+ if (record.renderType === 'video' && record.videoMd5) {
+ videoByMd5.set(String(record.videoMd5).toLowerCase(), record)
+ }
+ if (record.renderType === 'image') {
+ const keys = [
+ pickFirstMd5(record.imageMd5),
+ pickFirstMd5(record.fullmd5),
+ pickFirstMd5(record.thumbfullmd5)
+ ].filter(Boolean)
+ for (const key of keys) imageByMd5.set(key, record)
+ }
+ if (record.renderType === 'emoji') {
+ const keys = [
+ pickFirstMd5(record.emojiMd5),
+ pickFirstMd5(record.md5),
+ pickFirstMd5(record.fullmd5),
+ pickFirstMd5(record.thumbfullmd5)
+ ].filter(Boolean)
+ for (const key of keys) emojiByMd5.set(key, record)
+ }
+ }
+
+ for (const record of list) {
+ if (!record || String(record.renderType || '') !== 'text') continue
+
+ const refKey = pickFirstMd5(record.thumbfullmd5) || pickFirstMd5(record.fullmd5)
+ if (!refKey) continue
+
+ const video = videoByThumbMd5.get(refKey) || videoByMd5.get(refKey)
+ if (video) {
+ const quoteThumbCandidates = Array.isArray(video._videoThumbCandidates) ? video._videoThumbCandidates.slice() : []
+ record._quoteThumbCandidates = quoteThumbCandidates
+ record._quoteThumbCandidateIndex = 0
+ record._quoteThumbError = false
+ const quoteThumbUrl = quoteThumbCandidates[0] || video.videoThumbUrl || ''
+ record.renderType = 'quote'
+ record.quote = {
+ kind: 'video',
+ thumbUrl: quoteThumbUrl,
+ url: video.videoUrl || '',
+ duration: video.videoDuration || '',
+ label: video.content || '[视频]',
+ targetId: video.id || ''
+ }
+ record.quoteMedia = {
+ videoMd5: video.videoMd5,
+ videoThumbMd5: video.videoThumbMd5,
+ videoUrl: video.videoUrl,
+ videoThumbUrl: quoteThumbUrl
+ }
+ continue
+ }
+
+ const image = imageByMd5.get(refKey)
+ if (image) {
+ record.renderType = 'quote'
+ record.quote = {
+ kind: 'image',
+ thumbUrl: image.imageUrl || '',
+ url: image.imageUrl || '',
+ label: image.content || '[图片]',
+ targetId: image.id || ''
+ }
+ record.quoteMedia = {
+ imageMd5: image.imageMd5,
+ imageUrl: image.imageUrl
+ }
+ continue
+ }
+
+ const emoji = emojiByMd5.get(refKey)
+ if (emoji) {
+ record.renderType = 'quote'
+ record.quote = {
+ kind: 'emoji',
+ thumbUrl: emoji.emojiUrl || '',
+ url: emoji.emojiUrl || '',
+ label: emoji.content || '[表情]',
+ targetId: emoji.id || ''
+ }
+ record.quoteMedia = {
+ emojiMd5: emoji.emojiMd5,
+ emojiUrl: emoji.emojiUrl
+ }
+ }
+ }
+
+ return list
+}
+
+export const isChatHistoryRecordItemIncomplete = (recordItemXml) => {
+ const recordItem = String(recordItemXml || '').trim()
+ if (!recordItem) return true
+ try {
+ const parsed = parseChatHistoryRecord(recordItem)
+ const got = Array.isArray(parsed?.items) ? parsed.items.length : 0
+ const expected = Math.max(0, parseInt(String(parsed?.info?.count || '0'), 10) || 0)
+ if (expected > 0 && got < expected) return true
+ if (got <= 0) return true
+ } catch {
+ return true
+ }
+ return false
+}
+
+export const buildChatHistoryWindowPayload = (payload, normalizeRecordItem) => {
+ const title0 = String(payload?.title || '聊天记录')
+ const content0 = String(payload?.content || '')
+ const recordItem0 = String(payload?.recordItem || '').trim()
+ const parsed = parseChatHistoryRecord(recordItem0)
+ const info0 = parsed?.info || { isChatRoom: false, count: 0 }
+ const items = Array.isArray(parsed?.items) ? parsed.items : []
+ let records0 = items.length ? enhanceChatHistoryRecords(items.map(normalizeRecordItem)) : []
+ if (!records0.length) {
+ const lines = content0.trim().split(/\r?\n/).map((item) => item.trim()).filter(Boolean)
+ records0 = lines.map((line, idx) => normalizeRecordItem({
+ id: String(idx),
+ datatype: '1',
+ sourcename: '',
+ sourcetime: '',
+ content: line,
+ renderType: 'text'
+ }))
+ }
+ return { title0, content0, recordItem0, info0, records0 }
+}
+
+export { getChatHistoryPreviewLines }
diff --git a/frontend/lib/chat/file-icons.js b/frontend/lib/chat/file-icons.js
new file mode 100644
index 0000000..090fc82
--- /dev/null
+++ b/frontend/lib/chat/file-icons.js
@@ -0,0 +1,50 @@
+import zipIconUrl from '~/assets/images/wechat/zip.png'
+import pdfIconUrl from '~/assets/images/wechat/pdf.png'
+import wordIconUrl from '~/assets/images/wechat/word.png'
+import excelIconUrl from '~/assets/images/wechat/excel.png'
+
+export const getFileIconKind = (fileName) => {
+ if (!fileName) return 'default'
+ const ext = String(fileName).split('.').pop()?.toLowerCase() || ''
+ switch (ext) {
+ case 'pdf':
+ return 'pdf'
+ case 'zip':
+ case 'rar':
+ case '7z':
+ case 'tar':
+ case 'gz':
+ return 'zip'
+ case 'doc':
+ case 'docx':
+ return 'doc'
+ case 'xls':
+ case 'xlsx':
+ case 'csv':
+ return 'xls'
+ case 'ppt':
+ case 'pptx':
+ return 'ppt'
+ case 'txt':
+ case 'md':
+ case 'log':
+ return 'txt'
+ default:
+ return 'default'
+ }
+}
+
+export const getFileIconUrl = (fileName) => {
+ switch (getFileIconKind(fileName)) {
+ case 'pdf':
+ return pdfIconUrl
+ case 'doc':
+ return wordIconUrl
+ case 'xls':
+ return excelIconUrl
+ case 'zip':
+ return zipIconUrl
+ default:
+ return ''
+ }
+}
diff --git a/frontend/lib/chat/formatters.js b/frontend/lib/chat/formatters.js
new file mode 100644
index 0000000..1d85f3d
--- /dev/null
+++ b/frontend/lib/chat/formatters.js
@@ -0,0 +1,211 @@
+export const normalizeSessionPreview = (value) => {
+ const text = String(value || '').trim()
+ if (/^\[location\]/i.test(text)) return text.replace(/^\[location\]/i, '[位置]')
+ if (/:\s*\[location\]$/i.test(text)) return text.replace(/\[location\]$/i, '[位置]')
+ return text
+}
+
+export const formatSmartTime = (ts) => {
+ if (!ts) return ''
+ try {
+ const date = new Date(Number(ts) * 1000)
+ const now = new Date()
+ const hh = String(date.getHours()).padStart(2, '0')
+ const mm = String(date.getMinutes()).padStart(2, '0')
+ const timeStr = `${hh}:${mm}`
+
+ const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate())
+ const targetStart = new Date(date.getFullYear(), date.getMonth(), date.getDate())
+ const dayDiff = Math.floor((todayStart - targetStart) / (1000 * 60 * 60 * 24))
+
+ if (dayDiff === 0) return timeStr
+ if (dayDiff === 1) return `昨天 ${timeStr}`
+ if (dayDiff >= 2 && dayDiff <= 6) {
+ const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
+ return `${weekdays[date.getDay()]} ${timeStr}`
+ }
+
+ const month = date.getMonth() + 1
+ const day = date.getDate()
+ if (date.getFullYear() === now.getFullYear()) {
+ return `${month}月${day}日 ${timeStr}`
+ }
+
+ return `${date.getFullYear()}年${month}月${day}日 ${timeStr}`
+ } catch {
+ return ''
+ }
+}
+
+export const formatTimeDivider = (ts) => formatSmartTime(ts)
+
+export const formatMessageTime = (ts) => {
+ if (!ts) return ''
+ try {
+ const date = new Date(Number(ts) * 1000)
+ const hh = String(date.getHours()).padStart(2, '0')
+ const mm = String(date.getMinutes()).padStart(2, '0')
+ return `${hh}:${mm}`
+ } catch {
+ return ''
+ }
+}
+
+export const formatMessageFullTime = (ts) => {
+ if (!ts) return ''
+ try {
+ const date = new Date(Number(ts) * 1000)
+ const yyyy = String(date.getFullYear())
+ const MM = String(date.getMonth() + 1).padStart(2, '0')
+ const dd = String(date.getDate()).padStart(2, '0')
+ const hh = String(date.getHours()).padStart(2, '0')
+ const mm = String(date.getMinutes()).padStart(2, '0')
+ const ss = String(date.getSeconds()).padStart(2, '0')
+ return `${yyyy}-${MM}-${dd} ${hh}:${mm}:${ss}`
+ } catch {
+ return ''
+ }
+}
+
+export const formatFileSize = (size) => {
+ if (!size) return ''
+ const text = String(size).trim()
+ const value = parseFloat(text)
+ if (Number.isNaN(value)) return text
+ if (value < 1024) return `${value} B`
+ if (value < 1024 * 1024) return `${(value / 1024).toFixed(2)} KB`
+ return `${(value / 1024 / 1024).toFixed(2)} MB`
+}
+
+export const formatTransferAmount = (amount) => {
+ const text = String(amount ?? '').trim()
+ if (!text) return ''
+ return text.replace(/[¥¥]/g, '').trim()
+}
+
+export const getRedPacketText = (message) => {
+ const text = String(message?.content ?? '').trim()
+ if (!text || text === '[Red Packet]') return '恭喜发财,大吉大利'
+ return text
+}
+
+export const isTransferReturned = (message) => {
+ const paySubType = String(message?.paySubType || '').trim()
+ if (paySubType === '4' || paySubType === '9') return true
+ const status = String(message?.transferStatus || '').trim()
+ const content = String(message?.content || '').trim()
+ const text = `${status} ${content}`.trim()
+ if (!text) return false
+ return text.includes('退回') || text.includes('退还')
+}
+
+export const isTransferOverdue = (message) => {
+ const paySubType = String(message?.paySubType || '').trim()
+ if (paySubType === '10') return true
+ const status = String(message?.transferStatus || '').trim()
+ const content = String(message?.content || '').trim()
+ const text = `${status} ${content}`.trim()
+ if (!text) return false
+ return text.includes('过期')
+}
+
+export const getTransferTitle = (message) => {
+ const paySubType = String(message?.paySubType || '').trim()
+ if (message?.transferStatus) return message.transferStatus
+ switch (paySubType) {
+ case '1':
+ return '转账'
+ case '3':
+ return message?.isSent ? '已被接收' : '已收款'
+ case '8':
+ return '发起转账'
+ case '4':
+ return '已退还'
+ case '9':
+ return '已被退还'
+ case '10':
+ return '已过期'
+ default:
+ break
+ }
+ if (message?.content && message.content !== '转账' && message.content !== '[转账]') {
+ return message.content
+ }
+ return '转账'
+}
+
+export const formatCount = (count) => {
+ const value = Number(count || 0)
+ if (!Number.isFinite(value) || value <= 0) return ''
+ try {
+ return value.toLocaleString()
+ } catch {
+ return String(value)
+ }
+}
+
+export const escapeHtml = (value) => {
+ if (!value) return ''
+ return String(value)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+}
+
+export const highlightKeyword = (text, keyword) => {
+ if (!text || !keyword) return escapeHtml(text || '')
+ const escaped = escapeHtml(text)
+ const kw = String(keyword || '').trim()
+ if (!kw) return escaped
+ try {
+ const escapedKw = kw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ const regex = new RegExp(`(${escapedKw})`, 'gi')
+ return escaped.replace(regex, '
$1 ')
+ } catch {
+ return escaped
+ }
+}
+
+export const getVoiceDurationInSeconds = (durationMs) => {
+ const value = Number(durationMs || 0)
+ if (!Number.isFinite(value) || value <= 0) return 0
+ return Math.max(1, Math.round(value / 1000))
+}
+
+export const getVoiceWidth = (durationMs) => {
+ const seconds = getVoiceDurationInSeconds(durationMs)
+ const clamped = Math.min(60, Math.max(1, seconds))
+ return `${80 + clamped * 4}px`
+}
+
+export const toUnixSeconds = (datetimeLocal) => {
+ const value = String(datetimeLocal || '').trim()
+ if (!value) return null
+ const date = new Date(value)
+ const ms = date.getTime()
+ if (Number.isNaN(ms)) return null
+ return Math.floor(ms / 1000)
+}
+
+export const dateToUnixSeconds = (dateStr, endOfDay = false) => {
+ const value = String(dateStr || '').trim()
+ if (!value) return null
+ const matched = value.match(/^(\d{4})-(\d{2})-(\d{2})$/)
+ if (!matched) return null
+ const year = Number(matched[1])
+ const month = Number(matched[2])
+ const day = Number(matched[3])
+ if (!Number.isFinite(year) || !Number.isFinite(month) || !Number.isFinite(day)) return null
+ const date = new Date(year, month - 1, day, endOfDay ? 23 : 0, endOfDay ? 59 : 0, endOfDay ? 59 : 0)
+ const ms = date.getTime()
+ if (Number.isNaN(ms)) return null
+ return Math.floor(ms / 1000)
+}
+
+export const getChatHistoryPreviewLines = (message) => {
+ const raw = String(message?.content || '').trim()
+ if (!raw) return []
+ return raw.split(/\r?\n/).map((item) => item.trim()).filter(Boolean).slice(0, 4)
+}
diff --git a/frontend/lib/chat/message-normalizer.js b/frontend/lib/chat/message-normalizer.js
new file mode 100644
index 0000000..aef313f
--- /dev/null
+++ b/frontend/lib/chat/message-normalizer.js
@@ -0,0 +1,270 @@
+import { formatMessageFullTime, formatMessageTime } from '~/lib/chat/formatters'
+
+const normalizeMaybeUrl = (value) => (typeof value === 'string' ? value.trim() : '')
+
+const isUsableMediaUrl = (value) => {
+ const text = normalizeMaybeUrl(value)
+ if (!text) return false
+ return (
+ /^https?:\/\//i.test(text)
+ || /^blob:/i.test(text)
+ || /^data:/i.test(text)
+ || /^\/api\/chat\/media\//i.test(text)
+ )
+}
+
+const buildAccountMediaUrl = (apiBase, path, parts) => {
+ return `${apiBase}${path}?${parts.filter(Boolean).join('&')}`
+}
+
+export const createMessageNormalizer = ({ apiBase, getSelectedAccount, getSelectedContact, getLocalMediaVersion }) => {
+ return (msg) => {
+ const account = String(getSelectedAccount?.() || '').trim()
+ const contact = getSelectedContact?.() || null
+ const username = String(contact?.username || '').trim()
+ const localMediaVersion = Number(getLocalMediaVersion?.() || 0)
+ const isSent = !!msg.isSent
+ const sender = isSent ? '我' : (msg.senderDisplayName || msg.senderUsername || contact?.name || '')
+ const fallbackAvatar = (!isSent && !contact?.isGroup) ? (contact?.avatar || null) : null
+
+ const normalizedThumbUrl = (() => {
+ const candidates = [msg.thumbUrl, msg.preview]
+ for (const candidate of candidates) {
+ if (isUsableMediaUrl(candidate)) return normalizeMaybeUrl(candidate)
+ }
+ return ''
+ })()
+
+ const normalizedLinkPreviewUrl = (() => {
+ const url = normalizedThumbUrl
+ if (!url) return ''
+ if (/^\/api\/chat\/media\//i.test(url) || /^blob:/i.test(url) || /^data:/i.test(url)) return url
+ if (!/^https?:\/\//i.test(url)) return url
+ try {
+ const host = new URL(url).hostname.toLowerCase()
+ if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
+ return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(url)}`
+ }
+ } catch {}
+ return url
+ })()
+
+ const fromUsername = String(msg.fromUsername || '').trim()
+ const fromAvatar = fromUsername
+ ? `${apiBase}/chat/avatar?account=${encodeURIComponent(account)}&username=${encodeURIComponent(fromUsername)}`
+ : (() => {
+ const href = String(msg.url || '').trim()
+ return href ? `${apiBase}/chat/media/favicon?url=${encodeURIComponent(href)}` : ''
+ })()
+
+ const localEmojiUrl = msg.emojiMd5
+ ? `${apiBase}/chat/media/emoji?account=${encodeURIComponent(account)}&md5=${encodeURIComponent(msg.emojiMd5)}&username=${encodeURIComponent(username)}`
+ : ''
+
+ const localImageUrl = (() => {
+ if (!msg.imageMd5 && !msg.imageFileId) return ''
+ return buildAccountMediaUrl(apiBase, '/chat/media/image', [
+ `account=${encodeURIComponent(account)}`,
+ msg.imageMd5 ? `md5=${encodeURIComponent(msg.imageMd5)}` : '',
+ msg.imageFileId ? `file_id=${encodeURIComponent(msg.imageFileId)}` : '',
+ `username=${encodeURIComponent(username)}`,
+ localMediaVersion > 0 ? `v=${encodeURIComponent(String(localMediaVersion))}` : ''
+ ])
+ })()
+
+ const normalizedImageUrl = (() => {
+ const current = isUsableMediaUrl(msg.imageUrl) ? normalizeMaybeUrl(msg.imageUrl) : ''
+ if (current && /\/api\/chat\/media\/image\b/i.test(current) && localImageUrl) {
+ return localImageUrl
+ }
+ return current || localImageUrl || ''
+ })()
+
+ const normalizedEmojiUrl = msg.emojiUrl || localEmojiUrl
+
+ const localVideoThumbUrl = (() => {
+ if (!msg.videoThumbMd5 && !msg.videoThumbFileId) return ''
+ return buildAccountMediaUrl(apiBase, '/chat/media/video_thumb', [
+ `account=${encodeURIComponent(account)}`,
+ msg.videoThumbMd5 ? `md5=${encodeURIComponent(msg.videoThumbMd5)}` : '',
+ msg.videoThumbFileId ? `file_id=${encodeURIComponent(msg.videoThumbFileId)}` : '',
+ `username=${encodeURIComponent(username)}`,
+ localMediaVersion > 0 ? `v=${encodeURIComponent(String(localMediaVersion))}` : ''
+ ])
+ })()
+
+ const localVideoUrl = (() => {
+ if (!msg.videoMd5 && !msg.videoFileId) return ''
+ return buildAccountMediaUrl(apiBase, '/chat/media/video', [
+ `account=${encodeURIComponent(account)}`,
+ msg.videoMd5 ? `md5=${encodeURIComponent(msg.videoMd5)}` : '',
+ msg.videoFileId ? `file_id=${encodeURIComponent(msg.videoFileId)}` : '',
+ `username=${encodeURIComponent(username)}`
+ ])
+ })()
+
+ const normalizedVideoThumbUrl = (isUsableMediaUrl(msg.videoThumbUrl) ? normalizeMaybeUrl(msg.videoThumbUrl) : '') || localVideoThumbUrl
+ const normalizedVideoUrl = (isUsableMediaUrl(msg.videoUrl) ? normalizeMaybeUrl(msg.videoUrl) : '') || localVideoUrl
+ const serverIdStr = String(msg.serverIdStr || (msg.serverId != null ? String(msg.serverId) : '')).trim()
+ const normalizedVoiceUrl = (() => {
+ if (msg.voiceUrl) return msg.voiceUrl
+ if (!serverIdStr) return ''
+ if (String(msg.renderType || '') !== 'voice') return ''
+ return `${apiBase}/chat/media/voice?account=${encodeURIComponent(account)}&server_id=${encodeURIComponent(serverIdStr)}`
+ })()
+
+ const remoteFromServer = (
+ typeof msg.emojiRemoteUrl === 'string'
+ && /^https?:\/\//i.test(msg.emojiRemoteUrl)
+ && !/\/api\/chat\/media\/emoji\b/i.test(msg.emojiRemoteUrl)
+ && !/\blocalhost\b/i.test(msg.emojiRemoteUrl)
+ && !/\b127\.0\.0\.1\b/i.test(msg.emojiRemoteUrl)
+ ) ? msg.emojiRemoteUrl : ''
+
+ const remoteFromEmojiUrl = (
+ typeof msg.emojiUrl === 'string'
+ && /^https?:\/\//i.test(msg.emojiUrl)
+ && !/\/api\/chat\/media\/emoji\b/i.test(msg.emojiUrl)
+ && !/\blocalhost\b/i.test(msg.emojiUrl)
+ && !/\b127\.0\.0\.1\b/i.test(msg.emojiUrl)
+ ) ? msg.emojiUrl : ''
+
+ const emojiRemoteUrl = remoteFromServer || remoteFromEmojiUrl
+ const emojiIsLocal = typeof normalizedEmojiUrl === 'string' && /\/api\/chat\/media\/emoji\b/i.test(normalizedEmojiUrl)
+ const emojiDownloaded = !!emojiRemoteUrl && !!emojiIsLocal
+
+ const replyText = String(msg.content || '').trim()
+ let quoteContent = String(msg.quoteContent || '')
+ const trimmedQuoteContent = quoteContent.trim()
+ if (replyText && trimmedQuoteContent) {
+ if (trimmedQuoteContent === replyText) {
+ quoteContent = ''
+ } else {
+ const lines = trimmedQuoteContent.split(/\r?\n/).map((item) => item.trim())
+ if (lines.length && (lines[0] === replyText || lines[0] === replyText.split(/\r?\n/)[0]?.trim())) {
+ quoteContent = trimmedQuoteContent.split(/\r?\n/).slice(1).join('\n').trim()
+ } else if (trimmedQuoteContent.startsWith(replyText)) {
+ quoteContent = trimmedQuoteContent.slice(replyText.length).trim()
+ }
+ }
+ }
+
+ const quoteServerIdStr = String(msg.quoteServerId || '').trim()
+ const quoteTypeStr = String(msg.quoteType || '').trim()
+ const quoteVoiceUrl = quoteServerIdStr
+ ? `${apiBase}/chat/media/voice?account=${encodeURIComponent(account)}&server_id=${encodeURIComponent(quoteServerIdStr)}`
+ : ''
+
+ const quoteImageUrl = (() => {
+ if (!quoteServerIdStr) return ''
+ if (quoteTypeStr !== '3' && String(msg.quoteContent || '').trim() !== '[图片]') return ''
+ return buildAccountMediaUrl(apiBase, '/chat/media/image', [
+ `account=${encodeURIComponent(account)}`,
+ `server_id=${encodeURIComponent(quoteServerIdStr)}`,
+ username ? `username=${encodeURIComponent(username)}` : '',
+ localMediaVersion > 0 ? `v=${encodeURIComponent(String(localMediaVersion))}` : ''
+ ])
+ })()
+
+ const quoteThumbUrl = (() => {
+ const raw = isUsableMediaUrl(msg.quoteThumbUrl) ? normalizeMaybeUrl(msg.quoteThumbUrl) : ''
+ if (!raw) return ''
+ if (/^\/api\/chat\/media\//i.test(raw) || /^blob:/i.test(raw) || /^data:/i.test(raw)) return raw
+ if (!/^https?:\/\//i.test(raw)) return raw
+ try {
+ const host = new URL(raw).hostname.toLowerCase()
+ if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
+ return `${apiBase}/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
+ }
+ } catch {}
+ return raw
+ })()
+
+ return {
+ id: msg.id,
+ localId: Number(msg.localId || 0),
+ serverId: msg.serverId || 0,
+ serverIdStr,
+ type: Number(msg.type || 0),
+ sender,
+ senderUsername: msg.senderUsername || '',
+ senderDisplayName: msg.senderDisplayName || '',
+ content: msg.content || '',
+ time: formatMessageTime(msg.createTime),
+ fullTime: formatMessageFullTime(msg.createTime),
+ createTime: Number(msg.createTime || 0),
+ isSent,
+ renderType: msg.renderType || 'text',
+ voipType: msg.voipType || '',
+ title: msg.title || '',
+ url: msg.url || '',
+ recordItem: msg.recordItem || '',
+ imageMd5: msg.imageMd5 || '',
+ imageFileId: msg.imageFileId || '',
+ emojiMd5: msg.emojiMd5 || '',
+ emojiUrl: normalizedEmojiUrl || '',
+ emojiLocalUrl: localEmojiUrl || '',
+ emojiRemoteUrl,
+ _emojiDownloaded: !!emojiDownloaded,
+ thumbUrl: msg.thumbUrl || '',
+ imageUrl: normalizedImageUrl || '',
+ videoMd5: msg.videoMd5 || '',
+ videoThumbMd5: msg.videoThumbMd5 || '',
+ videoFileId: msg.videoFileId || '',
+ videoThumbFileId: msg.videoThumbFileId || '',
+ videoThumbUrl: normalizedVideoThumbUrl || '',
+ videoUrl: normalizedVideoUrl || '',
+ quoteTitle: msg.quoteTitle || '',
+ quoteContent,
+ quoteUsername: msg.quoteUsername || '',
+ quoteServerId: quoteServerIdStr,
+ quoteType: quoteTypeStr,
+ quoteVoiceLength: msg.quoteVoiceLength || '',
+ quoteVoiceUrl,
+ quoteImageUrl: quoteImageUrl || '',
+ quoteThumbUrl: quoteThumbUrl || '',
+ _quoteImageError: false,
+ _quoteThumbError: false,
+ amount: msg.amount || '',
+ coverUrl: msg.coverUrl || '',
+ fileSize: msg.fileSize || '',
+ fileMd5: msg.fileMd5 || '',
+ paySubType: msg.paySubType || '',
+ transferStatus: msg.transferStatus || '',
+ transferReceived: msg.paySubType === '3' || msg.transferStatus === '已收款' || msg.transferStatus === '已被接收',
+ voiceUrl: normalizedVoiceUrl || '',
+ voiceDuration: msg.voiceLength || msg.voiceDuration || '',
+ locationLat: msg.locationLat ?? null,
+ locationLng: msg.locationLng ?? null,
+ locationPoiname: String(msg.locationPoiname || '').trim(),
+ locationLabel: String(msg.locationLabel || '').trim(),
+ preview: normalizedLinkPreviewUrl || '',
+ linkType: String(msg.linkType || '').trim(),
+ linkStyle: String(msg.linkStyle || '').trim(),
+ linkCardVariant: String(msg.linkStyle || '').trim() === 'cover' ? 'cover' : 'default',
+ from: String(msg.from || '').trim(),
+ fromUsername,
+ fromAvatar,
+ isGroup: !!contact?.isGroup,
+ avatar: msg.senderAvatar || msg.avatar || fallbackAvatar || null,
+ avatarColor: null
+ }
+ }
+}
+
+export const dedupeMessagesById = (list) => {
+ const input = Array.isArray(list) ? list : []
+ const seen = new Set()
+ const output = []
+ for (const item of input) {
+ const id = String(item?.id || '')
+ if (!id) {
+ output.push(item)
+ continue
+ }
+ if (seen.has(id)) continue
+ seen.add(id)
+ output.push(item)
+ }
+ return output
+}
diff --git a/frontend/lib/desktop-settings.js b/frontend/lib/desktop-settings.js
new file mode 100644
index 0000000..81bf607
--- /dev/null
+++ b/frontend/lib/desktop-settings.js
@@ -0,0 +1,22 @@
+export const DESKTOP_SETTING_AUTO_REALTIME_KEY = 'desktop.settings.autoRealtime'
+export const DESKTOP_SETTING_DEFAULT_TO_CHAT_KEY = 'desktop.settings.defaultToChatWhenData'
+// 朋友圈图片:是否允许使用缓存(默认开启)。关闭后会尽量每次都走下载+解密流程。
+export const SNS_SETTING_USE_CACHE_KEY = 'sns.settings.useCache'
+
+export const readLocalBoolSetting = (key, fallback = false) => {
+ if (!process.client) return !!fallback
+ try {
+ const raw = localStorage.getItem(String(key || ''))
+ if (raw == null) return !!fallback
+ return String(raw).toLowerCase() === 'true'
+ } catch {
+ return !!fallback
+ }
+}
+
+export const writeLocalBoolSetting = (key, value) => {
+ if (!process.client) return
+ try {
+ localStorage.setItem(String(key || ''), value ? 'true' : 'false')
+ } catch {}
+}
diff --git a/frontend/lib/privacy-mode.js b/frontend/lib/privacy-mode.js
new file mode 100644
index 0000000..85c7ce7
--- /dev/null
+++ b/frontend/lib/privacy-mode.js
@@ -0,0 +1,20 @@
+export const PRIVACY_MODE_KEY = 'ui.privacy_mode'
+
+export const readPrivacyMode = (fallback = false) => {
+ if (!process.client) return !!fallback
+ try {
+ const raw = localStorage.getItem(PRIVACY_MODE_KEY)
+ if (raw == null) return !!fallback
+ const normalized = String(raw).trim().toLowerCase()
+ return normalized === '1' || normalized === 'true'
+ } catch {
+ return !!fallback
+ }
+}
+
+export const writePrivacyMode = (enabled) => {
+ if (!process.client) return
+ try {
+ localStorage.setItem(PRIVACY_MODE_KEY, enabled ? '1' : '0')
+ } catch {}
+}
diff --git a/frontend/lib/server-error-logging.js b/frontend/lib/server-error-logging.js
new file mode 100644
index 0000000..04c9276
--- /dev/null
+++ b/frontend/lib/server-error-logging.js
@@ -0,0 +1,206 @@
+import { useApiBase } from '~/composables/useApiBase'
+
+const FRONTEND_SERVER_ERROR_ENDPOINT = '/admin/log-frontend-server-error'
+
+const normalizeStatus = (value) => {
+ const n = Number(value)
+ if (!Number.isInteger(n)) return 0
+ return n
+}
+
+const stringifyDetail = (value) => {
+ if (value == null) return ''
+ if (typeof value === 'string') return value.trim()
+ try {
+ return JSON.stringify(value)
+ } catch {
+ return String(value).trim()
+ }
+}
+
+const currentOrigin = () => {
+ if (!process.client || typeof window === 'undefined') return ''
+ try {
+ return String(window.location?.origin || '').trim()
+ } catch {
+ return ''
+ }
+}
+
+const normalizeBasePath = (apiBase) => {
+ const raw = String(apiBase || '').trim()
+ if (!raw) return '/api'
+ if (/^https?:\/\//i.test(raw)) {
+ try {
+ const u = new URL(raw)
+ return u.pathname.replace(/\/+$/, '') || '/'
+ } catch {
+ return '/api'
+ }
+ }
+ return raw.replace(/\/+$/, '') || '/'
+}
+
+const normalizePathname = (value) => {
+ const raw = String(value || '').trim()
+ if (!raw) return ''
+ try {
+ return new URL(raw).pathname.replace(/\/+$/, '')
+ } catch {
+ return raw.split(/[?#]/, 1)[0].replace(/\/+$/, '')
+ }
+}
+
+export const isServerErrorStatus = (status) => normalizeStatus(status) >= 500
+
+export const resolveRequestUrl = (requestUrl, apiBase = '') => {
+ const raw = String(requestUrl || '').trim()
+ if (!raw) return ''
+ if (/^https?:\/\//i.test(raw)) return raw
+
+ const origin = currentOrigin()
+ if (!origin) return raw
+
+ if (raw.startsWith('/')) {
+ const prefix = normalizeBasePath(apiBase)
+ const combined = raw === prefix || raw.startsWith(`${prefix}/`) ? raw : `${prefix}${raw}`
+ if (/^https?:\/\//i.test(String(apiBase || '').trim())) {
+ try {
+ const baseUrl = new URL(String(apiBase).trim())
+ return new URL(combined, `${baseUrl.origin}/`).toString()
+ } catch {
+ return new URL(combined, origin).toString()
+ }
+ }
+ return new URL(combined, origin).toString()
+ }
+
+ if (/^https?:\/\//i.test(String(apiBase || '').trim())) {
+ try {
+ const base = String(apiBase).trim()
+ return new URL(raw, base.endsWith('/') ? base : `${base}/`).toString()
+ } catch {
+ return new URL(raw, origin).toString()
+ }
+ }
+
+ return new URL(raw, origin).toString()
+}
+
+const isFrontendServerLogUrl = (requestUrl) => {
+ const path = normalizePathname(requestUrl)
+ return path.endsWith('/api/admin/log-frontend-server-error') || path.endsWith('/admin/log-frontend-server-error')
+}
+
+const extractBackendDetail = (data) => {
+ if (data == null) return ''
+ if (typeof data === 'string') return data.trim()
+ if (typeof data === 'object' && !Array.isArray(data) && Object.prototype.hasOwnProperty.call(data, 'detail')) {
+ return stringifyDetail(data.detail)
+ }
+ return stringifyDetail(data)
+}
+
+const resolveApiBase = (apiBase) => {
+ const raw = String(apiBase || '').trim()
+ if (raw) return raw
+ if (!process.client) return ''
+ try {
+ return String(useApiBase() || '').trim()
+ } catch {
+ return ''
+ }
+}
+
+export const extractServerErrorFromError = (error) => {
+ const response = error?.response
+ return {
+ status: normalizeStatus(error?.status ?? response?.status),
+ backendDetail: extractBackendDetail(response?._data ?? response?.data ?? error?.data),
+ message: String(error?.message || '').trim(),
+ requestUrl: String(response?.url || error?.request || '').trim(),
+ }
+}
+
+export const extractServerErrorDetailFromResponse = async (response) => {
+ if (!response || typeof response.clone !== 'function') return ''
+ try {
+ const clone = response.clone()
+ const contentType = String(clone.headers?.get?.('content-type') || '').toLowerCase()
+ if (contentType.includes('json')) {
+ try {
+ const payload = await clone.json()
+ return extractBackendDetail(payload)
+ } catch {}
+ }
+ const text = String(await clone.text()).trim()
+ if (!text) return ''
+ if (contentType.includes('json')) {
+ try {
+ return extractBackendDetail(JSON.parse(text))
+ } catch {}
+ }
+ return text
+ } catch {
+ return ''
+ }
+}
+
+export const reportServerError = async (context = {}) => {
+ if (!process.client || typeof window === 'undefined') return false
+
+ const status = normalizeStatus(context.status)
+ if (!isServerErrorStatus(status)) return false
+
+ const apiBase = resolveApiBase(context.apiBase)
+ const requestUrl = resolveRequestUrl(context.requestUrl, apiBase)
+ if (!requestUrl || isFrontendServerLogUrl(requestUrl)) return false
+
+ const endpointUrl = resolveRequestUrl(FRONTEND_SERVER_ERROR_ENDPOINT, apiBase)
+ if (!endpointUrl) return false
+
+ const payload = {
+ status,
+ method: String(context.method || 'GET').trim().toUpperCase() || 'GET',
+ request_url: requestUrl,
+ message: String(context.message || '').trim(),
+ backend_detail: String(context.backendDetail || '').trim(),
+ source: String(context.source || '').trim(),
+ page_url: String(window.location?.href || '').trim(),
+ }
+
+ try {
+ await fetch(endpointUrl, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(payload),
+ keepalive: true,
+ })
+ return true
+ } catch {
+ return false
+ }
+}
+
+export const reportServerErrorFromError = async (error, context = {}) => {
+ const info = extractServerErrorFromError(error)
+ return await reportServerError({
+ ...context,
+ status: context.status ?? info.status,
+ requestUrl: context.requestUrl || info.requestUrl,
+ message: context.message || info.message,
+ backendDetail: context.backendDetail || info.backendDetail,
+ })
+}
+
+export const reportServerErrorFromResponse = async (response, context = {}) => {
+ const status = normalizeStatus(context.status ?? response?.status)
+ if (!isServerErrorStatus(status)) return false
+ const backendDetail = context.backendDetail || (await extractServerErrorDetailFromResponse(response))
+ return await reportServerError({
+ ...context,
+ status,
+ requestUrl: context.requestUrl || response?.url || '',
+ backendDetail,
+ })
+}
diff --git a/frontend/lib/ui-theme.js b/frontend/lib/ui-theme.js
new file mode 100644
index 0000000..5269553
--- /dev/null
+++ b/frontend/lib/ui-theme.js
@@ -0,0 +1,36 @@
+export const UI_THEME_KEY = 'ui.theme'
+export const UI_THEME_LIGHT = 'light'
+export const UI_THEME_DARK = 'dark'
+
+export const normalizeUiTheme = (value, fallback = UI_THEME_LIGHT) => {
+ const normalized = String(value || '').trim().toLowerCase()
+ if (normalized === UI_THEME_DARK) return UI_THEME_DARK
+ if (normalized === UI_THEME_LIGHT) return UI_THEME_LIGHT
+ return fallback === UI_THEME_DARK ? UI_THEME_DARK : UI_THEME_LIGHT
+}
+
+export const readUiTheme = (fallback = UI_THEME_LIGHT) => {
+ if (!process.client) return normalizeUiTheme(fallback)
+ try {
+ const raw = localStorage.getItem(UI_THEME_KEY)
+ return normalizeUiTheme(raw, fallback)
+ } catch {
+ return normalizeUiTheme(fallback)
+ }
+}
+
+export const writeUiTheme = (theme) => {
+ if (!process.client) return
+ try {
+ localStorage.setItem(UI_THEME_KEY, normalizeUiTheme(theme))
+ } catch {}
+}
+
+export const applyUiTheme = (theme) => {
+ if (!process.client || typeof document === 'undefined') return
+ const normalized = normalizeUiTheme(theme)
+ const root = document.documentElement
+ root.dataset.theme = normalized
+ root.classList.toggle('theme-dark', normalized === UI_THEME_DARK)
+ root.style.colorScheme = normalized === UI_THEME_DARK ? 'dark' : 'light'
+}
diff --git a/frontend/utils/wechat-emojis.ts b/frontend/lib/wechat-emojis.ts
similarity index 100%
rename from frontend/utils/wechat-emojis.ts
rename to frontend/lib/wechat-emojis.ts
diff --git a/frontend/lib/wrapped/heatmap.js b/frontend/lib/wrapped/heatmap.js
new file mode 100644
index 0000000..d7dedd0
--- /dev/null
+++ b/frontend/lib/wrapped/heatmap.js
@@ -0,0 +1,46 @@
+// Utilities for Wrapped heatmap rendering.
+
+export const clamp01 = (v) => {
+ const n = Number(v)
+ if (!Number.isFinite(n)) return 0
+ if (n < 0) return 0
+ if (n > 1) return 1
+ return n
+}
+
+export const maxInMatrix = (matrix) => {
+ if (!Array.isArray(matrix)) return 0
+ let m = 0
+ for (const row of matrix) {
+ if (!Array.isArray(row)) continue
+ for (const v of row) {
+ const n = Number(v)
+ if (Number.isFinite(n) && n > m) m = n
+ }
+ }
+ return m
+}
+
+// Color inspired by WeChat green, with a slight "gold" shift on high intensity
+// (EchoTrace-style accent) while keeping the overall WeChat vibe.
+export const heatColor = (value, max) => {
+ const v = Number(value) || 0
+ const m = Number(max) || 0
+ if (!(v > 0) || !(m > 0)) return 'rgba(0,0,0,0.05)'
+
+ // Use sqrt scaling to make low values still visible.
+ const t = clamp01(Math.sqrt(v / m))
+
+ // Hue from green (~145) -> yellow-green (~95)
+ const hue = 145 - 50 * t
+ const sat = 70
+ const light = 92 - 42 * t
+ return `hsl(${hue.toFixed(1)} ${sat}% ${light.toFixed(1)}%)`
+}
+
+export const formatHourRange = (hour) => {
+ const h = Number(hour)
+ if (!Number.isFinite(h)) return ''
+ const hh = String(h).padStart(2, '0')
+ return `${hh}:00-${hh}:59`
+}
diff --git a/frontend/lib/wrapped/types.js b/frontend/lib/wrapped/types.js
new file mode 100644
index 0000000..57083dd
--- /dev/null
+++ b/frontend/lib/wrapped/types.js
@@ -0,0 +1,45 @@
+// JSDoc types for the Wrapped API (kept in JS to match the current codebase).
+
+/**
+ * @typedef {Object} WrappedCardBase
+ * @property {number} id
+ * @property {string} title
+ * @property {'global'} scope
+ * @property {'A'|'B'|'C'|'D'|'E'} category
+ * @property {'ok'|'error'|'idle'|'loading'} status
+ * @property {string} kind
+ * @property {string} narrative
+ * @property {Record
} data
+ */
+
+/**
+ * @typedef {Object} WrappedCardManifest
+ * @property {number} id
+ * @property {string} title
+ * @property {'global'} scope
+ * @property {'A'|'B'|'C'|'D'|'E'} category
+ * @property {string} kind
+ */
+
+/**
+ * @typedef {Object} WrappedAnnualMetaResponse
+ * @property {string} account
+ * @property {number} year
+ * @property {'global'} scope
+ * @property {number[]|undefined} availableYears
+ * @property {WrappedCardManifest[]} cards
+ */
+
+/**
+ * @typedef {Object} WrappedAnnualResponse
+ * @property {string} account
+ * @property {number} year
+ * @property {'global'} scope
+ * @property {string|null} username
+ * @property {number} generated_at
+ * @property {boolean} cached
+ * @property {number[]|undefined} availableYears
+ * @property {WrappedCardBase[]} cards
+ */
+
+export {}
diff --git a/frontend/nuxt.config.ts b/frontend/nuxt.config.ts
index 3e6f203..7fe1eae 100644
--- a/frontend/nuxt.config.ts
+++ b/frontend/nuxt.config.ts
@@ -1,23 +1,50 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
+const frontendHost = String(process.env.NUXT_HOST || '').trim()
+const frontendPort = Number.parseInt(String(process.env.NUXT_PORT || process.env.PORT || '3000').trim(), 10)
+const backendPort = String(process.env.WECHAT_TOOL_PORT || '10392').trim() || '10392'
+const devProxyTarget = `http://127.0.0.1:${backendPort}/api`
+
export default defineNuxtConfig({
compatibilityDate: '2025-07-15',
devtools: { enabled: false },
+ experimental: {
+ // This app does not use Nuxt route rules on the client, so disabling
+ // the app manifest avoids an unnecessary `/_nuxt/builds/meta/dev.json`
+ // preload request and the related Chrome warning in dev mode.
+ appManifest: false,
+ },
+
+ runtimeConfig: {
+ public: {
+ // Full API base, including `/api` when needed.
+ // Example: `NUXT_PUBLIC_API_BASE=http://127.0.0.1:10392/api`
+ apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api',
+ },
+ },
// 配置前端开发服务器端口
devServer: {
- port: 3000
+ ...(frontendHost ? { host: frontendHost } : {}),
+ port: Number.isInteger(frontendPort) && frontendPort >= 1 && frontendPort <= 65535 ? frontendPort : 3000
},
// 配置API代理,解决跨域问题
nitro: {
devProxy: {
'/api': {
- target: 'http://localhost:8000',
+ // `h3` strips the matched prefix (`/api`) before calling the middleware,
+ // so the proxy target must include `/api` to preserve backend routes.
+ target: devProxyTarget,
changeOrigin: true
}
}
},
+ // 应用配置
+ css: [
+ '~/assets/css/chat.css'
+ ],
+
// 应用配置
app: {
head: {
@@ -28,10 +55,10 @@ export default defineNuxtConfig({
{ name: 'description', content: '微信4.x版本数据库解密工具' }
],
link: [
- { rel: 'icon', type: 'image/png', href: '/logo.png' }
+ { rel: 'icon', type: 'image/png', href: '/logo.png' },
+ { rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css' }
]
- },
- pageTransition: { name: 'page', mode: 'out-in' }
+ }
},
// 模块配置
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 2139597..ee60af1 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -9,10 +9,16 @@
"dependencies": {
"@nuxtjs/tailwindcss": "^6.14.0",
"@pinia/nuxt": "^0.11.2",
+ "@vueuse/motion": "^3.0.3",
"axios": "^1.11.0",
+ "gsap": "^3.14.2",
"nuxt": "^4.0.1",
+ "ogl": "^1.0.11",
"vue": "^3.5.17",
"vue-router": "^4.5.1"
+ },
+ "devDependencies": {
+ "tailwindcss": "3.4.17"
}
},
"node_modules/@alloc/quick-lru": {
@@ -27,26 +33,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@ampproject/remapping": {
- "version": "2.3.0",
- "resolved": "https://registry.npmmirror.com/@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/@babel/code-frame": {
- "version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz",
- "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
"license": "MIT",
"dependencies": {
- "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
@@ -55,30 +48,30 @@
}
},
"node_modules/@babel/compat-data": {
- "version": "7.28.0",
- "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.0.tgz",
- "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
+ "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
- "version": "7.28.0",
- "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.28.0.tgz",
- "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==",
- "license": "MIT",
- "dependencies": {
- "@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.0",
- "@babel/helper-compilation-targets": "^7.27.2",
- "@babel/helper-module-transforms": "^7.27.3",
- "@babel/helpers": "^7.27.6",
- "@babel/parser": "^7.28.0",
- "@babel/template": "^7.27.2",
- "@babel/traverse": "^7.28.0",
- "@babel/types": "^7.28.0",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -95,7 +88,7 @@
},
"node_modules/@babel/core/node_modules/semver": {
"version": "6.3.1",
- "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"license": "ISC",
"bin": {
@@ -103,13 +96,13 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.28.0",
- "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.0.tgz",
- "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==",
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.0",
- "@babel/types": "^7.28.0",
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
@@ -120,7 +113,7 @@
},
"node_modules/@babel/helper-annotate-as-pure": {
"version": "7.27.3",
- "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
"integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
"license": "MIT",
"dependencies": {
@@ -131,12 +124,12 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.27.2",
- "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
- "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
"license": "MIT",
"dependencies": {
- "@babel/compat-data": "^7.27.2",
+ "@babel/compat-data": "^7.28.6",
"@babel/helper-validator-option": "^7.27.1",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
@@ -148,7 +141,7 @@
},
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
"version": "6.3.1",
- "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"license": "ISC",
"bin": {
@@ -156,17 +149,17 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz",
- "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz",
+ "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==",
"license": "MIT",
"dependencies": {
- "@babel/helper-annotate-as-pure": "^7.27.1",
- "@babel/helper-member-expression-to-functions": "^7.27.1",
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
"@babel/helper-optimise-call-expression": "^7.27.1",
- "@babel/helper-replace-supers": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.28.6",
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
- "@babel/traverse": "^7.27.1",
+ "@babel/traverse": "^7.28.6",
"semver": "^6.3.1"
},
"engines": {
@@ -178,7 +171,7 @@
},
"node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
"version": "6.3.1",
- "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"license": "ISC",
"bin": {
@@ -187,7 +180,7 @@
},
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
- "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
"license": "MIT",
"engines": {
@@ -195,40 +188,40 @@
}
},
"node_modules/@babel/helper-member-expression-to-functions": {
- "version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
- "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz",
+ "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==",
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
- "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
"license": "MIT",
"dependencies": {
- "@babel/traverse": "^7.27.1",
- "@babel/types": "^7.27.1"
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.27.3",
- "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz",
- "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1",
- "@babel/traverse": "^7.27.3"
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
@@ -239,7 +232,7 @@
},
"node_modules/@babel/helper-optimise-call-expression": {
"version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
"integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==",
"license": "MIT",
"dependencies": {
@@ -250,23 +243,23 @@
}
},
"node_modules/@babel/helper-plugin-utils": {
- "version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
- "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
+ "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-replace-supers": {
- "version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz",
- "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz",
+ "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==",
"license": "MIT",
"dependencies": {
- "@babel/helper-member-expression-to-functions": "^7.27.1",
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
"@babel/helper-optimise-call-expression": "^7.27.1",
- "@babel/traverse": "^7.27.1"
+ "@babel/traverse": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
@@ -277,7 +270,7 @@
},
"node_modules/@babel/helper-skip-transparent-expression-wrappers": {
"version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
"integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==",
"license": "MIT",
"dependencies": {
@@ -290,7 +283,7 @@
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"license": "MIT",
"engines": {
@@ -298,9 +291,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
- "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -308,7 +301,7 @@
},
"node_modules/@babel/helper-validator-option": {
"version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
"license": "MIT",
"engines": {
@@ -316,25 +309,25 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.28.2",
- "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.28.2.tgz",
- "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
+ "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
"license": "MIT",
"dependencies": {
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.2"
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
- "version": "7.28.0",
- "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.0.tgz",
- "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
+ "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
"license": "MIT",
"dependencies": {
- "@babel/types": "^7.28.0"
+ "@babel/types": "^7.29.0"
},
"bin": {
"parser": "bin/babel-parser.js"
@@ -344,12 +337,12 @@
}
},
"node_modules/@babel/plugin-syntax-jsx": {
- "version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz",
- "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz",
+ "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==",
"license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
+ "@babel/helper-plugin-utils": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
@@ -359,12 +352,12 @@
}
},
"node_modules/@babel/plugin-syntax-typescript": {
- "version": "7.27.1",
- "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz",
- "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz",
+ "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==",
"license": "MIT",
"dependencies": {
- "@babel/helper-plugin-utils": "^7.27.1"
+ "@babel/helper-plugin-utils": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
@@ -374,16 +367,16 @@
}
},
"node_modules/@babel/plugin-transform-typescript": {
- "version": "7.28.0",
- "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz",
- "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz",
+ "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==",
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.27.3",
- "@babel/helper-create-class-features-plugin": "^7.27.1",
- "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
- "@babel/plugin-syntax-typescript": "^7.27.1"
+ "@babel/plugin-syntax-typescript": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
@@ -393,31 +386,31 @@
}
},
"node_modules/@babel/template": {
- "version": "7.27.2",
- "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz",
- "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/parser": "^7.27.2",
- "@babel/types": "^7.27.1"
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.28.0",
- "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.0.tgz",
- "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.27.1",
- "@babel/generator": "^7.28.0",
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
"@babel/helper-globals": "^7.28.0",
- "@babel/parser": "^7.28.0",
- "@babel/template": "^7.27.2",
- "@babel/types": "^7.28.0",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0",
"debug": "^4.3.1"
},
"engines": {
@@ -425,49 +418,71 @@
}
},
"node_modules/@babel/types": {
- "version": "7.28.2",
- "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.2.tgz",
- "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1"
+ "@babel/helper-validator-identifier": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
}
},
- "node_modules/@cloudflare/kv-asset-handler": {
- "version": "0.4.0",
- "resolved": "https://registry.npmmirror.com/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.0.tgz",
- "integrity": "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==",
- "license": "MIT OR Apache-2.0",
- "dependencies": {
- "mime": "^3.0.0"
+ "node_modules/@bomb.sh/tab": {
+ "version": "0.0.12",
+ "resolved": "https://registry.npmjs.org/@bomb.sh/tab/-/tab-0.0.12.tgz",
+ "integrity": "sha512-dYRwg4MqfHR5/BcTy285XOGRhjQFmNpaJBZ0tl2oU+RY595MQ5ApTF6j3OvauPAooHL6cfoOZMySQrOQztT8RQ==",
+ "license": "MIT",
+ "bin": {
+ "tab": "dist/bin/cli.js"
},
- "engines": {
- "node": ">=18.0.0"
+ "peerDependencies": {
+ "cac": "^6.7.14",
+ "citty": "^0.1.6",
+ "commander": "^13.1.0"
+ },
+ "peerDependenciesMeta": {
+ "cac": {
+ "optional": true
+ },
+ "citty": {
+ "optional": true
+ },
+ "commander": {
+ "optional": true
+ }
}
},
- "node_modules/@cloudflare/kv-asset-handler/node_modules/mime": {
- "version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/mime/-/mime-3.0.0.tgz",
- "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+ "node_modules/@clack/core": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.0.1.tgz",
+ "integrity": "sha512-WKeyK3NOBwDOzagPR5H08rFk9D/WuN705yEbuZvKqlkmoLM2woKtXb10OO2k1NoSU4SFG947i2/SCYh+2u5e4g==",
"license": "MIT",
- "bin": {
- "mime": "cli.js"
- },
- "engines": {
- "node": ">=10.0.0"
+ "dependencies": {
+ "picocolors": "^1.0.0",
+ "sisteransi": "^1.0.5"
}
},
- "node_modules/@colors/colors": {
- "version": "1.6.0",
- "resolved": "https://registry.npmmirror.com/@colors/colors/-/colors-1.6.0.tgz",
- "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
+ "node_modules/@clack/prompts": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.0.1.tgz",
+ "integrity": "sha512-/42G73JkuYdyWZ6m8d/CJtBrGl1Hegyc7Fy78m5Ob+jF85TOUmLR5XLce/U3LxYAw0kJ8CT5aI99RIvPHcGp/Q==",
"license": "MIT",
+ "dependencies": {
+ "@clack/core": "1.0.1",
+ "picocolors": "^1.0.0",
+ "sisteransi": "^1.0.5"
+ }
+ },
+ "node_modules/@cloudflare/kv-asset-handler": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz",
+ "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==",
+ "license": "MIT OR Apache-2.0",
"engines": {
- "node": ">=0.1.90"
+ "node": ">=18.0.0"
}
},
"node_modules/@csstools/selector-resolve-nested": {
@@ -514,45 +529,40 @@
"postcss-selector-parser": "^7.0.0"
}
},
- "node_modules/@dabh/diagnostics": {
- "version": "2.0.3",
- "resolved": "https://registry.npmmirror.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
- "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
+ "node_modules/@dxup/nuxt": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@dxup/nuxt/-/nuxt-0.3.2.tgz",
+ "integrity": "sha512-2f2usP4oLNsIGjPprvABe3f3GWuIhIDp0169pGLFxTDRI5A4d4sBbGpR+tD9bGZCT+1Btb6Q2GKlyv3LkDCW5g==",
"license": "MIT",
"dependencies": {
- "colorspace": "1.1.x",
- "enabled": "2.0.x",
- "kuler": "^2.0.0"
+ "@dxup/unimport": "^0.1.2",
+ "@nuxt/kit": "^4.2.2",
+ "chokidar": "^5.0.0",
+ "pathe": "^2.0.3",
+ "tinyglobby": "^0.2.15"
}
},
- "node_modules/@dependents/detective-less": {
- "version": "5.0.1",
- "resolved": "https://registry.npmmirror.com/@dependents/detective-less/-/detective-less-5.0.1.tgz",
- "integrity": "sha512-Y6+WUMsTFWE5jb20IFP4YGa5IrGY/+a/FbOSjDF/wz9gepU2hwCYSXRHP/vPwBvwcY3SVMASt4yXxbXNXigmZQ==",
- "license": "MIT",
- "dependencies": {
- "gonzales-pe": "^4.3.0",
- "node-source-walk": "^7.0.1"
- },
- "engines": {
- "node": ">=18"
- }
+ "node_modules/@dxup/unimport": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@dxup/unimport/-/unimport-0.1.2.tgz",
+ "integrity": "sha512-/B8YJGPzaYq1NbsQmwgP8EZqg40NpTw4ZB3suuI0TplbxKHeK94jeaawLmVhCv+YwUnOpiWEz9U6SeThku/8JQ==",
+ "license": "MIT"
},
"node_modules/@emnapi/core": {
- "version": "1.4.5",
- "resolved": "https://registry.npmmirror.com/@emnapi/core/-/core-1.4.5.tgz",
- "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==",
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz",
+ "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==",
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/wasi-threads": "1.0.4",
+ "@emnapi/wasi-threads": "1.1.0",
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.4.5",
- "resolved": "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.4.5.tgz",
- "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -560,9 +570,9 @@
}
},
"node_modules/@emnapi/wasi-threads": {
- "version": "1.0.4",
- "resolved": "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz",
- "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
+ "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -570,9 +580,9 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
- "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
+ "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
"cpu": [
"ppc64"
],
@@ -586,9 +596,9 @@
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
- "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
+ "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
"cpu": [
"arm"
],
@@ -602,9 +612,9 @@
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
- "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
+ "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
"cpu": [
"arm64"
],
@@ -618,9 +628,9 @@
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
- "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
+ "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
"cpu": [
"x64"
],
@@ -634,9 +644,9 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
- "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
+ "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
"cpu": [
"arm64"
],
@@ -650,9 +660,9 @@
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
- "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
+ "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
"cpu": [
"x64"
],
@@ -666,9 +676,9 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
- "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
"cpu": [
"arm64"
],
@@ -682,9 +692,9 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
- "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
+ "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
"cpu": [
"x64"
],
@@ -698,9 +708,9 @@
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
- "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
+ "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
"cpu": [
"arm"
],
@@ -714,9 +724,9 @@
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
- "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
+ "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
"cpu": [
"arm64"
],
@@ -730,9 +740,9 @@
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
- "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
+ "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
"cpu": [
"ia32"
],
@@ -746,9 +756,9 @@
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
- "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
+ "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
"cpu": [
"loong64"
],
@@ -762,9 +772,9 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
- "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
+ "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
"cpu": [
"mips64el"
],
@@ -778,9 +788,9 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
- "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
+ "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
"cpu": [
"ppc64"
],
@@ -794,9 +804,9 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
- "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
+ "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
"cpu": [
"riscv64"
],
@@ -810,9 +820,9 @@
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
- "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
+ "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
"cpu": [
"s390x"
],
@@ -826,9 +836,9 @@
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
- "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
+ "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
"cpu": [
"x64"
],
@@ -842,9 +852,9 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
- "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
"cpu": [
"arm64"
],
@@ -858,9 +868,9 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
- "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
"cpu": [
"x64"
],
@@ -874,9 +884,9 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
- "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
"cpu": [
"arm64"
],
@@ -890,9 +900,9 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
- "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
"cpu": [
"x64"
],
@@ -906,9 +916,9 @@
}
},
"node_modules/@esbuild/openharmony-arm64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
- "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
+ "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
"cpu": [
"arm64"
],
@@ -922,9 +932,9 @@
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
- "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
+ "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
"cpu": [
"x64"
],
@@ -938,9 +948,9 @@
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
- "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
+ "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
"cpu": [
"arm64"
],
@@ -954,9 +964,9 @@
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
- "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
+ "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
"cpu": [
"ia32"
],
@@ -970,9 +980,9 @@
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
- "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
+ "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
"cpu": [
"x64"
],
@@ -985,16 +995,10 @@
"node": ">=18"
}
},
- "node_modules/@fastify/busboy": {
- "version": "3.1.1",
- "resolved": "https://registry.npmmirror.com/@fastify/busboy/-/busboy-3.1.1.tgz",
- "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==",
- "license": "MIT"
- },
"node_modules/@ioredis/commands": {
- "version": "1.2.0",
- "resolved": "https://registry.npmmirror.com/@ioredis/commands/-/commands-1.2.0.tgz",
- "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==",
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz",
+ "integrity": "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==",
"license": "MIT"
},
"node_modules/@isaacs/cliui": {
@@ -1016,7 +1020,7 @@
},
"node_modules/@isaacs/fs-minipass": {
"version": "4.0.1",
- "resolved": "https://registry.npmmirror.com/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
"license": "ISC",
"dependencies": {
@@ -1036,6 +1040,16 @@
"@jridgewell/trace-mapping": "^0.3.24"
}
},
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@@ -1046,9 +1060,9 @@
}
},
"node_modules/@jridgewell/source-map": {
- "version": "0.3.10",
- "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.10.tgz",
- "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==",
+ "version": "0.3.11",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
+ "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -1056,9 +1070,9 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.4",
- "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
- "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
+ "version": "1.5.5",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
@@ -1089,7 +1103,7 @@
},
"node_modules/@kwsites/file-exists": {
"version": "1.1.1",
- "resolved": "https://registry.npmmirror.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
"integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
"license": "MIT",
"dependencies": {
@@ -1098,14 +1112,14 @@
},
"node_modules/@kwsites/promise-deferred": {
"version": "1.1.1",
- "resolved": "https://registry.npmmirror.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
"license": "MIT"
},
"node_modules/@mapbox/node-pre-gyp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz",
- "integrity": "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.3.tgz",
+ "integrity": "sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==",
"license": "BSD-3-Clause",
"dependencies": {
"consola": "^3.2.3",
@@ -1123,1015 +1137,471 @@
"node": ">=18"
}
},
- "node_modules/@mapbox/node-pre-gyp/node_modules/detect-libc": {
- "version": "2.0.4",
- "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.4.tgz",
- "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
- "license": "Apache-2.0",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/@napi-rs/wasm-runtime": {
- "version": "1.0.1",
- "resolved": "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.1.tgz",
- "integrity": "sha512-KVlQ/jgywZpixGCKMNwxStmmbYEMyokZpCf2YuIChhfJA2uqfAKNEM8INz7zzTo55iEXfBhIIs3VqYyqzDLj8g==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
+ "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
"license": "MIT",
"optional": true,
"dependencies": {
- "@emnapi/core": "^1.4.5",
- "@emnapi/runtime": "^1.4.5",
- "@tybys/wasm-util": "^0.10.0"
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1",
+ "@tybys/wasm-util": "^0.10.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
}
},
- "node_modules/@netlify/binary-info": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/@netlify/binary-info/-/binary-info-1.0.0.tgz",
- "integrity": "sha512-4wMPu9iN3/HL97QblBsBay3E1etIciR84izI3U+4iALY+JHCrI+a2jO0qbAZ/nxKoegypYEaiiqWXylm+/zfrw==",
- "license": "Apache 2"
- },
- "node_modules/@netlify/blobs": {
- "version": "9.1.2",
- "resolved": "https://registry.npmmirror.com/@netlify/blobs/-/blobs-9.1.2.tgz",
- "integrity": "sha512-7dMjExSH4zj4ShvLem49mE3mf0K171Tx2pV4WDWhJbRUWW3SJIR2qntz0LvUGS97N5HO1SmnzrgWUhEXCsApiw==",
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"license": "MIT",
"dependencies": {
- "@netlify/dev-utils": "2.2.0",
- "@netlify/runtime-utils": "1.3.1"
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
},
"engines": {
- "node": "^14.16.0 || >=16.0.0"
+ "node": ">= 8"
}
},
- "node_modules/@netlify/dev-utils": {
- "version": "2.2.0",
- "resolved": "https://registry.npmmirror.com/@netlify/dev-utils/-/dev-utils-2.2.0.tgz",
- "integrity": "sha512-5XUvZuffe3KetyhbWwd4n2ktd7wraocCYw10tlM+/u/95iAz29GjNiuNxbCD1T6Bn1MyGc4QLVNKOWhzJkVFAw==",
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"license": "MIT",
- "dependencies": {
- "@whatwg-node/server": "^0.9.60",
- "chokidar": "^4.0.1",
- "decache": "^4.6.2",
- "dot-prop": "9.0.0",
- "env-paths": "^3.0.0",
- "find-up": "7.0.0",
- "lodash.debounce": "^4.0.8",
- "netlify": "^13.3.5",
- "parse-gitignore": "^2.0.0",
- "uuid": "^11.1.0",
- "write-file-atomic": "^6.0.0"
- },
"engines": {
- "node": "^14.16.0 || >=16.0.0"
+ "node": ">= 8"
}
},
- "node_modules/@netlify/functions": {
- "version": "3.1.10",
- "resolved": "https://registry.npmmirror.com/@netlify/functions/-/functions-3.1.10.tgz",
- "integrity": "sha512-sI93kcJ2cUoMgDRPnrEm0lZhuiDVDqM6ngS/UbHTApIH3+eg3yZM5p/0SDFQQq9Bad0/srFmgBmTdXushzY5kg==",
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"license": "MIT",
"dependencies": {
- "@netlify/blobs": "9.1.2",
- "@netlify/dev-utils": "2.2.0",
- "@netlify/serverless-functions-api": "1.41.2",
- "@netlify/zip-it-and-ship-it": "^12.1.0",
- "cron-parser": "^4.9.0",
- "decache": "^4.6.2",
- "extract-zip": "^2.0.1",
- "is-stream": "^4.0.1",
- "jwt-decode": "^4.0.0",
- "lambda-local": "^2.2.0",
- "read-package-up": "^11.0.0",
- "source-map-support": "^0.5.21"
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
},
"engines": {
- "node": ">=14.0.0"
+ "node": ">= 8"
}
},
- "node_modules/@netlify/functions/node_modules/is-stream": {
- "version": "4.0.1",
- "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-4.0.1.tgz",
- "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==",
+ "node_modules/@nuxt/cli": {
+ "version": "3.33.1",
+ "resolved": "https://registry.npmjs.org/@nuxt/cli/-/cli-3.33.1.tgz",
+ "integrity": "sha512-/sCrcI0WemING9zASaXPgPDY7PrQTPlRyCXlSgGx8VwRAkWbxGaPhIc3kZQikgLwVAwy+muWVV4Wks8OTtW5Tw==",
"license": "MIT",
+ "dependencies": {
+ "@bomb.sh/tab": "^0.0.12",
+ "@clack/prompts": "^1.0.0",
+ "c12": "^3.3.3",
+ "citty": "^0.2.0",
+ "confbox": "^0.2.4",
+ "consola": "^3.4.2",
+ "copy-paste": "^2.2.0",
+ "debug": "^4.4.3",
+ "defu": "^6.1.4",
+ "exsolve": "^1.0.8",
+ "fuse.js": "^7.1.0",
+ "fzf": "^0.5.2",
+ "giget": "^3.1.2",
+ "jiti": "^2.6.1",
+ "listhen": "^1.9.0",
+ "nypm": "^0.6.5",
+ "ofetch": "^1.5.1",
+ "ohash": "^2.0.11",
+ "pathe": "^2.0.3",
+ "perfect-debounce": "^2.1.0",
+ "pkg-types": "^2.3.0",
+ "scule": "^1.3.0",
+ "semver": "^7.7.4",
+ "srvx": "^0.11.2",
+ "std-env": "^3.10.0",
+ "tinyexec": "^1.0.2",
+ "ufo": "^1.6.3",
+ "youch": "^4.1.0-beta.13"
+ },
+ "bin": {
+ "nuxi": "bin/nuxi.mjs",
+ "nuxi-ng": "bin/nuxi.mjs",
+ "nuxt": "bin/nuxi.mjs",
+ "nuxt-cli": "bin/nuxi.mjs"
+ },
"engines": {
- "node": ">=18"
+ "node": "^16.10.0 || >=18.0.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "peerDependencies": {
+ "@nuxt/schema": "^4.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@nuxt/schema": {
+ "optional": true
+ }
}
},
- "node_modules/@netlify/open-api": {
- "version": "2.37.0",
- "resolved": "https://registry.npmmirror.com/@netlify/open-api/-/open-api-2.37.0.tgz",
- "integrity": "sha512-zXnRFkxgNsalSgU8/vwTWnav3R+8KG8SsqHxqaoJdjjJtnZR7wo3f+qqu4z+WtZ/4V7fly91HFUwZ6Uz2OdW7w==",
+ "node_modules/@nuxt/cli/node_modules/citty": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz",
+ "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==",
+ "license": "MIT"
+ },
+ "node_modules/@nuxt/cli/node_modules/giget": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/giget/-/giget-3.1.2.tgz",
+ "integrity": "sha512-T2qUpKBHeUTwHcIhydgnJzhL0Hj785ms+JkxaaWQH9SDM/llXeewnOkfJcFShAHjWI+26hOChwUfCoupaXLm8g==",
"license": "MIT",
- "engines": {
- "node": ">=14.8.0"
+ "bin": {
+ "giget": "dist/cli.mjs"
}
},
- "node_modules/@netlify/runtime-utils": {
- "version": "1.3.1",
- "resolved": "https://registry.npmmirror.com/@netlify/runtime-utils/-/runtime-utils-1.3.1.tgz",
- "integrity": "sha512-7/vIJlMYrPJPlEW84V2yeRuG3QBu66dmlv9neTmZ5nXzwylhBEOhy11ai+34A8mHCSZI4mKns25w3HM9kaDdJg==",
+ "node_modules/@nuxt/cli/node_modules/perfect-debounce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
+ "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==",
+ "license": "MIT"
+ },
+ "node_modules/@nuxt/devalue": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@nuxt/devalue/-/devalue-2.0.2.tgz",
+ "integrity": "sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA==",
+ "license": "MIT"
+ },
+ "node_modules/@nuxt/devtools": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@nuxt/devtools/-/devtools-3.2.2.tgz",
+ "integrity": "sha512-b6roSuKed5XMg09oWejXb4bRG+iYPDFRHEP2HpAfwpFWgAhpiQIAdrdjZNt4f/pzbfhDqb1R5TSa1KmztOuMKw==",
"license": "MIT",
- "engines": {
- "node": ">=16.0.0"
+ "dependencies": {
+ "@nuxt/devtools-kit": "3.2.2",
+ "@nuxt/devtools-wizard": "3.2.2",
+ "@nuxt/kit": "^4.3.1",
+ "@vue/devtools-core": "^8.0.6",
+ "@vue/devtools-kit": "^8.0.6",
+ "birpc": "^4.0.0",
+ "consola": "^3.4.2",
+ "destr": "^2.0.5",
+ "error-stack-parser-es": "^1.0.5",
+ "execa": "^8.0.1",
+ "fast-npm-meta": "^1.2.1",
+ "get-port-please": "^3.2.0",
+ "hookable": "^6.0.1",
+ "image-meta": "^0.2.2",
+ "is-installed-globally": "^1.0.0",
+ "launch-editor": "^2.13.0",
+ "local-pkg": "^1.1.2",
+ "magicast": "^0.5.2",
+ "nypm": "^0.6.5",
+ "ohash": "^2.0.11",
+ "pathe": "^2.0.3",
+ "perfect-debounce": "^2.1.0",
+ "pkg-types": "^2.3.0",
+ "semver": "^7.7.4",
+ "simple-git": "^3.32.2",
+ "sirv": "^3.0.2",
+ "structured-clone-es": "^1.0.0",
+ "tinyglobby": "^0.2.15",
+ "vite-plugin-inspect": "^11.3.3",
+ "vite-plugin-vue-tracer": "^1.2.0",
+ "which": "^5.0.0",
+ "ws": "^8.19.0"
+ },
+ "bin": {
+ "devtools": "cli.mjs"
+ },
+ "peerDependencies": {
+ "@vitejs/devtools": "*",
+ "vite": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vitejs/devtools": {
+ "optional": true
+ }
}
},
- "node_modules/@netlify/serverless-functions-api": {
- "version": "1.41.2",
- "resolved": "https://registry.npmmirror.com/@netlify/serverless-functions-api/-/serverless-functions-api-1.41.2.tgz",
- "integrity": "sha512-pfCkH50JV06SGMNsNPjn8t17hOcId4fA881HeYQgMBOrewjsw4csaYgHEnCxCEu24Y5x75E2ULbFpqm9CvRCqw==",
+ "node_modules/@nuxt/devtools-kit": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@nuxt/devtools-kit/-/devtools-kit-3.2.2.tgz",
+ "integrity": "sha512-07E1phqoVPNlexlkrYuOMPhTzLIRjcl9iEqyc/vZLH2zWeH/T1X3v+RLTVW5Oio40f/XBp9yQuyihmX34ddjgQ==",
"license": "MIT",
- "engines": {
- "node": ">=18.0.0"
+ "dependencies": {
+ "@nuxt/kit": "^4.3.1",
+ "execa": "^8.0.1"
+ },
+ "peerDependencies": {
+ "vite": ">=6.0"
}
},
- "node_modules/@netlify/zip-it-and-ship-it": {
- "version": "12.2.1",
- "resolved": "https://registry.npmmirror.com/@netlify/zip-it-and-ship-it/-/zip-it-and-ship-it-12.2.1.tgz",
- "integrity": "sha512-zAr+8Tg80y/sUbhdUkZsq4Uy1IMzkSB6H/sKRMrDQ2NJx4uPgf5X5jMdg9g2FljNcxzpfJwc1Gg4OXQrjD0Z4A==",
+ "node_modules/@nuxt/devtools-wizard": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@nuxt/devtools-wizard/-/devtools-wizard-3.2.2.tgz",
+ "integrity": "sha512-FaKV3xZF+Sj2ORxJNWTUalnEV8cpXW2rkg60KzQd7LryEHgUdFMuY/oTSVh9YmURqSzwVlfYd1Su56yi02pxlA==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.22.5",
- "@babel/types": "7.28.0",
- "@netlify/binary-info": "^1.0.0",
- "@netlify/serverless-functions-api": "^2.1.3",
- "@vercel/nft": "0.29.4",
- "archiver": "^7.0.0",
- "common-path-prefix": "^3.0.0",
- "copy-file": "^11.0.0",
- "es-module-lexer": "^1.0.0",
- "esbuild": "0.25.5",
- "execa": "^8.0.0",
- "fast-glob": "^3.3.3",
- "filter-obj": "^6.0.0",
- "find-up": "^7.0.0",
- "is-builtin-module": "^3.1.0",
- "is-path-inside": "^4.0.0",
- "junk": "^4.0.0",
- "locate-path": "^7.0.0",
- "merge-options": "^3.0.4",
- "minimatch": "^9.0.0",
- "normalize-path": "^3.0.0",
- "p-map": "^7.0.0",
- "path-exists": "^5.0.0",
- "precinct": "^12.0.0",
- "require-package-name": "^2.0.1",
- "resolve": "^2.0.0-next.1",
- "semver": "^7.3.8",
- "tmp-promise": "^3.0.2",
- "toml": "^3.0.0",
- "unixify": "^1.0.0",
- "urlpattern-polyfill": "8.0.2",
- "yargs": "^17.0.0",
- "zod": "^3.23.8"
+ "@clack/prompts": "^1.0.1",
+ "consola": "^3.4.2",
+ "diff": "^8.0.3",
+ "execa": "^8.0.1",
+ "magicast": "^0.5.2",
+ "pathe": "^2.0.3",
+ "pkg-types": "^2.3.0",
+ "semver": "^7.7.4"
},
"bin": {
- "zip-it-and-ship-it": "bin.js"
- },
- "engines": {
- "node": ">=18.14.0"
+ "devtools-wizard": "cli.mjs"
}
},
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@babel/types": {
- "version": "7.28.0",
- "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.0.tgz",
- "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==",
+ "node_modules/@nuxt/devtools/node_modules/@vue/devtools-kit": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.6.tgz",
+ "integrity": "sha512-9zXZPTJW72OteDXeSa5RVML3zWDCRcO5t77aJqSs228mdopYj5AiTpihozbsfFJ0IodfNs7pSgOGO3qfCuxDtw==",
"license": "MIT",
"dependencies": {
- "@babel/helper-string-parser": "^7.27.1",
- "@babel/helper-validator-identifier": "^7.27.1"
- },
- "engines": {
- "node": ">=6.9.0"
+ "@vue/devtools-shared": "^8.0.6",
+ "birpc": "^2.6.1",
+ "hookable": "^5.5.3",
+ "mitt": "^3.0.1",
+ "perfect-debounce": "^2.0.0",
+ "speakingurl": "^14.0.1",
+ "superjson": "^2.2.2"
}
},
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/aix-ppc64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
- "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
- "cpu": [
- "ppc64"
- ],
+ "node_modules/@nuxt/devtools/node_modules/@vue/devtools-kit/node_modules/birpc": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
+ "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
"license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
}
},
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/android-arm": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
- "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
- "cpu": [
- "arm"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/android-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
- "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/android-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
- "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/darwin-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
- "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/darwin-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
- "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/freebsd-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
- "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/freebsd-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
- "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-arm": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
- "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
- "cpu": [
- "arm"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
- "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-ia32": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
- "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
- "cpu": [
- "ia32"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-loong64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
- "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
- "cpu": [
- "loong64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-mips64el": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
- "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
- "cpu": [
- "mips64el"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-ppc64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
- "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
- "cpu": [
- "ppc64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-riscv64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
- "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
- "cpu": [
- "riscv64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-s390x": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
- "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
- "cpu": [
- "s390x"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/linux-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
- "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/netbsd-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
- "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/netbsd-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
- "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/openbsd-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
- "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/openbsd-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
- "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/sunos-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
- "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/win32-arm64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
- "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/win32-ia32": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
- "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
- "cpu": [
- "ia32"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@esbuild/win32-x64": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
- "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
- "cpu": [
- "x64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/@netlify/serverless-functions-api": {
- "version": "2.1.3",
- "resolved": "https://registry.npmmirror.com/@netlify/serverless-functions-api/-/serverless-functions-api-2.1.3.tgz",
- "integrity": "sha512-bNlN/hpND8xFQzpjyKxm6vJayD+bPBlOvs4lWihE7WULrphuH1UuFsoVE5386bNNGH8Rs1IH01AFsl7ALQgOlQ==",
- "license": "MIT",
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@netlify/zip-it-and-ship-it/node_modules/esbuild": {
- "version": "0.25.5",
- "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.5.tgz",
- "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.5",
- "@esbuild/android-arm": "0.25.5",
- "@esbuild/android-arm64": "0.25.5",
- "@esbuild/android-x64": "0.25.5",
- "@esbuild/darwin-arm64": "0.25.5",
- "@esbuild/darwin-x64": "0.25.5",
- "@esbuild/freebsd-arm64": "0.25.5",
- "@esbuild/freebsd-x64": "0.25.5",
- "@esbuild/linux-arm": "0.25.5",
- "@esbuild/linux-arm64": "0.25.5",
- "@esbuild/linux-ia32": "0.25.5",
- "@esbuild/linux-loong64": "0.25.5",
- "@esbuild/linux-mips64el": "0.25.5",
- "@esbuild/linux-ppc64": "0.25.5",
- "@esbuild/linux-riscv64": "0.25.5",
- "@esbuild/linux-s390x": "0.25.5",
- "@esbuild/linux-x64": "0.25.5",
- "@esbuild/netbsd-arm64": "0.25.5",
- "@esbuild/netbsd-x64": "0.25.5",
- "@esbuild/openbsd-arm64": "0.25.5",
- "@esbuild/openbsd-x64": "0.25.5",
- "@esbuild/sunos-x64": "0.25.5",
- "@esbuild/win32-arm64": "0.25.5",
- "@esbuild/win32-ia32": "0.25.5",
- "@esbuild/win32-x64": "0.25.5"
- }
- },
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "license": "MIT",
- "engines": {
- "node": ">= 8"
- }
+ "node_modules/@nuxt/devtools/node_modules/@vue/devtools-kit/node_modules/hookable": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
+ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
+ "license": "MIT"
},
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "node_modules/@nuxt/devtools/node_modules/@vue/devtools-shared": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.6.tgz",
+ "integrity": "sha512-Pp1JylTqlgMJvxW6MGyfTF8vGvlBSCAvMFaDCYa82Mgw7TT5eE5kkHgDvmOGHWeJE4zIDfCpCxHapsK2LtIAJg==",
"license": "MIT",
"dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
+ "rfdc": "^1.4.1"
}
},
- "node_modules/@nuxt/cli": {
- "version": "3.26.4",
- "resolved": "https://registry.npmmirror.com/@nuxt/cli/-/cli-3.26.4.tgz",
- "integrity": "sha512-PeZcH7ghQxIcCaKyu+So3qGEjKG18TN1ic4jKvKFQouNgzPSVfvZAeBOHU4znEuDFp/wmoN5EliyHO4HaSs+rw==",
+ "node_modules/@nuxt/devtools/node_modules/birpc": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/birpc/-/birpc-4.0.0.tgz",
+ "integrity": "sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==",
"license": "MIT",
- "dependencies": {
- "c12": "^3.1.0",
- "citty": "^0.1.6",
- "clipboardy": "^4.0.0",
- "confbox": "^0.2.2",
- "consola": "^3.4.2",
- "defu": "^6.1.4",
- "exsolve": "^1.0.7",
- "fuse.js": "^7.1.0",
- "get-port-please": "^3.2.0",
- "giget": "^2.0.0",
- "h3": "^1.15.3",
- "httpxy": "^0.1.7",
- "jiti": "^2.4.2",
- "listhen": "^1.9.0",
- "nypm": "^0.6.0",
- "ofetch": "^1.4.1",
- "ohash": "^2.0.11",
- "pathe": "^2.0.3",
- "perfect-debounce": "^1.0.0",
- "pkg-types": "^2.2.0",
- "scule": "^1.3.0",
- "semver": "^7.7.2",
- "std-env": "^3.9.0",
- "tinyexec": "^1.0.1",
- "ufo": "^1.6.1",
- "youch": "^4.1.0-beta.10"
- },
- "bin": {
- "nuxi": "bin/nuxi.mjs",
- "nuxi-ng": "bin/nuxi.mjs",
- "nuxt": "bin/nuxi.mjs",
- "nuxt-cli": "bin/nuxi.mjs"
- },
- "engines": {
- "node": "^16.10.0 || >=18.0.0"
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
}
},
- "node_modules/@nuxt/devalue": {
- "version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/@nuxt/devalue/-/devalue-2.0.2.tgz",
- "integrity": "sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA==",
+ "node_modules/@nuxt/devtools/node_modules/hookable": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.0.1.tgz",
+ "integrity": "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==",
"license": "MIT"
},
- "node_modules/@nuxt/devtools": {
- "version": "2.6.2",
- "resolved": "https://registry.npmmirror.com/@nuxt/devtools/-/devtools-2.6.2.tgz",
- "integrity": "sha512-pqcSDPv1I+8fxa6FvhAxVrfcN/sXYLOBe9scTLbRQOVLTO0pHzryayho678qNKiwWGgj/rcjEDr6IZCgwqOCfA==",
- "license": "MIT",
- "dependencies": {
- "@nuxt/devtools-kit": "2.6.2",
- "@nuxt/devtools-wizard": "2.6.2",
- "@nuxt/kit": "^3.17.6",
- "@vue/devtools-core": "^7.7.7",
- "@vue/devtools-kit": "^7.7.7",
- "birpc": "^2.4.0",
- "consola": "^3.4.2",
- "destr": "^2.0.5",
- "error-stack-parser-es": "^1.0.5",
- "execa": "^8.0.1",
- "fast-npm-meta": "^0.4.4",
- "get-port-please": "^3.1.2",
- "hookable": "^5.5.3",
- "image-meta": "^0.2.1",
- "is-installed-globally": "^1.0.0",
- "launch-editor": "^2.10.0",
- "local-pkg": "^1.1.1",
- "magicast": "^0.3.5",
- "nypm": "^0.6.0",
- "ohash": "^2.0.11",
- "pathe": "^2.0.3",
- "perfect-debounce": "^1.0.0",
- "pkg-types": "^2.2.0",
- "semver": "^7.7.2",
- "simple-git": "^3.28.0",
- "sirv": "^3.0.1",
- "structured-clone-es": "^1.0.0",
- "tinyglobby": "^0.2.14",
- "vite-plugin-inspect": "^11.3.0",
- "vite-plugin-vue-tracer": "^1.0.0",
- "which": "^5.0.0",
- "ws": "^8.18.3"
- },
- "bin": {
- "devtools": "cli.mjs"
- },
- "peerDependencies": {
- "vite": ">=6.0"
- }
- },
- "node_modules/@nuxt/devtools-kit": {
- "version": "2.6.2",
- "resolved": "https://registry.npmmirror.com/@nuxt/devtools-kit/-/devtools-kit-2.6.2.tgz",
- "integrity": "sha512-esErdMQ0u3wXXogKQ3IE2m0fxv52w6CzPsfsXF4o5ZVrUQrQaH58ygupDAQTYdlGTgtqmEA6KkHTGG5cM6yxeg==",
- "license": "MIT",
- "dependencies": {
- "@nuxt/kit": "^3.17.6",
- "execa": "^8.0.1"
- },
- "peerDependencies": {
- "vite": ">=6.0"
- }
+ "node_modules/@nuxt/devtools/node_modules/perfect-debounce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
+ "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==",
+ "license": "MIT"
},
- "node_modules/@nuxt/devtools-kit/node_modules/@nuxt/kit": {
- "version": "3.17.7",
- "resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-3.17.7.tgz",
- "integrity": "sha512-JLno3ur7Pix2o/StxIMlEHRkMawA6h7uzjZBDgxdeKXRWTYY8ID9YekSkN4PBlEFGXBfCBOcPd5+YqcyBUAMkw==",
+ "node_modules/@nuxt/kit": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.3.1.tgz",
+ "integrity": "sha512-UjBFt72dnpc+83BV3OIbCT0YHLevJtgJCHpxMX0YRKWLDhhbcDdUse87GtsQBrjvOzK7WUNUYLDS/hQLYev5rA==",
"license": "MIT",
"dependencies": {
- "c12": "^3.0.4",
+ "c12": "^3.3.3",
"consola": "^3.4.2",
"defu": "^6.1.4",
"destr": "^2.0.5",
"errx": "^0.1.0",
- "exsolve": "^1.0.7",
+ "exsolve": "^1.0.8",
"ignore": "^7.0.5",
- "jiti": "^2.4.2",
+ "jiti": "^2.6.1",
"klona": "^2.0.6",
- "knitwork": "^1.2.0",
- "mlly": "^1.7.4",
+ "mlly": "^1.8.0",
"ohash": "^2.0.11",
"pathe": "^2.0.3",
- "pkg-types": "^2.2.0",
+ "pkg-types": "^2.3.0",
+ "rc9": "^3.0.0",
"scule": "^1.3.0",
- "semver": "^7.7.2",
- "std-env": "^3.9.0",
- "tinyglobby": "^0.2.14",
- "ufo": "^1.6.1",
- "unctx": "^2.4.1",
- "unimport": "^5.1.0",
+ "semver": "^7.7.4",
+ "tinyglobby": "^0.2.15",
+ "ufo": "^1.6.3",
+ "unctx": "^2.5.0",
"untyped": "^2.0.0"
},
"engines": {
"node": ">=18.12.0"
}
},
- "node_modules/@nuxt/devtools-wizard": {
- "version": "2.6.2",
- "resolved": "https://registry.npmmirror.com/@nuxt/devtools-wizard/-/devtools-wizard-2.6.2.tgz",
- "integrity": "sha512-s1eYYKi2eZu2ZUPQrf22C0SceWs5/C3c3uow/DVunD304Um/Tj062xM9E4p1B9L8yjaq8t0Gtyu/YvZdo/reyg==",
- "license": "MIT",
- "dependencies": {
- "consola": "^3.4.2",
- "diff": "^8.0.2",
- "execa": "^8.0.1",
- "magicast": "^0.3.5",
- "pathe": "^2.0.3",
- "pkg-types": "^2.2.0",
- "prompts": "^2.4.2",
- "semver": "^7.7.2"
- },
- "bin": {
- "devtools-wizard": "cli.mjs"
- }
- },
- "node_modules/@nuxt/devtools/node_modules/@nuxt/kit": {
- "version": "3.17.7",
- "resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-3.17.7.tgz",
- "integrity": "sha512-JLno3ur7Pix2o/StxIMlEHRkMawA6h7uzjZBDgxdeKXRWTYY8ID9YekSkN4PBlEFGXBfCBOcPd5+YqcyBUAMkw==",
+ "node_modules/@nuxt/kit/node_modules/rc9": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/rc9/-/rc9-3.0.0.tgz",
+ "integrity": "sha512-MGOue0VqscKWQ104udASX/3GYDcKyPI4j4F8gu/jHHzglpmy9a/anZK3PNe8ug6aZFl+9GxLtdhe3kVZuMaQbA==",
"license": "MIT",
"dependencies": {
- "c12": "^3.0.4",
- "consola": "^3.4.2",
"defu": "^6.1.4",
- "destr": "^2.0.5",
- "errx": "^0.1.0",
- "exsolve": "^1.0.7",
- "ignore": "^7.0.5",
- "jiti": "^2.4.2",
- "klona": "^2.0.6",
- "knitwork": "^1.2.0",
- "mlly": "^1.7.4",
- "ohash": "^2.0.11",
- "pathe": "^2.0.3",
- "pkg-types": "^2.2.0",
- "scule": "^1.3.0",
- "semver": "^7.7.2",
- "std-env": "^3.9.0",
- "tinyglobby": "^0.2.14",
- "ufo": "^1.6.1",
- "unctx": "^2.4.1",
- "unimport": "^5.1.0",
- "untyped": "^2.0.0"
- },
- "engines": {
- "node": ">=18.12.0"
+ "destr": "^2.0.5"
}
},
- "node_modules/@nuxt/kit": {
- "version": "4.0.1",
- "resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-4.0.1.tgz",
- "integrity": "sha512-9vYpbuK3xcVhuDq+NyoLhbAolV/bEESaozFOMutl0jhrODcNWFrJ8wQSZIt9yxcFXUgXgUa2ms31qaUEpXrykw==",
+ "node_modules/@nuxt/nitro-server": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@nuxt/nitro-server/-/nitro-server-4.3.1.tgz",
+ "integrity": "sha512-4aNiM69Re02gI1ywnDND0m6QdVKXhWzDdtvl/16veytdHZj3FSq57ZCwOClNJ7HQkEMqXgS+bi6S2HmJX+et+g==",
"license": "MIT",
"dependencies": {
- "c12": "^3.1.0",
+ "@nuxt/devalue": "^2.0.2",
+ "@nuxt/kit": "4.3.1",
+ "@unhead/vue": "^2.1.3",
+ "@vue/shared": "^3.5.27",
"consola": "^3.4.2",
"defu": "^6.1.4",
- "destr": "^2.0.5",
- "errx": "^0.1.0",
- "exsolve": "^1.0.7",
- "ignore": "^7.0.5",
- "jiti": "^2.4.2",
- "klona": "^2.0.6",
- "mlly": "^1.7.4",
- "ohash": "^2.0.11",
- "pathe": "^2.0.3",
- "pkg-types": "^2.2.0",
- "scule": "^1.3.0",
- "semver": "^7.7.2",
- "std-env": "^3.9.0",
- "tinyglobby": "^0.2.14",
- "ufo": "^1.6.1",
- "unctx": "^2.4.1",
- "unimport": "^5.1.0",
- "untyped": "^2.0.0"
+ "destr": "^2.0.5",
+ "devalue": "^5.6.2",
+ "errx": "^0.1.0",
+ "escape-string-regexp": "^5.0.0",
+ "exsolve": "^1.0.8",
+ "h3": "^1.15.5",
+ "impound": "^1.0.0",
+ "klona": "^2.0.6",
+ "mocked-exports": "^0.1.1",
+ "nitropack": "^2.13.1",
+ "ohash": "^2.0.11",
+ "pathe": "^2.0.3",
+ "pkg-types": "^2.3.0",
+ "rou3": "^0.7.12",
+ "std-env": "^3.10.0",
+ "ufo": "^1.6.3",
+ "unctx": "^2.5.0",
+ "unstorage": "^1.17.4",
+ "vue": "^3.5.27",
+ "vue-bundle-renderer": "^2.2.0",
+ "vue-devtools-stub": "^0.1.0"
},
"engines": {
- "node": ">=18.12.0"
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "nuxt": "^4.3.1"
}
},
"node_modules/@nuxt/schema": {
- "version": "4.0.1",
- "resolved": "https://registry.npmmirror.com/@nuxt/schema/-/schema-4.0.1.tgz",
- "integrity": "sha512-/e/avVyJ/pLydTQL9iGlpvyGiJ0Y6+TKLXlUFR0zPTJv6asHzCqHKbiL84+wSAQmTw6Hl+z0yZv8uEx21+JoHw==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@nuxt/schema/-/schema-4.3.1.tgz",
+ "integrity": "sha512-S+wHJdYDuyk9I43Ej27y5BeWMZgi7R/UVql3b3qtT35d0fbpXW7fUenzhLRCCDC6O10sjguc6fcMcR9sMKvV8g==",
"license": "MIT",
"dependencies": {
- "@vue/shared": "^3.5.17",
- "consola": "^3.4.2",
+ "@vue/shared": "^3.5.27",
"defu": "^6.1.4",
"pathe": "^2.0.3",
- "std-env": "^3.9.0"
+ "pkg-types": "^2.3.0",
+ "std-env": "^3.10.0"
},
"engines": {
"node": "^14.18.0 || >=16.10.0"
}
},
"node_modules/@nuxt/telemetry": {
- "version": "2.6.6",
- "resolved": "https://registry.npmmirror.com/@nuxt/telemetry/-/telemetry-2.6.6.tgz",
- "integrity": "sha512-Zh4HJLjzvm3Cq9w6sfzIFyH9ozK5ePYVfCUzzUQNiZojFsI2k1QkSBrVI9BGc6ArKXj/O6rkI6w7qQ+ouL8Cag==",
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/@nuxt/telemetry/-/telemetry-2.7.0.tgz",
+ "integrity": "sha512-mrKC3NjAlBOooLLVTYcIUie1meipoYq5vkoESoVTEWTB34T3a0QJzOfOPch+HYlUR+5Lqy1zLMv6epHFgYAKLA==",
"license": "MIT",
"dependencies": {
- "@nuxt/kit": "^3.15.4",
- "citty": "^0.1.6",
+ "citty": "^0.2.0",
"consola": "^3.4.2",
- "destr": "^2.0.3",
- "dotenv": "^16.4.7",
- "git-url-parse": "^16.0.1",
- "is-docker": "^3.0.0",
- "ofetch": "^1.4.1",
- "package-manager-detector": "^1.1.0",
- "pathe": "^2.0.3",
- "rc9": "^2.1.2",
- "std-env": "^3.8.1"
+ "ofetch": "^2.0.0-alpha.3",
+ "rc9": "^3.0.0",
+ "std-env": "^3.10.0"
},
"bin": {
"nuxt-telemetry": "bin/nuxt-telemetry.mjs"
},
"engines": {
"node": ">=18.12.0"
+ },
+ "peerDependencies": {
+ "@nuxt/kit": ">=3.0.0"
}
},
- "node_modules/@nuxt/telemetry/node_modules/@nuxt/kit": {
- "version": "3.17.7",
- "resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-3.17.7.tgz",
- "integrity": "sha512-JLno3ur7Pix2o/StxIMlEHRkMawA6h7uzjZBDgxdeKXRWTYY8ID9YekSkN4PBlEFGXBfCBOcPd5+YqcyBUAMkw==",
+ "node_modules/@nuxt/telemetry/node_modules/citty": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz",
+ "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==",
+ "license": "MIT"
+ },
+ "node_modules/@nuxt/telemetry/node_modules/ofetch": {
+ "version": "2.0.0-alpha.3",
+ "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-2.0.0-alpha.3.tgz",
+ "integrity": "sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA==",
+ "license": "MIT"
+ },
+ "node_modules/@nuxt/telemetry/node_modules/rc9": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/rc9/-/rc9-3.0.0.tgz",
+ "integrity": "sha512-MGOue0VqscKWQ104udASX/3GYDcKyPI4j4F8gu/jHHzglpmy9a/anZK3PNe8ug6aZFl+9GxLtdhe3kVZuMaQbA==",
"license": "MIT",
"dependencies": {
- "c12": "^3.0.4",
- "consola": "^3.4.2",
"defu": "^6.1.4",
- "destr": "^2.0.5",
- "errx": "^0.1.0",
- "exsolve": "^1.0.7",
- "ignore": "^7.0.5",
- "jiti": "^2.4.2",
- "klona": "^2.0.6",
- "knitwork": "^1.2.0",
- "mlly": "^1.7.4",
- "ohash": "^2.0.11",
- "pathe": "^2.0.3",
- "pkg-types": "^2.2.0",
- "scule": "^1.3.0",
- "semver": "^7.7.2",
- "std-env": "^3.9.0",
- "tinyglobby": "^0.2.14",
- "ufo": "^1.6.1",
- "unctx": "^2.4.1",
- "unimport": "^5.1.0",
- "untyped": "^2.0.0"
- },
- "engines": {
- "node": ">=18.12.0"
+ "destr": "^2.0.5"
}
},
"node_modules/@nuxt/vite-builder": {
- "version": "4.0.1",
- "resolved": "https://registry.npmmirror.com/@nuxt/vite-builder/-/vite-builder-4.0.1.tgz",
- "integrity": "sha512-+ScfRxpCCHkJgkBYRXkvQHLsF/vxyFkwQzTBDL6+8sg9+BcTYkxOjVHDZ1l0qzeVORXzoq4G+oPfKsob64vODA==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@nuxt/vite-builder/-/vite-builder-4.3.1.tgz",
+ "integrity": "sha512-LndnxPJzDUDbWAB8q5gZZN1mSOLHEyMOoj4T3pTdPydGf31QZdMR0V1fQ1fdRgtgNtWB3WLP0d1ZfaAOITsUpw==",
"license": "MIT",
"dependencies": {
- "@nuxt/kit": "4.0.1",
- "@rollup/plugin-replace": "^6.0.2",
- "@vitejs/plugin-vue": "^6.0.0",
- "@vitejs/plugin-vue-jsx": "^5.0.1",
- "autoprefixer": "^10.4.21",
+ "@nuxt/kit": "4.3.1",
+ "@rollup/plugin-replace": "^6.0.3",
+ "@vitejs/plugin-vue": "^6.0.4",
+ "@vitejs/plugin-vue-jsx": "^5.1.4",
+ "autoprefixer": "^10.4.24",
"consola": "^3.4.2",
- "cssnano": "^7.1.0",
+ "cssnano": "^7.1.2",
"defu": "^6.1.4",
- "esbuild": "^0.25.8",
+ "esbuild": "^0.27.3",
"escape-string-regexp": "^5.0.0",
- "exsolve": "^1.0.7",
+ "exsolve": "^1.0.8",
"get-port-please": "^3.2.0",
- "h3": "^1.15.3",
- "jiti": "^2.4.2",
- "knitwork": "^1.2.0",
- "magic-string": "^0.30.17",
- "mlly": "^1.7.4",
+ "jiti": "^2.6.1",
+ "knitwork": "^1.3.0",
+ "magic-string": "^0.30.21",
+ "mlly": "^1.8.0",
"mocked-exports": "^0.1.1",
"pathe": "^2.0.3",
- "pkg-types": "^2.2.0",
+ "pkg-types": "^2.3.0",
"postcss": "^8.5.6",
- "rollup-plugin-visualizer": "^6.0.3",
- "std-env": "^3.9.0",
- "ufo": "^1.6.1",
- "unenv": "^2.0.0-rc.18",
- "vite": "^7.0.5",
- "vite-node": "^3.2.4",
- "vite-plugin-checker": "^0.10.0",
- "vue-bundle-renderer": "^2.1.1"
+ "rollup-plugin-visualizer": "^6.0.5",
+ "seroval": "^1.5.0",
+ "std-env": "^3.10.0",
+ "ufo": "^1.6.3",
+ "unenv": "^2.0.0-rc.24",
+ "vite": "^7.3.1",
+ "vite-node": "^5.3.0",
+ "vite-plugin-checker": "^0.12.0",
+ "vue-bundle-renderer": "^2.2.0"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
+ "nuxt": "4.3.1",
+ "rolldown": "^1.0.0-beta.38",
"vue": "^3.3.4"
+ },
+ "peerDependenciesMeta": {
+ "rolldown": {
+ "optional": true
+ }
}
},
"node_modules/@nuxtjs/tailwindcss": {
@@ -2191,10 +1661,26 @@
"node": ">=18.12.0"
}
},
+ "node_modules/@oxc-minify/binding-android-arm-eabi": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-android-arm-eabi/-/binding-android-arm-eabi-0.112.0.tgz",
+ "integrity": "sha512-m7TGBR2hjsBJIN9UJ909KBoKsuogo6CuLsHKvUIBXdjI0JVHP8g4ZHeB+BJpGn5LJdeSGDfz9MWiuXrZDRzunw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
"node_modules/@oxc-minify/binding-android-arm64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-android-arm64/-/binding-android-arm64-0.77.3.tgz",
- "integrity": "sha512-9bGiDHSkPr6eaP4+/2DQerG+V69Ut4mezL1JtBTk54Iyc6tNsoHa9s+3wJSUHesXEgiHd/IxwuSXRtD9yC3VhQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-android-arm64/-/binding-android-arm64-0.112.0.tgz",
+ "integrity": "sha512-RvxOOkzvP5NeeoraBtgNJSBqO+XzlS7DooxST/drAXCfO52GsmxVB1N7QmifrsTYtH8GC2z3DTFjZQ1w/AJOWg==",
"cpu": [
"arm64"
],
@@ -2204,13 +1690,13 @@
"android"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-darwin-arm64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-darwin-arm64/-/binding-darwin-arm64-0.77.3.tgz",
- "integrity": "sha512-DcRuFK/W3VqIlS8Wvb9bwd5yX+QTlr2ds2f5HW52OPx4odFwyF3+dD6nj3kyxvxITtf6U3jjqyaZEkq+LSQ5RQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-darwin-arm64/-/binding-darwin-arm64-0.112.0.tgz",
+ "integrity": "sha512-hDslO3uVHza3kB9zkcsi25JzN65Gj5ZYty0OvylS11Mhg9ydCYxAzfQ/tISHW/YmV1NRUJX8+GGqM1cKmrHaTA==",
"cpu": [
"arm64"
],
@@ -2220,13 +1706,13 @@
"darwin"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-darwin-x64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-darwin-x64/-/binding-darwin-x64-0.77.3.tgz",
- "integrity": "sha512-ZOKwC0nRNKpDKZq+sbFTbzJbrGR+drhIx3jhaTzSFpTWyzs3m5PW0yB+bKhhrqnk1Y26jtNixykBNiyhuPhCxQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-darwin-x64/-/binding-darwin-x64-0.112.0.tgz",
+ "integrity": "sha512-mWA2Y5bUyNoGM+gSGGHesgtQ3LDWgpRe4zDGkBDovxNIiDLBXqu/7QcuS+G918w8oG9VYm1q1iinILer/2pD1Q==",
"cpu": [
"x64"
],
@@ -2236,13 +1722,13 @@
"darwin"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-freebsd-x64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-freebsd-x64/-/binding-freebsd-x64-0.77.3.tgz",
- "integrity": "sha512-z2LgrCT0YjxNIZRTOBFY5/FnqGX9S5QvkC/yoYqfDDuest8T6feTN68xXWg6D8+vFJPukvKEGY1xGikybc33xA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-freebsd-x64/-/binding-freebsd-x64-0.112.0.tgz",
+ "integrity": "sha512-T7fsegxcy82xS0jWPXkz/BMhrkb3D7YOCiV0R9pDksjaov+iIFoNEWAoBsaC5NtpdzkX+bmffwDpu336EIfEeg==",
"cpu": [
"x64"
],
@@ -2252,13 +1738,13 @@
"freebsd"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-linux-arm-gnueabihf": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.77.3.tgz",
- "integrity": "sha512-VdpPQk9Xuu6C+p2DprWAEhIyELBrZLAzipMxoRnmox/HlFigs+FIeEfklCMls3yMSLCu6wKTdMdWeRu+dLXEHg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.112.0.tgz",
+ "integrity": "sha512-yePavbIilAcpVYc8vRsDCn3xJxHMXDZIiamyH9fuLosAHNELcLib4/JR4fhDk4NmHVagQH3kRhsnm5Q9cm3pAw==",
"cpu": [
"arm"
],
@@ -2268,13 +1754,13 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-linux-arm-musleabihf": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.77.3.tgz",
- "integrity": "sha512-bhiPBIQKIxKtOSHgxYQiVeJ7CrfHWDZxaNFMf6ktDBmYBeD9lE9A356wDfgBPFkVOGV+juSPrnpu7qg2si/Q7Q==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.112.0.tgz",
+ "integrity": "sha512-lmPWLXtW6FspERhy97iP0hwbmLtL66xI29QQ9GpHmTiE4k+zv/FaefuV/Qw+LuHnmFSYzUNrLcxh4ulOZTIP2g==",
"cpu": [
"arm"
],
@@ -2284,13 +1770,13 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-linux-arm64-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.77.3.tgz",
- "integrity": "sha512-PYjFgTLCMxoa4yIgxVTNOltGk9nuPWTYZpDGEZu0he+0HC4iD86ZJIEl0mW0CaNaMU2np/7gAr+Izu3W71q+FQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.112.0.tgz",
+ "integrity": "sha512-gySS5XqU5MKs/oCjsTlVm8zb8lqcNKHEANsaRmhW2qvGKJoeGwFb6Fbq6TLCZMRuk143mLbncbverBCa1c3dog==",
"cpu": [
"arm64"
],
@@ -2300,13 +1786,13 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-linux-arm64-musl": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.77.3.tgz",
- "integrity": "sha512-GWa6MyEIwrDfEruj9SmIi/eG0XyEPSSupbltCL2k/cYgb+aUl1lT3sJLbOlKZqBbTzpuouAd+CkDqz+8UH/0qA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.112.0.tgz",
+ "integrity": "sha512-IRFMZX589lr3rjG0jc8N261/7wqFq2Vl0OMrJWeFls5BF8HiB+fRYuf0Zy2CyRH6NCY2vbdDdp+QCAavQGVsGw==",
"cpu": [
"arm64"
],
@@ -2316,13 +1802,29 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-minify/binding-linux-ppc64-gnu": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.112.0.tgz",
+ "integrity": "sha512-V/69XqIW9hCUceDpcZh79oDg+F4ptEgIfKRENzYs41LRbSoJ7sNjjcW4zifqyviTvzcnXLgK4uoTyoymmNZBMQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-linux-riscv64-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.77.3.tgz",
- "integrity": "sha512-Wj1h95rGfMMVu0NMBNzo56WaB+z/mBVFRF4ij4Dbf2oBy4o3qDe2Q5Doa5U5c1k/uJbsM1X/mV7vqqgkHdORBA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.112.0.tgz",
+ "integrity": "sha512-zghvexySyGXGNW+MutjZN7UGTyOQl56RWMlPe1gb+knBm/+0hf9qjk7Q6ofm2tSte+vQolPfQttifGl0dP9uvQ==",
"cpu": [
"riscv64"
],
@@ -2332,13 +1834,29 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-minify/binding-linux-riscv64-musl": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.112.0.tgz",
+ "integrity": "sha512-E4a8VUFDJPb2mPcc7J4NQQPi1ssHKF7/g4r6KD2+SBVERIaEEd3cGNqR7SG3g82/BLGV2UDoQe/WvZCkt5M/bQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-linux-s390x-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.77.3.tgz",
- "integrity": "sha512-xTIGeZZoOfa7c4FU+1OcZTk73W/0YD2m3Zwg4p0Wtch+0Z6VRyu/7CENjBXpCRkWF4C8sgvl6d8ZKOzF5wU+Dw==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.112.0.tgz",
+ "integrity": "sha512-2Hx87sK3y6jBV364Mvv0zyxiITIuy26Ixenv6pK7e+4an3HgNdhAj8nk3aLoLTTSvLik5/MaGhcZGEu9tYV1aA==",
"cpu": [
"s390x"
],
@@ -2348,13 +1866,13 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-linux-x64-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.77.3.tgz",
- "integrity": "sha512-YkgfAVmdtPMqJO/elfYBstnwGjD2P0SJwAs02c84/1JKRemrjSKqSewg3ETFIpo43c6b0g9OtoWj47Wwpnka7A==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.112.0.tgz",
+ "integrity": "sha512-2MSCnEPLk9ddSouMhJo78Xy2/JbYC80OYzWdR4yWTGSULsgH3d1VXg73DSwFL8vU7Ad9oK10DioBY2ww7sQTEg==",
"cpu": [
"x64"
],
@@ -2364,13 +1882,13 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-linux-x64-musl": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-linux-x64-musl/-/binding-linux-x64-musl-0.77.3.tgz",
- "integrity": "sha512-//A5mBFmxvV+JzqI2/94SFpEF+nev0I/urXwhYPe8qzCYTlnzwxodH0Yb6js+BgebqiPdYs6YEp5Q2C/6OgsbA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-x64-musl/-/binding-linux-x64-musl-0.112.0.tgz",
+ "integrity": "sha512-HAPfmQKlkVi97/zRonVE9t/kKUG3ni+mOuU1Euw+3s37KwUuOJjmcwXdclVgXKBlTkCGO0FajPwW5dAJeIXCCw==",
"cpu": [
"x64"
],
@@ -2380,29 +1898,45 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-minify/binding-openharmony-arm64": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-openharmony-arm64/-/binding-openharmony-arm64-0.112.0.tgz",
+ "integrity": "sha512-bLnMojcPadYzMNpB6IAqMiTOag4etc0zbs8On73JsotO1W5c5/j/ncplpSokpEpNasKRUpHVRXpmq0KRXprNhw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-wasm32-wasi": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-wasm32-wasi/-/binding-wasm32-wasi-0.77.3.tgz",
- "integrity": "sha512-4RCG1ZZyEyKIaZE2vXyFnVocDF1jIbfE/f5qbb1l0Wql2s4K5m1QDkKqPAVPuCmYiJ6+X2HyWus5QGqgnUKrXA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-wasm32-wasi/-/binding-wasm32-wasi-0.112.0.tgz",
+ "integrity": "sha512-tv7PmHYq/8QBlqMaDjsy51GF5KQkG17Yc/PsgB5OVndU34kwbQuebBIic7UfK9ygzidI8moYq3ztnu3za/rqHw==",
"cpu": [
"wasm32"
],
"license": "MIT",
"optional": true,
"dependencies": {
- "@napi-rs/wasm-runtime": "^1.0.0"
+ "@napi-rs/wasm-runtime": "^1.1.1"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@oxc-minify/binding-win32-arm64-msvc": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.77.3.tgz",
- "integrity": "sha512-ppyKF8Y3iASeIBnPDL0mwDxnlq/nnKFEZpZ9dy2hDma/JDD9qmOheP3CGYZyUnkS9r0LvEtrtR5/FjKXF2VQOw==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.112.0.tgz",
+ "integrity": "sha512-d+jes2jwRkcBSpcaZC6cL8GBi56Br6uAorn9dfquhWLczWL+hHSvvVrRgT1i5/6dkf5UWx2zdoEsAMiJ11w78A==",
"cpu": [
"arm64"
],
@@ -2412,13 +1946,29 @@
"win32"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-minify/binding-win32-ia32-msvc": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.112.0.tgz",
+ "integrity": "sha512-TV1C3qDwj7//jNIi5tnNRhReSUgtaRQKi5KobDE6zVAc5gjeuBA8G2qizS9ziXlf/I0dlelrGmGMMDJmH9ekWg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-minify/binding-win32-x64-msvc": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-minify/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.77.3.tgz",
- "integrity": "sha512-8IY7xgdZjBDFyQCF0s7EB7YzVB+C4+p8AKDbPfKLYhSlntIfIqTYvSXc3dZQb83OH6kDLAs1GpdWgb8ByDu4kg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.112.0.tgz",
+ "integrity": "sha512-LML2Gld6VY8/+7a3VH4k1qngsBXvTkXgbmYgSYwaElqtiQiYaAcXfi0XKOUGe3k3GbBK4juAGixC31CrdFHAQw==",
"cpu": [
"x64"
],
@@ -2428,13 +1978,29 @@
"win32"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-parser/binding-android-arm-eabi": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.112.0.tgz",
+ "integrity": "sha512-retxBzJ39Da7Lh/eZTn9+HJgTeDUxZIpuI0urOsmcFsBKXAth3lc1jIvwseQ9qbAI/VrsoFOXiGIzgclARbAHg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-android-arm64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.77.3.tgz",
- "integrity": "sha512-Tr9pldnu+Csd5dQm2/fLKJyBloxiBC/Xl3c3Ki1ZGQewndsFyfFOklFpigZCCqlt75o+HtwtoLiCx3y4i8cdjg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.112.0.tgz",
+ "integrity": "sha512-pRkbBRbuIIsufUWpOJ+JHWfJFNupkidy4sbjfcm37e6xwYrn9LSKMLubPHvNaL1Zf92ZRhGiwaYkEcmaFg2VcA==",
"cpu": [
"arm64"
],
@@ -2444,13 +2010,13 @@
"android"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-darwin-arm64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.77.3.tgz",
- "integrity": "sha512-KL91O6OpfVUTOhTW8cQWQ44z4VhyqBAsRfTm7DQCczBZkArygp2Sg+uaYLXNLWlGPWs4CoyZPCvu4FC6p1Q+nA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.112.0.tgz",
+ "integrity": "sha512-fh6/KQL/cbH5DukT3VkdCqnULLuvVnszVKySD5IgSE0WZb32YZo/cPsPdEv052kk6w3N4agu+NTiMnZjcvhUIg==",
"cpu": [
"arm64"
],
@@ -2460,13 +2026,13 @@
"darwin"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-darwin-x64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.77.3.tgz",
- "integrity": "sha512-BTWnX9ymZFdkJONuL20Y63ODjDo1hpRHcqa0Z9pqcLANFgS+sDltcu0DXkJpNuJoZQJ+/44FVSWFmbYGG+862g==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.112.0.tgz",
+ "integrity": "sha512-vUBOOY1E30vlu/DoTGDoT1UbLlwu5Yv9tqeBabAwRzwNDz8Skho16VKhsBDUiyqddtpsR3//v6vNk38w4c+6IA==",
"cpu": [
"x64"
],
@@ -2476,13 +2042,13 @@
"darwin"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-freebsd-x64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.77.3.tgz",
- "integrity": "sha512-YGp4lA0deJXrqrQC1PZwfQSPuY+TPZJOr5pqB+GLekRVZDlq2++Wr3lZfsESp1inVZHGFZS0x55/MadABG23rg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.112.0.tgz",
+ "integrity": "sha512-hnEtO/9AVnYWzrgnp6L+oPs/6UqlFeteUL6n7magkd2tttgmx1C01hyNNh6nTpZfLzEVJSNJ0S+4NTsK2q2CxA==",
"cpu": [
"x64"
],
@@ -2492,13 +2058,13 @@
"freebsd"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-arm-gnueabihf": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.77.3.tgz",
- "integrity": "sha512-C05V3gAtSM1j2gsybF4Z+vlA5wsuNJ+Ciklc0K9y1SNbObz2JDv/Q7PTYMUz9EFk7Y00aCzjy5sXEdCI191Htw==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.112.0.tgz",
+ "integrity": "sha512-WxJrUz3pcIc2hp4lvJbvt/sTL33oX9NPvkD3vDDybE6tc0V++rS+hNOJxwXdD2FDIFPkHs/IEn5asEZFVH+VKw==",
"cpu": [
"arm"
],
@@ -2508,13 +2074,13 @@
"linux"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-arm-musleabihf": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.77.3.tgz",
- "integrity": "sha512-g4bbjZ/fDm1rQbfEhqXCtK4eLmmm6U+W37zsl5Lpy7c24RJYhR25keI+RWfwH5f31Sn5ytuwfxwgXeCby6AiVA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.112.0.tgz",
+ "integrity": "sha512-jj8A8WWySaJQqM9XKAIG8U2Q3qxhFQKrXPWv98d1oC35at+L1h+C+V4M3l8BAKhpHKCu3dYlloaAbHd5q1Hw6A==",
"cpu": [
"arm"
],
@@ -2524,13 +2090,13 @@
"linux"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-arm64-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.77.3.tgz",
- "integrity": "sha512-c2Ak73vOeGnSQhsaZpqVyGYXtmQ8TR4L3uX34LNavXTnzrXm20bk6i80Nxnksz3B+5ohYRiYhb+UVk1zk1Gl1A==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.112.0.tgz",
+ "integrity": "sha512-G2F8H6FcAExVK5vvhpSh61tqWx5QoaXXUnSsj5FyuDiFT/K7AMMVSQVqnZREDc+YxhrjB0vnKjCcuobXK63kIw==",
"cpu": [
"arm64"
],
@@ -2540,13 +2106,13 @@
"linux"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-arm64-musl": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.77.3.tgz",
- "integrity": "sha512-1DNLBoJ6fsEdymD8Q4bo5zXkK0gw3ZMkEZ+F5w+7OrJOiQqzp8JzCQ6HRmSsJgjvaXzBvy95nCH2RegoeSN9JQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.112.0.tgz",
+ "integrity": "sha512-3R0iqjM3xYOZCnwgcxOQXH7hrz64/USDIuLbNTM1kZqQzRqaR4w7SwoWKU934zABo8d0op2oSwOp+CV3hZnM7A==",
"cpu": [
"arm64"
],
@@ -2556,13 +2122,29 @@
"linux"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-parser/binding-linux-ppc64-gnu": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.112.0.tgz",
+ "integrity": "sha512-lAQf8PQxfgy7h0bmcfSVE3hg3qMueshPYULFsCrHM+8KefGZ9W+ZMvRyU33gLrB4w1O3Fz1orR0hmKMCRxXNrQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-riscv64-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.77.3.tgz",
- "integrity": "sha512-Lv0RQCHRKezkDzNPXoPuB7KTnK7ktw3OgyuZmNJKFGmZRFjlm8w+sEhAiE8XaCGqoOA6ivNIRSheUYeFNpAANA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.112.0.tgz",
+ "integrity": "sha512-2QlvQBUhHuAE3ezD4X3CAEKMXdfgInggQ5Bj/7gb5NcYP3GyfLTj7c+mMu+BRwfC9B3AXBNyqHWbqEuuUvZyRQ==",
"cpu": [
"riscv64"
],
@@ -2572,13 +2154,29 @@
"linux"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-parser/binding-linux-riscv64-musl": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.112.0.tgz",
+ "integrity": "sha512-v06iu0osHszgqJ1dLQRb6leWFU1sjG/UQk4MoVBtE6ZPewgfTkby6G9II1SpEAf2onnAuQceVYxQH9iuU3NJqw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-s390x-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.77.3.tgz",
- "integrity": "sha512-Q0sOdRzyhhUaATgtSR7lG23SvalRI9/7oVAWArU/8fEXCU9NsfKnpeuXsgT/N5lG4mgcbhUrnGzKaOzYcaatdQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.112.0.tgz",
+ "integrity": "sha512-+5HhNHtxsdcd7+ljXFnn9FOoCNXJX3UPgIfIE6vdwS1HqdGNH6eAcVobuqGOp54l8pvcxDQA6F4cPswCgLrQfQ==",
"cpu": [
"s390x"
],
@@ -2588,13 +2186,13 @@
"linux"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-x64-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.77.3.tgz",
- "integrity": "sha512-ckcntxRTyPE+4nnCDnc9t4kiO1CSs5jOR7Qe7KLStkU9SPQkUZyjNP2aSaHre+iQha5xXABag9pamqb0dOY/PQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.112.0.tgz",
+ "integrity": "sha512-jKwO7ZLNkjxwg7FoCLw+fJszooL9yXRZsDN0AQ1AQUTWq1l8GH/2e44k68N3fcP19jl8O8jGpqLAZcQTYk6skA==",
"cpu": [
"x64"
],
@@ -2604,13 +2202,13 @@
"linux"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-linux-x64-musl": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.77.3.tgz",
- "integrity": "sha512-jrKtGQrjcocnWpUIxJ3qzb0WpLGcDZoQTen/CZ5QtuwFA5EudM5rAJMt+SQpYYL4UPK0CPm8G5ZWJXqernLe1A==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.112.0.tgz",
+ "integrity": "sha512-TYqnuKV/p3eOc+N61E0961nA7DC+gaCeJ3+V2LcjJdTwFMdikqWL6uVk1jlrpUCBrozHDATVUKDZYH7r4FQYjQ==",
"cpu": [
"x64"
],
@@ -2620,29 +2218,45 @@
"linux"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-parser/binding-openharmony-arm64": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.112.0.tgz",
+ "integrity": "sha512-ZhrVmWFifVEFQX4XPwLoVFDHw9tAWH9p9vHsHFH+5uCKdfVR+jje4WxVo6YrokWCboGckoOzHq5KKMOcPZfkRg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-wasm32-wasi": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.77.3.tgz",
- "integrity": "sha512-76f53rr4Dz7A/FdUaM1NegHsQqT2w8CDBnRCptzapVA8humKA/tlJ24XfLvvr76JeT/OSKXorPyJ5xyGCa+yQg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.112.0.tgz",
+ "integrity": "sha512-Gr8X2PUU3hX1g3F5oLWIZB8DhzDmjr5TfOrmn5tlBOo9l8ojPGdKjnIBfObM7X15928vza8QRKW25RTR7jfivg==",
"cpu": [
"wasm32"
],
"license": "MIT",
"optional": true,
"dependencies": {
- "@napi-rs/wasm-runtime": "^1.0.0"
+ "@napi-rs/wasm-runtime": "^1.1.1"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@oxc-parser/binding-win32-arm64-msvc": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.77.3.tgz",
- "integrity": "sha512-YiUlN4yS5U7ntU1eVsaSiKD5PzW3zaW1tSB6RIp/eaDg10xORAPXEpoCXYlo35tAOV3IklOrX8ClhSJxF99AEQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.112.0.tgz",
+ "integrity": "sha512-t5CDLbU70Ea88bGRhvU/dLJTc/Wcrtf2Jp534E8P3cgjAvHDjdKsfDDqBZrhybJ8Jv9v9vW5ngE40EK51BluDA==",
"cpu": [
"arm64"
],
@@ -2652,13 +2266,29 @@
"win32"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-parser/binding-win32-ia32-msvc": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.112.0.tgz",
+ "integrity": "sha512-rZH0JynCCwnhe2HfRoyNOl/Kfd9pudoWxgpC5OZhj7j77pMK0UOAa35hYDfrtSOUk2HLzrikV5dPUOY2DpSBSA==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-parser/binding-win32-x64-msvc": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.77.3.tgz",
- "integrity": "sha512-d4JRqTtkpyB7QrGQk65xhiSOIwK2WZiTW5aBjyoQ+SicrvnHtviAY1U1Mnl2AyldUZ6MkUvaR6k8tCm9FMhawg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.112.0.tgz",
+ "integrity": "sha512-oGHluohzmVFAuQrkEnl1OXAxMz2aYmimxUqIgKXpBgbr7PvFv0doELB273sX+5V3fKeggohKg1A2Qq21W9Z9cQ==",
"cpu": [
"x64"
],
@@ -2668,22 +2298,38 @@
"win32"
],
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-project/types": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-project/types/-/types-0.77.3.tgz",
- "integrity": "sha512-5Vh+neJhhxuF0lYCjZXbxjqm2EO6YJ1jG+KuHntrd6VY67OMpYhWq2cZhUhy+xL9qLJVJRaeII7Xj9fciA6v7A==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.112.0.tgz",
+ "integrity": "sha512-m6RebKHIRsax2iCwVpYW2ErQwa4ywHJrE4sCK3/8JK8ZZAWOKXaRJFl/uP51gaVyyXlaS4+chU1nSCdzYf6QqQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
}
},
+ "node_modules/@oxc-transform/binding-android-arm-eabi": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-android-arm-eabi/-/binding-android-arm-eabi-0.112.0.tgz",
+ "integrity": "sha512-r4LuBaPnOAi0eUOBNi880Fm2tO2omH7N1FRrL6+nyz/AjQ+QPPLtoyZJva0O+sKi1buyN/7IzM5p9m+5ANSDbg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
"node_modules/@oxc-transform/binding-android-arm64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-android-arm64/-/binding-android-arm64-0.77.3.tgz",
- "integrity": "sha512-HZdfhSsaqBCwl/HtsRVNh7binRz0N3IdwlTc5emEqYWMMZ94RkhPheNnbhRCzdvnzRKrpGirf3Rsk1X2oqSlxg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-android-arm64/-/binding-android-arm64-0.112.0.tgz",
+ "integrity": "sha512-ve46vQcQrY8eGe8990VSlS9gkD+AogJqbtfOkeua+5sQGQTDgeIRRxOm7ktCo19uZc2bEBwXRJITgosd+NRVmQ==",
"cpu": [
"arm64"
],
@@ -2693,13 +2339,13 @@
"android"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-darwin-arm64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-darwin-arm64/-/binding-darwin-arm64-0.77.3.tgz",
- "integrity": "sha512-5sMgT6Ie7S5UqqZCdssAGBVU5PouZKIIfUf10SM4dY7J/1M0Sb4E1E7O+p2VUkECJ2j2RFRykK5rdKz71na8hg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-darwin-arm64/-/binding-darwin-arm64-0.112.0.tgz",
+ "integrity": "sha512-ddbmLU3Tr+i7MOynfwAXxUXud3SjJKlv7XNjaq08qiI8Av/QvhXVGc2bMhXkWQSMSBUeTDoiughKjK+Zsb6y/A==",
"cpu": [
"arm64"
],
@@ -2709,13 +2355,13 @@
"darwin"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-darwin-x64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-darwin-x64/-/binding-darwin-x64-0.77.3.tgz",
- "integrity": "sha512-k99EStA6V4jOoFwN0pblhWuOFTKnaMasTpJIq30227U/Cg1J+rttK8loONSvgrw6FUKLJSymUA2Ydwpdvn5+sg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-darwin-x64/-/binding-darwin-x64-0.112.0.tgz",
+ "integrity": "sha512-TKvmNw96jQZPqYb4pRrzLFDailNB3YS14KNn+x2hwRbqc6CqY96S9PYwyOpVpYdxfoRjYO9WgX9SoS+62a1DPA==",
"cpu": [
"x64"
],
@@ -2725,13 +2371,13 @@
"darwin"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-freebsd-x64": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-freebsd-x64/-/binding-freebsd-x64-0.77.3.tgz",
- "integrity": "sha512-pxtPtFdJcI0xkUKWMaHV/fXy9MY5ugocA/gLoXIjTDKZC1OMVjr6Srrtk0CoUIU7l7DMePbcJIAtwrpHwRiwpQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-freebsd-x64/-/binding-freebsd-x64-0.112.0.tgz",
+ "integrity": "sha512-YPMkSCDaelO8HHYRMYjm+Q+IfkfIbdtQzwPuasItYkq8UUkNeHNPheNh2JkvQa3c+io3E9ePOgHQ2yihpk7o/Q==",
"cpu": [
"x64"
],
@@ -2741,13 +2387,13 @@
"freebsd"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-linux-arm-gnueabihf": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.77.3.tgz",
- "integrity": "sha512-zXsbUE/5tU7OJwyhhKUfl559W9w7QJp8USKA3WyW7BzHrBe0V0U6Lw+tM18tgyEvvwvXn5Wg0Jj/RWZwhO9BAA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.112.0.tgz",
+ "integrity": "sha512-nA7kzQGNEpuTRknst/IJ3l8hqmDmEda3aun6jkXgp7gKxESjuHeaNH04mKISxvJ7fIacvP2g/wtTSnm4u5jL8Q==",
"cpu": [
"arm"
],
@@ -2757,13 +2403,13 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-linux-arm-musleabihf": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.77.3.tgz",
- "integrity": "sha512-D3o/POM0GUno8x0zKgFKmlO5shpB/j0FdNiOXhv8nilNGQgUXwkEHC/SDjmYJNGZy1HTcXyB7P+yRX9dTUUaAg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.112.0.tgz",
+ "integrity": "sha512-w8GuLmckKlGc3YujaZKhtbFxziCcosvM2l9GnQjCb/yENWLGDiyQOy0BTAgPGdJwpYTiOeJblEXSuXYvlE1Ong==",
"cpu": [
"arm"
],
@@ -2773,13 +2419,13 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-linux-arm64-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.77.3.tgz",
- "integrity": "sha512-LgY4sT+bnt01l3Dxq3Zv19gMAsJ5kI7sdVvL3CNCtAj47h/Zdfxg7WlD+L+FJZ3sfTQ5n2SJ0WDiZm380isBxg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.112.0.tgz",
+ "integrity": "sha512-9LwwGnJ8+WT0rXcrI8M0RJtDNt91eMqcDPPEvJxhRFHIMcHTy5D5xT+fOl3Us0yMqKo3HUWkbfUYqAp4GoZ3Jw==",
"cpu": [
"arm64"
],
@@ -2789,13 +2435,13 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-linux-arm64-musl": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.77.3.tgz",
- "integrity": "sha512-Fq72ARLt8iriotueGp7zaWjFpfYBpRS5WElmAtpZLIy/p1dNwBEDhVUIjAl+sU14y0odp+yaTRHM7ULnMYGZhQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.112.0.tgz",
+ "integrity": "sha512-Lg6VOuSd3oXv7J0eGywgqh/086h+qQzIBOD+47pYKMTTJcbDe+f3h/RgGoMKJE5HhiwT5sH1aGEJfIfaYUiVSw==",
"cpu": [
"arm64"
],
@@ -2805,13 +2451,29 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-transform/binding-linux-ppc64-gnu": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.112.0.tgz",
+ "integrity": "sha512-PXzmj82o1moA4IGphYImTRgc2youTi4VRfyFX3CHwLjxPcQ5JtcsgbDt4QUdOzXZ+zC07s5jf2ZzhRapEOlj2w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-linux-riscv64-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.77.3.tgz",
- "integrity": "sha512-jtq6JREdyZ6xdTFJGM5Gm068WCkoMwh3Fkm08rZ2TAu4qjISdkJvTQ1wiEDDz2F8sqAdmASDqxnE/2DJ6Z6Clg==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.112.0.tgz",
+ "integrity": "sha512-vhJsMsVH/6xwa3bt1LGts33FXUkGjaEGDwsRyp4lIfOjSfQVWMtCmWMFNaA0dW9FVWdD2Gt2fSFBSZ+azDxlpg==",
"cpu": [
"riscv64"
],
@@ -2821,13 +2483,29 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-transform/binding-linux-riscv64-musl": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.112.0.tgz",
+ "integrity": "sha512-cXWFb7z+2IjFUEcXtRwluq9oEG5qnyFCjiu3SWrgYNcWwPdHusv3I/7K5/CTbbi4StoZ5txbi7/iSfDHNyWuRw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-linux-s390x-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.77.3.tgz",
- "integrity": "sha512-HQz++ZmT9xWU9KS24DE+8oVTeUPd/JQkbjL2uvr0+SWY3loPnLG3kFAOLE/xXgYG/0D24mZylbZUwhzYND4snw==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.112.0.tgz",
+ "integrity": "sha512-eEFu4SRqJTJ20/88KRWmp+jpHKAw0Y1DsnSgpEeXyBIIcsOaLIUMU/TfYWUmqRbvbMV9rmOmI3kp5xWYUq6kSQ==",
"cpu": [
"s390x"
],
@@ -2837,13 +2515,13 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-linux-x64-gnu": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.77.3.tgz",
- "integrity": "sha512-GcuFDJf/pxrfd2hq+gBytlnr/hiPn36JxuPXP0nToNG4SNa1gHT8K0bDxZuN2UjmZlWmIC8ELDdpVcNeZON+lQ==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.112.0.tgz",
+ "integrity": "sha512-ST1MDT+TlOyZ1c5btrGinRSUW2Jf4Pa+0gdKwsyjDSOC3dxy2ZNkN3mosTf4ywc3J+mxfYKqtjs7zSwHz03ILA==",
"cpu": [
"x64"
],
@@ -2853,13 +2531,13 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-linux-x64-musl": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-linux-x64-musl/-/binding-linux-x64-musl-0.77.3.tgz",
- "integrity": "sha512-unhkqVg/jb/kghmiMCto8AGKm3uBwH2P5/GwR8jZkBjSFX7ekNu6/8P5IuIs5KDiZXzcjww84vCzQVBlql6WkA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-x64-musl/-/binding-linux-x64-musl-0.112.0.tgz",
+ "integrity": "sha512-ISQoA3pD4cyTGpf9sXXeerH6pL2L6EIpdy6oAy2ttkswyVFDyQNVOVIGIdLZDgbpmqGljxZnWqt/J/N68pQaig==",
"cpu": [
"x64"
],
@@ -2869,29 +2547,45 @@
"linux"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-transform/binding-openharmony-arm64": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-openharmony-arm64/-/binding-openharmony-arm64-0.112.0.tgz",
+ "integrity": "sha512-UOGVrGIv7yLJovyEXEyUTADuLq98vd/cbMHFLJweRXD+11I8Tn4jASi4WzdsN8C3BVYGRHrXH2NlSBmhz33a4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-wasm32-wasi": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-wasm32-wasi/-/binding-wasm32-wasi-0.77.3.tgz",
- "integrity": "sha512-FOGQzHLYpf1Yx0KpaqRz9cuXwvlTu8RprjL1NLpuUKT/D7O3SThm+qhFX3El9RFj67jrSCcHhlElYCJB2p794g==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-wasm32-wasi/-/binding-wasm32-wasi-0.112.0.tgz",
+ "integrity": "sha512-XIX7Gpq9koAvzBVHDlVFHM79r5uOVK6kTEsdsN4qaajpjkgtv4tdsAOKIYK6l7fUbsbE6xS+6w1+yRFrDeC1kg==",
"cpu": [
"wasm32"
],
"license": "MIT",
"optional": true,
"dependencies": {
- "@napi-rs/wasm-runtime": "^1.0.0"
+ "@napi-rs/wasm-runtime": "^1.1.1"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@oxc-transform/binding-win32-arm64-msvc": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.77.3.tgz",
- "integrity": "sha512-o4EmaPBrdYv/mb4uU/ZzAZ6KGczcPnDwA3lZbVEuFMDPwczqL581gpJHFFlfXUwxToCosiHot8y4ELV+mKkZjw==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.112.0.tgz",
+ "integrity": "sha512-EgXef9kOne9BNsbYBbuRqxk2hteT0xsAGcx/VbtCBMJYNj8fANFhT271DUSOgfa4DAgrQQmsyt/Kr1aV9mpU9w==",
"cpu": [
"arm64"
],
@@ -2901,13 +2595,29 @@
"win32"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/@oxc-transform/binding-win32-ia32-msvc": {
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.112.0.tgz",
+ "integrity": "sha512-6QaB0qjNaou2YR+blncHdw7j0e26IOwOIjLbhVGDeuf9+4rjJeiqRXJ2hOtCcS4zblnao/MjdgQuZ3fM0nl+Kw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@oxc-transform/binding-win32-x64-msvc": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/@oxc-transform/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.77.3.tgz",
- "integrity": "sha512-l/J/T6jAL6QnsvdjzS7EcxwwToaGx9GPqXNGPU2sqbo8o/4ATB9Ky1/8oG/Mb+mPHgiULPBtFpJtDiDSI9fBIA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.112.0.tgz",
+ "integrity": "sha512-FRKYlY959QeqRPx9kXs0HjU2xuXPT1cdF+vvA200D9uAX/KLcC34MwRqUKTYml4kCc2Vf/P2pBR9cQuBm3zECQ==",
"cpu": [
"x64"
],
@@ -2917,20 +2627,20 @@
"win32"
],
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@parcel/watcher": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz",
- "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
+ "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
- "detect-libc": "^1.0.3",
+ "detect-libc": "^2.0.3",
"is-glob": "^4.0.3",
- "micromatch": "^4.0.5",
- "node-addon-api": "^7.0.0"
+ "node-addon-api": "^7.0.0",
+ "picomatch": "^4.0.3"
},
"engines": {
"node": ">= 10.0.0"
@@ -2940,25 +2650,25 @@
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
- "@parcel/watcher-android-arm64": "2.5.1",
- "@parcel/watcher-darwin-arm64": "2.5.1",
- "@parcel/watcher-darwin-x64": "2.5.1",
- "@parcel/watcher-freebsd-x64": "2.5.1",
- "@parcel/watcher-linux-arm-glibc": "2.5.1",
- "@parcel/watcher-linux-arm-musl": "2.5.1",
- "@parcel/watcher-linux-arm64-glibc": "2.5.1",
- "@parcel/watcher-linux-arm64-musl": "2.5.1",
- "@parcel/watcher-linux-x64-glibc": "2.5.1",
- "@parcel/watcher-linux-x64-musl": "2.5.1",
- "@parcel/watcher-win32-arm64": "2.5.1",
- "@parcel/watcher-win32-ia32": "2.5.1",
- "@parcel/watcher-win32-x64": "2.5.1"
+ "@parcel/watcher-android-arm64": "2.5.6",
+ "@parcel/watcher-darwin-arm64": "2.5.6",
+ "@parcel/watcher-darwin-x64": "2.5.6",
+ "@parcel/watcher-freebsd-x64": "2.5.6",
+ "@parcel/watcher-linux-arm-glibc": "2.5.6",
+ "@parcel/watcher-linux-arm-musl": "2.5.6",
+ "@parcel/watcher-linux-arm64-glibc": "2.5.6",
+ "@parcel/watcher-linux-arm64-musl": "2.5.6",
+ "@parcel/watcher-linux-x64-glibc": "2.5.6",
+ "@parcel/watcher-linux-x64-musl": "2.5.6",
+ "@parcel/watcher-win32-arm64": "2.5.6",
+ "@parcel/watcher-win32-ia32": "2.5.6",
+ "@parcel/watcher-win32-x64": "2.5.6"
}
},
"node_modules/@parcel/watcher-android-arm64": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
- "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
+ "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
"cpu": [
"arm64"
],
@@ -2976,9 +2686,9 @@
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
- "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
+ "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
"cpu": [
"arm64"
],
@@ -2996,9 +2706,9 @@
}
},
"node_modules/@parcel/watcher-darwin-x64": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
- "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
+ "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
"cpu": [
"x64"
],
@@ -3016,9 +2726,9 @@
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
- "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
+ "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
"cpu": [
"x64"
],
@@ -3036,9 +2746,9 @@
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
- "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
+ "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
"cpu": [
"arm"
],
@@ -3056,9 +2766,9 @@
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
- "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
+ "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
"cpu": [
"arm"
],
@@ -3076,9 +2786,9 @@
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
- "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
+ "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
"cpu": [
"arm64"
],
@@ -3096,9 +2806,9 @@
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
- "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
+ "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
"cpu": [
"arm64"
],
@@ -3116,9 +2826,9 @@
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
- "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
+ "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
"cpu": [
"x64"
],
@@ -3136,9 +2846,9 @@
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
- "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
+ "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
"cpu": [
"x64"
],
@@ -3156,17 +2866,17 @@
}
},
"node_modules/@parcel/watcher-wasm": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-wasm/-/watcher-wasm-2.5.1.tgz",
- "integrity": "sha512-RJxlQQLkaMMIuWRozy+z2vEqbaQlCuaCgVZIUCzQLYggY22LZbP5Y1+ia+FD724Ids9e+XIyOLXLrLgQSHIthw==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-wasm/-/watcher-wasm-2.5.6.tgz",
+ "integrity": "sha512-byAiBZ1t3tXQvc8dMD/eoyE7lTXYorhn+6uVW5AC+JGI1KtJC/LvDche5cfUE+qiefH+Ybq0bUCJU0aB1cSHUA==",
"bundleDependencies": [
"napi-wasm"
],
"license": "MIT",
"dependencies": {
"is-glob": "^4.0.3",
- "micromatch": "^4.0.5",
- "napi-wasm": "^1.1.0"
+ "napi-wasm": "^1.1.0",
+ "picomatch": "^4.0.3"
},
"engines": {
"node": ">= 10.0.0"
@@ -3182,9 +2892,9 @@
"license": "MIT"
},
"node_modules/@parcel/watcher-win32-arm64": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
- "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
+ "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
"cpu": [
"arm64"
],
@@ -3202,9 +2912,9 @@
}
},
"node_modules/@parcel/watcher-win32-ia32": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
- "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
+ "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
"cpu": [
"ia32"
],
@@ -3222,9 +2932,9 @@
}
},
"node_modules/@parcel/watcher-win32-x64": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
- "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
+ "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
"cpu": [
"x64"
],
@@ -3301,32 +3011,23 @@
},
"node_modules/@polka/url": {
"version": "1.0.0-next.29",
- "resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.29.tgz",
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
"integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
"license": "MIT"
},
"node_modules/@poppinss/colors": {
- "version": "4.1.5",
- "resolved": "https://registry.npmmirror.com/@poppinss/colors/-/colors-4.1.5.tgz",
- "integrity": "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw==",
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz",
+ "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==",
"license": "MIT",
"dependencies": {
"kleur": "^4.1.5"
}
},
- "node_modules/@poppinss/colors/node_modules/kleur": {
- "version": "4.1.5",
- "resolved": "https://registry.npmmirror.com/kleur/-/kleur-4.1.5.tgz",
- "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@poppinss/dumper": {
- "version": "0.6.4",
- "resolved": "https://registry.npmmirror.com/@poppinss/dumper/-/dumper-0.6.4.tgz",
- "integrity": "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ==",
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.7.0.tgz",
+ "integrity": "sha512-0UTYalzk2t6S4rA2uHOz5bSSW2CHdv4vggJI6Alg90yvl0UgXs6XSXpH96OH+bRkX4J/06djv29pqXJ0lq5Kag==",
"license": "MIT",
"dependencies": {
"@poppinss/colors": "^4.1.5",
@@ -3335,27 +3036,27 @@
}
},
"node_modules/@poppinss/exception": {
- "version": "1.2.2",
- "resolved": "https://registry.npmmirror.com/@poppinss/exception/-/exception-1.2.2.tgz",
- "integrity": "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz",
+ "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==",
"license": "MIT"
},
"node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.19",
- "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz",
- "integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==",
+ "version": "1.0.0-rc.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz",
+ "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==",
"license": "MIT"
},
"node_modules/@rollup/plugin-alias": {
- "version": "5.1.1",
- "resolved": "https://registry.npmmirror.com/@rollup/plugin-alias/-/plugin-alias-5.1.1.tgz",
- "integrity": "sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-6.0.0.tgz",
+ "integrity": "sha512-tPCzJOtS7uuVZd+xPhoy5W4vThe6KWXNmsFCNktaAh5RTqcLiSfT4huPQIXkgJ6YCOjJHvecOAzQxLFhPxKr+g==",
"license": "MIT",
"engines": {
- "node": ">=14.0.0"
+ "node": ">=20.19.0"
},
"peerDependencies": {
- "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ "rollup": ">=4.0.0"
},
"peerDependenciesMeta": {
"rollup": {
@@ -3364,9 +3065,9 @@
}
},
"node_modules/@rollup/plugin-commonjs": {
- "version": "28.0.6",
- "resolved": "https://registry.npmmirror.com/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz",
- "integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==",
+ "version": "29.0.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.0.tgz",
+ "integrity": "sha512-U2YHaxR2cU/yAiwKJtJRhnyLk7cifnQw0zUpISsocBDoHDJn+HTV74ABqnwr5bEgWUwFZC9oFL6wLe21lHu5eQ==",
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
@@ -3391,13 +3092,13 @@
},
"node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": {
"version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+ "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/@rollup/plugin-inject": {
"version": "5.0.5",
- "resolved": "https://registry.npmmirror.com/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz",
"integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==",
"license": "MIT",
"dependencies": {
@@ -3419,13 +3120,13 @@
},
"node_modules/@rollup/plugin-inject/node_modules/estree-walker": {
"version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+ "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/@rollup/plugin-json": {
"version": "6.1.0",
- "resolved": "https://registry.npmmirror.com/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
"integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
"license": "MIT",
"dependencies": {
@@ -3444,9 +3145,9 @@
}
},
"node_modules/@rollup/plugin-node-resolve": {
- "version": "16.0.1",
- "resolved": "https://registry.npmmirror.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz",
- "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==",
+ "version": "16.0.3",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz",
+ "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==",
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
@@ -3467,30 +3168,10 @@
}
}
},
- "node_modules/@rollup/plugin-node-resolve/node_modules/resolve": {
- "version": "1.22.10",
- "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz",
- "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.16.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/@rollup/plugin-replace": {
- "version": "6.0.2",
- "resolved": "https://registry.npmmirror.com/@rollup/plugin-replace/-/plugin-replace-6.0.2.tgz",
- "integrity": "sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==",
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz",
+ "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==",
"license": "MIT",
"dependencies": {
"@rollup/pluginutils": "^5.0.1",
@@ -3510,7 +3191,7 @@
},
"node_modules/@rollup/plugin-terser": {
"version": "0.4.4",
- "resolved": "https://registry.npmmirror.com/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
"integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
"license": "MIT",
"dependencies": {
@@ -3531,9 +3212,9 @@
}
},
"node_modules/@rollup/pluginutils": {
- "version": "5.2.0",
- "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
- "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
+ "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
@@ -3554,14 +3235,14 @@
},
"node_modules/@rollup/pluginutils/node_modules/estree-walker": {
"version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+ "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/@rollup/rollup-android-arm-eabi": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz",
- "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
+ "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
"cpu": [
"arm"
],
@@ -3572,9 +3253,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz",
- "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
+ "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
"cpu": [
"arm64"
],
@@ -3585,9 +3266,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz",
- "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
+ "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
"cpu": [
"arm64"
],
@@ -3598,9 +3279,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz",
- "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
+ "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
"cpu": [
"x64"
],
@@ -3611,9 +3292,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz",
- "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
+ "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
"cpu": [
"arm64"
],
@@ -3624,9 +3305,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz",
- "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
+ "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
"cpu": [
"x64"
],
@@ -3637,9 +3318,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz",
- "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
+ "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
"cpu": [
"arm"
],
@@ -3650,9 +3331,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz",
- "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
+ "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
"cpu": [
"arm"
],
@@ -3663,9 +3344,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz",
- "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
+ "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
"cpu": [
"arm64"
],
@@ -3676,9 +3357,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz",
- "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
+ "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
"cpu": [
"arm64"
],
@@ -3688,10 +3369,10 @@
"linux"
]
},
- "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz",
- "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==",
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
+ "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
"cpu": [
"loong64"
],
@@ -3701,10 +3382,36 @@
"linux"
]
},
- "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz",
- "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==",
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
+ "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
+ "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
+ "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
"cpu": [
"ppc64"
],
@@ -3715,9 +3422,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz",
- "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
+ "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
"cpu": [
"riscv64"
],
@@ -3728,9 +3435,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz",
- "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
+ "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
"cpu": [
"riscv64"
],
@@ -3741,9 +3448,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz",
- "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
+ "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
"cpu": [
"s390x"
],
@@ -3754,9 +3461,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz",
- "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
+ "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
"cpu": [
"x64"
],
@@ -3767,9 +3474,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz",
- "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
+ "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
"cpu": [
"x64"
],
@@ -3779,10 +3486,36 @@
"linux"
]
},
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
+ "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
+ "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz",
- "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
+ "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
"cpu": [
"arm64"
],
@@ -3793,9 +3526,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz",
- "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
+ "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
"cpu": [
"ia32"
],
@@ -3805,10 +3538,23 @@
"win32"
]
},
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
+ "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz",
- "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
+ "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
"cpu": [
"x64"
],
@@ -3819,9 +3565,9 @@
]
},
"node_modules/@sindresorhus/is": {
- "version": "7.0.2",
- "resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-7.0.2.tgz",
- "integrity": "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz",
+ "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -3831,9 +3577,9 @@
}
},
"node_modules/@sindresorhus/merge-streams": {
- "version": "2.3.0",
- "resolved": "https://registry.npmmirror.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
- "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz",
+ "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -3843,15 +3589,15 @@
}
},
"node_modules/@speed-highlight/core": {
- "version": "1.2.7",
- "resolved": "https://registry.npmmirror.com/@speed-highlight/core/-/core-1.2.7.tgz",
- "integrity": "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==",
+ "version": "1.2.14",
+ "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.14.tgz",
+ "integrity": "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==",
"license": "CC0-1.0"
},
"node_modules/@tybys/wasm-util": {
- "version": "0.10.0",
- "resolved": "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.0.tgz",
- "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==",
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -3864,248 +3610,215 @@
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"license": "MIT"
},
- "node_modules/@types/node": {
- "version": "24.1.0",
- "resolved": "https://registry.npmmirror.com/@types/node/-/node-24.1.0.tgz",
- "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "undici-types": "~7.8.0"
- }
- },
- "node_modules/@types/normalize-package-data": {
- "version": "2.4.4",
- "resolved": "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
- "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
- "license": "MIT"
- },
- "node_modules/@types/parse-path": {
- "version": "7.0.3",
- "resolved": "https://registry.npmmirror.com/@types/parse-path/-/parse-path-7.0.3.tgz",
- "integrity": "sha512-LriObC2+KYZD3FzCrgWGv/qufdUy4eXrxcLgQMfYXgPbLIecKIsVBaQgUPmxSSLcjmYbDTQbMgr6qr6l/eb7Bg==",
- "license": "MIT"
- },
"node_modules/@types/resolve": {
"version": "1.20.2",
- "resolved": "https://registry.npmmirror.com/@types/resolve/-/resolve-1.20.2.tgz",
+ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"license": "MIT"
},
- "node_modules/@types/triple-beam": {
- "version": "1.3.5",
- "resolved": "https://registry.npmmirror.com/@types/triple-beam/-/triple-beam-1.3.5.tgz",
- "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
+ "node_modules/@types/web-bluetooth": {
+ "version": "0.0.21",
+ "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
+ "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
"license": "MIT"
},
- "node_modules/@types/yauzl": {
- "version": "2.10.3",
- "resolved": "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz",
- "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@typescript-eslint/project-service": {
- "version": "8.38.0",
- "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.38.0.tgz",
- "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==",
+ "node_modules/@unhead/vue": {
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.1.9.tgz",
+ "integrity": "sha512-7SqqDEn5zFID1PnEdjLCLa/kOhoAlzol0JdYfVr2Ejek+H4ON4s8iyExv2QQ8bReMosbXQ/Bw41j2CF1NUuGSA==",
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.38.0",
- "@typescript-eslint/types": "^8.38.0",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "hookable": "^6.0.1",
+ "unhead": "2.1.9"
},
"funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
+ "url": "https://github.com/sponsors/harlan-zw"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <5.9.0"
+ "vue": ">=3.5.18"
}
},
- "node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.38.0",
- "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz",
- "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==",
+ "node_modules/@unhead/vue/node_modules/hookable": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.0.1.tgz",
+ "integrity": "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==",
+ "license": "MIT"
+ },
+ "node_modules/@vercel/nft": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-1.3.2.tgz",
+ "integrity": "sha512-HC8venRc4Ya7vNeBsJneKHHMDDWpQie7VaKhAIOst3MKO+DES+Y/SbzSp8mFkD7OzwAE2HhHkeSuSmwS20mz3A==",
"license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "dependencies": {
+ "@mapbox/node-pre-gyp": "^2.0.0",
+ "@rollup/pluginutils": "^5.1.3",
+ "acorn": "^8.6.0",
+ "acorn-import-attributes": "^1.9.5",
+ "async-sema": "^3.1.1",
+ "bindings": "^1.4.0",
+ "estree-walker": "2.0.2",
+ "glob": "^13.0.0",
+ "graceful-fs": "^4.2.9",
+ "node-gyp-build": "^4.2.2",
+ "picomatch": "^4.0.2",
+ "resolve-from": "^5.0.0"
},
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
+ "bin": {
+ "nft": "out/cli.js"
},
- "peerDependencies": {
- "typescript": ">=4.8.4 <5.9.0"
- }
- },
- "node_modules/@typescript-eslint/types": {
- "version": "8.38.0",
- "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.38.0.tgz",
- "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==",
- "license": "MIT",
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
+ "node": ">=20"
}
},
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.38.0",
- "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz",
- "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==",
+ "node_modules/@vercel/nft/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"license": "MIT",
- "dependencies": {
- "@typescript-eslint/project-service": "8.38.0",
- "@typescript-eslint/tsconfig-utils": "8.38.0",
- "@typescript-eslint/types": "8.38.0",
- "@typescript-eslint/visitor-keys": "8.38.0",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.1.0"
- },
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <5.9.0"
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.38.0",
- "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz",
- "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==",
+ "node_modules/@vercel/nft/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.38.0",
- "eslint-visitor-keys": "^4.2.1"
+ "balanced-match": "^4.0.2"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/@unhead/vue": {
- "version": "2.0.12",
- "resolved": "https://registry.npmmirror.com/@unhead/vue/-/vue-2.0.12.tgz",
- "integrity": "sha512-WFaiCVbBd39FK6Bx3GQskhgT9s45Vjx6dRQegYheVwU1AnF+FAfJVgWbrl21p6fRJcLAFp0xDz6wE18JYBM0eQ==",
- "license": "MIT",
- "dependencies": {
- "hookable": "^5.5.3",
- "unhead": "2.0.12"
- },
- "funding": {
- "url": "https://github.com/sponsors/harlan-zw"
- },
- "peerDependencies": {
- "vue": ">=3.5.13"
- }
+ "node_modules/@vercel/nft/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/@vercel/nft": {
- "version": "0.29.4",
- "resolved": "https://registry.npmmirror.com/@vercel/nft/-/nft-0.29.4.tgz",
- "integrity": "sha512-6lLqMNX3TuycBPABycx7A9F1bHQR7kiQln6abjFbPrf5C/05qHM9M5E4PeTE59c7z8g6vHnx1Ioihb2AQl7BTA==",
- "license": "MIT",
+ "node_modules/@vercel/nft/node_modules/glob": {
+ "version": "13.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
+ "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "@mapbox/node-pre-gyp": "^2.0.0",
- "@rollup/pluginutils": "^5.1.3",
- "acorn": "^8.6.0",
- "acorn-import-attributes": "^1.9.5",
- "async-sema": "^3.1.1",
- "bindings": "^1.4.0",
- "estree-walker": "2.0.2",
- "glob": "^10.4.5",
- "graceful-fs": "^4.2.9",
- "node-gyp-build": "^4.2.2",
- "picomatch": "^4.0.2",
- "resolve-from": "^5.0.0"
+ "minimatch": "^10.2.2",
+ "minipass": "^7.1.3",
+ "path-scurry": "^2.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
},
- "bin": {
- "nft": "out/cli.js"
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@vercel/nft/node_modules/lru-cache": {
+ "version": "11.2.6",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
+ "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@vercel/nft/node_modules/minimatch": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
},
"engines": {
- "node": ">=18"
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/@vercel/nft/node_modules/estree-walker": {
+ "node_modules/@vercel/nft/node_modules/path-scurry": {
"version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
- "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "license": "MIT"
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
+ "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
},
"node_modules/@vitejs/plugin-vue": {
- "version": "6.0.0",
- "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.0.tgz",
- "integrity": "sha512-iAliE72WsdhjzTOp2DtvKThq1VBC4REhwRcaA+zPAAph6I+OQhUXv+Xu2KS7ElxYtb7Zc/3R30Hwv1DxEo7NXQ==",
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.4.tgz",
+ "integrity": "sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ==",
"license": "MIT",
"dependencies": {
- "@rolldown/pluginutils": "1.0.0-beta.19"
+ "@rolldown/pluginutils": "1.0.0-rc.2"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
- "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0",
"vue": "^3.2.25"
}
},
"node_modules/@vitejs/plugin-vue-jsx": {
- "version": "5.0.1",
- "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-5.0.1.tgz",
- "integrity": "sha512-X7qmQMXbdDh+sfHUttXokPD0cjPkMFoae7SgbkF9vi3idGUKmxLcnU2Ug49FHwiKXebfzQRIm5yK3sfCJzNBbg==",
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-5.1.4.tgz",
+ "integrity": "sha512-70LmoVk9riR7qc4W2CpjsbNMWTPnuZb9dpFKX1emru0yP57nsc9k8nhLA6U93ngQapv5VDIUq2JatNfLbBIkrA==",
"license": "MIT",
"dependencies": {
- "@babel/core": "^7.27.7",
- "@babel/plugin-transform-typescript": "^7.27.1",
- "@rolldown/pluginutils": "^1.0.0-beta.21",
- "@vue/babel-plugin-jsx": "^1.4.0"
+ "@babel/core": "^7.29.0",
+ "@babel/plugin-syntax-typescript": "^7.28.6",
+ "@babel/plugin-transform-typescript": "^7.28.6",
+ "@rolldown/pluginutils": "^1.0.0-rc.2",
+ "@vue/babel-plugin-jsx": "^2.0.1"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
- "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0",
"vue": "^3.0.0"
}
},
- "node_modules/@vitejs/plugin-vue-jsx/node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.29",
- "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz",
- "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==",
+ "node_modules/@volar/language-core": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz",
+ "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@volar/source-map": "2.4.28"
+ }
+ },
+ "node_modules/@volar/source-map": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz",
+ "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==",
"license": "MIT"
},
"node_modules/@vue-macros/common": {
- "version": "3.0.0-beta.15",
- "resolved": "https://registry.npmmirror.com/@vue-macros/common/-/common-3.0.0-beta.15.tgz",
- "integrity": "sha512-DMgq/rIh1H20WYNWU7krIbEfJRYDDhy7ix64GlT4AVUJZZWCZ5pxiYVJR3A3GmWQPkn7Pg7i3oIiGqu4JGC65w==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.2.tgz",
+ "integrity": "sha512-h9t4ArDdniO9ekYHAD95t9AZcAbb19lEGK+26iAjUODOIJKmObDNBSe4+6ELQAA3vtYiFPPBtHh7+cQCKi3Dng==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-sfc": "^3.5.17",
- "ast-kit": "^2.1.0",
- "local-pkg": "^1.1.1",
- "magic-string-ast": "^1.0.0",
- "unplugin-utils": "^0.2.4"
+ "@vue/compiler-sfc": "^3.5.22",
+ "ast-kit": "^2.1.2",
+ "local-pkg": "^1.1.2",
+ "magic-string-ast": "^1.0.2",
+ "unplugin-utils": "^0.3.0"
},
"engines": {
- "node": ">=20.18.0"
+ "node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/sponsors/vue-macros"
@@ -4119,27 +3832,43 @@
}
}
},
+ "node_modules/@vue-macros/common/node_modules/unplugin-utils": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz",
+ "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==",
+ "license": "MIT",
+ "dependencies": {
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
"node_modules/@vue/babel-helper-vue-transform-on": {
- "version": "1.4.0",
- "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.4.0.tgz",
- "integrity": "sha512-mCokbouEQ/ocRce/FpKCRItGo+013tHg7tixg3DUNS+6bmIchPt66012kBMm476vyEIJPafrvOf4E5OYj3shSw==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-2.0.1.tgz",
+ "integrity": "sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==",
"license": "MIT"
},
"node_modules/@vue/babel-plugin-jsx": {
- "version": "1.4.0",
- "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.4.0.tgz",
- "integrity": "sha512-9zAHmwgMWlaN6qRKdrg1uKsBKHvnUU+Py+MOCTuYZBoZsopa90Di10QRjB+YPnVss0BZbG/H5XFwJY1fTxJWhA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-2.0.1.tgz",
+ "integrity": "sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==",
"license": "MIT",
"dependencies": {
- "@babel/helper-module-imports": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.26.5",
- "@babel/plugin-syntax-jsx": "^7.25.9",
- "@babel/template": "^7.26.9",
- "@babel/traverse": "^7.26.9",
- "@babel/types": "^7.26.9",
- "@vue/babel-helper-vue-transform-on": "1.4.0",
- "@vue/babel-plugin-resolve-type": "1.4.0",
- "@vue/shared": "^3.5.13"
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-syntax-jsx": "^7.27.1",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.4",
+ "@babel/types": "^7.28.4",
+ "@vue/babel-helper-vue-transform-on": "2.0.1",
+ "@vue/babel-plugin-resolve-type": "2.0.1",
+ "@vue/shared": "^3.5.22"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
@@ -4151,16 +3880,16 @@
}
},
"node_modules/@vue/babel-plugin-resolve-type": {
- "version": "1.4.0",
- "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.4.0.tgz",
- "integrity": "sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-2.0.1.tgz",
+ "integrity": "sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==",
"license": "MIT",
"dependencies": {
- "@babel/code-frame": "^7.26.2",
- "@babel/helper-module-imports": "^7.25.9",
- "@babel/helper-plugin-utils": "^7.26.5",
- "@babel/parser": "^7.26.9",
- "@vue/compiler-sfc": "^3.5.13"
+ "@babel/code-frame": "^7.27.1",
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/parser": "^7.28.4",
+ "@vue/compiler-sfc": "^3.5.22"
},
"funding": {
"url": "https://github.com/sponsors/sxzz"
@@ -4170,47 +3899,47 @@
}
},
"node_modules/@vue/compiler-core": {
- "version": "3.5.18",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.18.tgz",
- "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==",
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.29.tgz",
+ "integrity": "sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.0",
- "@vue/shared": "3.5.18",
- "entities": "^4.5.0",
+ "@babel/parser": "^7.29.0",
+ "@vue/shared": "3.5.29",
+ "entities": "^7.0.1",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-core/node_modules/estree-walker": {
"version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+ "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/@vue/compiler-dom": {
- "version": "3.5.18",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz",
- "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==",
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.29.tgz",
+ "integrity": "sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-core": "3.5.18",
- "@vue/shared": "3.5.18"
+ "@vue/compiler-core": "3.5.29",
+ "@vue/shared": "3.5.29"
}
},
"node_modules/@vue/compiler-sfc": {
- "version": "3.5.18",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz",
- "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==",
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.29.tgz",
+ "integrity": "sha512-oJZhN5XJs35Gzr50E82jg2cYdZQ78wEwvRO6Y63TvLVTc+6xICzJHP1UIecdSPPYIbkautNBanDiWYa64QSFIA==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.0",
- "@vue/compiler-core": "3.5.18",
- "@vue/compiler-dom": "3.5.18",
- "@vue/compiler-ssr": "3.5.18",
- "@vue/shared": "3.5.18",
+ "@babel/parser": "^7.29.0",
+ "@vue/compiler-core": "3.5.29",
+ "@vue/compiler-dom": "3.5.29",
+ "@vue/compiler-ssr": "3.5.29",
+ "@vue/shared": "3.5.29",
"estree-walker": "^2.0.2",
- "magic-string": "^0.30.17",
+ "magic-string": "^0.30.21",
"postcss": "^8.5.6",
"source-map-js": "^1.2.1"
}
@@ -4222,13 +3951,13 @@
"license": "MIT"
},
"node_modules/@vue/compiler-ssr": {
- "version": "3.5.18",
- "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz",
- "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==",
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.29.tgz",
+ "integrity": "sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.18",
- "@vue/shared": "3.5.18"
+ "@vue/compiler-dom": "3.5.29",
+ "@vue/shared": "3.5.29"
}
},
"node_modules/@vue/devtools-api": {
@@ -4238,27 +3967,58 @@
"license": "MIT"
},
"node_modules/@vue/devtools-core": {
- "version": "7.7.7",
- "resolved": "https://registry.npmmirror.com/@vue/devtools-core/-/devtools-core-7.7.7.tgz",
- "integrity": "sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==",
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.0.6.tgz",
+ "integrity": "sha512-fN7iVtpSQQdtMORWwVZ1JiIAKriinhD+lCHqPw9Rr252ae2TczILEmW0zcAZifPW8HfYcbFkn+h7Wv6kQQCayw==",
"license": "MIT",
"dependencies": {
- "@vue/devtools-kit": "^7.7.7",
- "@vue/devtools-shared": "^7.7.7",
+ "@vue/devtools-kit": "^8.0.6",
+ "@vue/devtools-shared": "^8.0.6",
"mitt": "^3.0.1",
- "nanoid": "^5.1.0",
+ "nanoid": "^5.1.5",
"pathe": "^2.0.3",
- "vite-hot-client": "^2.0.4"
+ "vite-hot-client": "^2.1.0"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
+ "node_modules/@vue/devtools-core/node_modules/@vue/devtools-kit": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.6.tgz",
+ "integrity": "sha512-9zXZPTJW72OteDXeSa5RVML3zWDCRcO5t77aJqSs228mdopYj5AiTpihozbsfFJ0IodfNs7pSgOGO3qfCuxDtw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-shared": "^8.0.6",
+ "birpc": "^2.6.1",
+ "hookable": "^5.5.3",
+ "mitt": "^3.0.1",
+ "perfect-debounce": "^2.0.0",
+ "speakingurl": "^14.0.1",
+ "superjson": "^2.2.2"
+ }
+ },
+ "node_modules/@vue/devtools-core/node_modules/@vue/devtools-shared": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.6.tgz",
+ "integrity": "sha512-Pp1JylTqlgMJvxW6MGyfTF8vGvlBSCAvMFaDCYa82Mgw7TT5eE5kkHgDvmOGHWeJE4zIDfCpCxHapsK2LtIAJg==",
+ "license": "MIT",
+ "dependencies": {
+ "rfdc": "^1.4.1"
+ }
+ },
+ "node_modules/@vue/devtools-core/node_modules/perfect-debounce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
+ "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==",
+ "license": "MIT"
+ },
"node_modules/@vue/devtools-kit": {
"version": "7.7.7",
"resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz",
"integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@vue/devtools-shared": "^7.7.7",
"birpc": "^2.3.0",
@@ -4274,137 +4034,170 @@
"resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz",
"integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"rfdc": "^1.4.1"
}
},
+ "node_modules/@vue/language-core": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.2.5.tgz",
+ "integrity": "sha512-d3OIxN/+KRedeM5wQ6H6NIpwS3P5gC9nmyaHgBk+rO6dIsjY+tOh4UlPpiZbAh3YtLdCGEX4M16RmsBqPmJV+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "2.4.28",
+ "@vue/compiler-dom": "^3.5.0",
+ "@vue/shared": "^3.5.0",
+ "alien-signals": "^3.0.0",
+ "muggle-string": "^0.4.1",
+ "path-browserify": "^1.0.1",
+ "picomatch": "^4.0.2"
+ }
+ },
"node_modules/@vue/reactivity": {
- "version": "3.5.18",
- "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.18.tgz",
- "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==",
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.29.tgz",
+ "integrity": "sha512-zcrANcrRdcLtmGZETBxWqIkoQei8HaFpZWx/GHKxx79JZsiZ8j1du0VUJtu4eJjgFvU/iKL5lRXFXksVmI+5DA==",
"license": "MIT",
"dependencies": {
- "@vue/shared": "3.5.18"
+ "@vue/shared": "3.5.29"
}
},
"node_modules/@vue/runtime-core": {
- "version": "3.5.18",
- "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.18.tgz",
- "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==",
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.29.tgz",
+ "integrity": "sha512-8DpW2QfdwIWOLqtsNcds4s+QgwSaHSJY/SUe04LptianUQ/0xi6KVsu/pYVh+HO3NTVvVJjIPL2t6GdeKbS4Lg==",
"license": "MIT",
"dependencies": {
- "@vue/reactivity": "3.5.18",
- "@vue/shared": "3.5.18"
+ "@vue/reactivity": "3.5.29",
+ "@vue/shared": "3.5.29"
}
},
"node_modules/@vue/runtime-dom": {
- "version": "3.5.18",
- "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz",
- "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==",
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.29.tgz",
+ "integrity": "sha512-AHvvJEtcY9tw/uk+s/YRLSlxxQnqnAkjqvK25ZiM4CllCZWzElRAoQnCM42m9AHRLNJ6oe2kC5DCgD4AUdlvXg==",
"license": "MIT",
"dependencies": {
- "@vue/reactivity": "3.5.18",
- "@vue/runtime-core": "3.5.18",
- "@vue/shared": "3.5.18",
- "csstype": "^3.1.3"
+ "@vue/reactivity": "3.5.29",
+ "@vue/runtime-core": "3.5.29",
+ "@vue/shared": "3.5.29",
+ "csstype": "^3.2.3"
}
},
"node_modules/@vue/server-renderer": {
- "version": "3.5.18",
- "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.18.tgz",
- "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==",
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.29.tgz",
+ "integrity": "sha512-G/1k6WK5MusLlbxSE2YTcqAAezS+VuwHhOvLx2KnQU7G2zCH6KIb+5Wyt6UjMq7a3qPzNEjJXs1hvAxDclQH+g==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-ssr": "3.5.18",
- "@vue/shared": "3.5.18"
+ "@vue/compiler-ssr": "3.5.29",
+ "@vue/shared": "3.5.29"
},
"peerDependencies": {
- "vue": "3.5.18"
+ "vue": "3.5.29"
}
},
"node_modules/@vue/shared": {
- "version": "3.5.18",
- "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.18.tgz",
- "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==",
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.29.tgz",
+ "integrity": "sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==",
"license": "MIT"
},
- "node_modules/@whatwg-node/disposablestack": {
- "version": "0.0.6",
- "resolved": "https://registry.npmmirror.com/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz",
- "integrity": "sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==",
+ "node_modules/@vueuse/core": {
+ "version": "13.9.0",
+ "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-13.9.0.tgz",
+ "integrity": "sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==",
"license": "MIT",
"dependencies": {
- "@whatwg-node/promise-helpers": "^1.0.0",
- "tslib": "^2.6.3"
+ "@types/web-bluetooth": "^0.0.21",
+ "@vueuse/metadata": "13.9.0",
+ "@vueuse/shared": "13.9.0"
},
- "engines": {
- "node": ">=18.0.0"
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "vue": "^3.5.0"
}
},
- "node_modules/@whatwg-node/fetch": {
- "version": "0.10.9",
- "resolved": "https://registry.npmmirror.com/@whatwg-node/fetch/-/fetch-0.10.9.tgz",
- "integrity": "sha512-2TaXKmjy53cybNtaAtzbPOzwIPkjXbzvZcimnaJxQwYXKSC8iYnWoZOyT4+CFt8w0KDieg5J5dIMNzUrW/UZ5g==",
+ "node_modules/@vueuse/metadata": {
+ "version": "13.9.0",
+ "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-13.9.0.tgz",
+ "integrity": "sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==",
"license": "MIT",
- "dependencies": {
- "@whatwg-node/node-fetch": "^0.7.22",
- "urlpattern-polyfill": "^10.0.0"
- },
- "engines": {
- "node": ">=18.0.0"
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
}
},
- "node_modules/@whatwg-node/fetch/node_modules/urlpattern-polyfill": {
- "version": "10.1.0",
- "resolved": "https://registry.npmmirror.com/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz",
- "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==",
- "license": "MIT"
- },
- "node_modules/@whatwg-node/node-fetch": {
- "version": "0.7.22",
- "resolved": "https://registry.npmmirror.com/@whatwg-node/node-fetch/-/node-fetch-0.7.22.tgz",
- "integrity": "sha512-h4GGjGF2vH3kGJ/fEOeg9Xfu4ncoyRwFcjGIxr/5dTBgZNVwq888byIsZ+XXRDJnNnRlzVVVQDcqrZpY2yctGA==",
+ "node_modules/@vueuse/motion": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmmirror.com/@vueuse/motion/-/motion-3.0.3.tgz",
+ "integrity": "sha512-4B+ITsxCI9cojikvrpaJcLXyq0spj3sdlzXjzesWdMRd99hhtFI6OJ/1JsqwtF73YooLe0hUn/xDR6qCtmn5GQ==",
"license": "MIT",
"dependencies": {
- "@fastify/busboy": "^3.1.1",
- "@whatwg-node/disposablestack": "^0.0.6",
- "@whatwg-node/promise-helpers": "^1.3.2",
- "tslib": "^2.6.3"
+ "@vueuse/core": "^13.0.0",
+ "@vueuse/shared": "^13.0.0",
+ "defu": "^6.1.4",
+ "framesync": "^6.1.2",
+ "popmotion": "^11.0.5",
+ "style-value-types": "^5.1.2"
},
- "engines": {
- "node": ">=18.0.0"
+ "optionalDependencies": {
+ "@nuxt/kit": "^3.13.0"
+ },
+ "peerDependencies": {
+ "vue": ">=3.0.0"
}
},
- "node_modules/@whatwg-node/promise-helpers": {
- "version": "1.3.2",
- "resolved": "https://registry.npmmirror.com/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz",
- "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==",
+ "node_modules/@vueuse/motion/node_modules/@nuxt/kit": {
+ "version": "3.21.0",
+ "resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-3.21.0.tgz",
+ "integrity": "sha512-KMTLK/dsGaQioZzkYUvgfN9le4grNW54aNcA1jqzgVZLcFVy4jJfrJr5WZio9NT2EMfajdoZ+V28aD7BRr4Zfw==",
"license": "MIT",
+ "optional": true,
"dependencies": {
- "tslib": "^2.6.3"
+ "c12": "^3.3.3",
+ "consola": "^3.4.2",
+ "defu": "^6.1.4",
+ "destr": "^2.0.5",
+ "errx": "^0.1.0",
+ "exsolve": "^1.0.8",
+ "ignore": "^7.0.5",
+ "jiti": "^2.6.1",
+ "klona": "^2.0.6",
+ "knitwork": "^1.3.0",
+ "mlly": "^1.8.0",
+ "ohash": "^2.0.11",
+ "pathe": "^2.0.3",
+ "pkg-types": "^2.3.0",
+ "rc9": "^2.1.2",
+ "scule": "^1.3.0",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ufo": "^1.6.3",
+ "unctx": "^2.5.0",
+ "untyped": "^2.0.0"
},
"engines": {
- "node": ">=16.0.0"
+ "node": ">=18.12.0"
}
},
- "node_modules/@whatwg-node/server": {
- "version": "0.9.71",
- "resolved": "https://registry.npmmirror.com/@whatwg-node/server/-/server-0.9.71.tgz",
- "integrity": "sha512-ueFCcIPaMgtuYDS9u0qlUoEvj6GiSsKrwnOLPp9SshqjtcRaR1IEHRjoReq3sXNydsF5i0ZnmuYgXq9dV53t0g==",
+ "node_modules/@vueuse/shared": {
+ "version": "13.9.0",
+ "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-13.9.0.tgz",
+ "integrity": "sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==",
"license": "MIT",
- "dependencies": {
- "@whatwg-node/disposablestack": "^0.0.6",
- "@whatwg-node/fetch": "^0.10.5",
- "@whatwg-node/promise-helpers": "^1.2.2",
- "tslib": "^2.6.3"
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
},
- "engines": {
- "node": ">=18.0.0"
+ "peerDependencies": {
+ "vue": "^3.5.0"
}
},
"node_modules/abbrev": {
"version": "3.0.1",
- "resolved": "https://registry.npmmirror.com/abbrev/-/abbrev-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz",
"integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==",
"license": "ISC",
"engines": {
@@ -4413,7 +4206,7 @@
},
"node_modules/abort-controller": {
"version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"license": "MIT",
"dependencies": {
@@ -4436,31 +4229,10 @@
"node": ">= 0.6"
}
},
- "node_modules/accepts/node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/accepts/node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "license": "MIT",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -4471,7 +4243,7 @@
},
"node_modules/acorn-import-attributes": {
"version": "1.9.5",
- "resolved": "https://registry.npmmirror.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
+ "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
"integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
"license": "MIT",
"peerDependencies": {
@@ -4480,13 +4252,19 @@
},
"node_modules/agent-base": {
"version": "7.1.4",
- "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
+ "node_modules/alien-signals": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.2.tgz",
+ "integrity": "sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==",
+ "license": "MIT"
+ },
"node_modules/ansi-regex": {
"version": "6.1.0",
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.1.0.tgz",
@@ -4512,9 +4290,9 @@
}
},
"node_modules/ansis": {
- "version": "4.1.0",
- "resolved": "https://registry.npmmirror.com/ansis/-/ansis-4.1.0.tgz",
- "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz",
+ "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==",
"license": "ISC",
"engines": {
"node": ">=14"
@@ -4553,7 +4331,7 @@
},
"node_modules/archiver": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/archiver/-/archiver-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
"integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==",
"license": "MIT",
"dependencies": {
@@ -4571,7 +4349,7 @@
},
"node_modules/archiver-utils": {
"version": "5.0.2",
- "resolved": "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-5.0.2.tgz",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz",
"integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==",
"license": "MIT",
"dependencies": {
@@ -4589,7 +4367,7 @@
},
"node_modules/archiver-utils/node_modules/is-stream": {
"version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"license": "MIT",
"engines": {
@@ -4606,41 +4384,32 @@
"license": "MIT"
},
"node_modules/ast-kit": {
- "version": "2.1.1",
- "resolved": "https://registry.npmmirror.com/ast-kit/-/ast-kit-2.1.1.tgz",
- "integrity": "sha512-mfh6a7gKXE8pDlxTvqIc/syH/P3RkzbOF6LeHdcKztLEzYe6IMsRCL7N8vI7hqTGWNxpkCuuRTpT21xNWqhRtQ==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz",
+ "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.27.7",
+ "@babel/parser": "^7.28.5",
"pathe": "^2.0.3"
},
"engines": {
- "node": ">=20.18.0"
+ "node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/sponsors/sxzz"
}
},
- "node_modules/ast-module-types": {
- "version": "6.0.1",
- "resolved": "https://registry.npmmirror.com/ast-module-types/-/ast-module-types-6.0.1.tgz",
- "integrity": "sha512-WHw67kLXYbZuHTmcdbIrVArCq5wxo6NEuj3hiYAWr8mwJeC+C2mMCIBIWCiDoCye/OF/xelc+teJ1ERoWmnEIA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/ast-walker-scope": {
- "version": "0.8.1",
- "resolved": "https://registry.npmmirror.com/ast-walker-scope/-/ast-walker-scope-0.8.1.tgz",
- "integrity": "sha512-72XOdbzQCMKERvFrxAykatn2pu7osPNq/sNUzwcHdWzwPvOsNpPqkawfDXVvQbA2RT+ivtsMNjYdojTUZitt1A==",
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.8.3.tgz",
+ "integrity": "sha512-cbdCP0PGOBq0ASG+sjnKIoYkWMKhhz+F/h9pRexUdX2Hd38+WOlBkRKlqkGOSm0YQpcFMQBJeK4WspUAkwsEdg==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.27.2",
- "ast-kit": "^2.0.0"
+ "@babel/parser": "^7.28.4",
+ "ast-kit": "^2.1.3"
},
"engines": {
- "node": ">=20.18.0"
+ "node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/sponsors/sxzz"
@@ -4654,13 +4423,13 @@
},
"node_modules/async-sema": {
"version": "3.1.1",
- "resolved": "https://registry.npmmirror.com/async-sema/-/async-sema-3.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz",
"integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==",
"license": "MIT"
},
"node_modules/asynckit": {
"version": "0.4.0",
- "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
@@ -4674,9 +4443,9 @@
}
},
"node_modules/autoprefixer": {
- "version": "10.4.21",
- "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz",
- "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
+ "version": "10.4.27",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz",
+ "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==",
"funding": [
{
"type": "opencollective",
@@ -4693,10 +4462,9 @@
],
"license": "MIT",
"dependencies": {
- "browserslist": "^4.24.4",
- "caniuse-lite": "^1.0.30001702",
- "fraction.js": "^4.3.7",
- "normalize-range": "^0.1.2",
+ "browserslist": "^4.28.1",
+ "caniuse-lite": "^1.0.30001774",
+ "fraction.js": "^5.3.4",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
},
@@ -4711,21 +4479,29 @@
}
},
"node_modules/axios": {
- "version": "1.11.0",
- "resolved": "https://registry.npmmirror.com/axios/-/axios-1.11.0.tgz",
- "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
+ "version": "1.13.6",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
+ "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
"license": "MIT",
"dependencies": {
- "follow-redirects": "^1.15.6",
- "form-data": "^4.0.4",
+ "follow-redirects": "^1.15.11",
+ "form-data": "^4.0.5",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/b4a": {
- "version": "1.6.7",
- "resolved": "https://registry.npmmirror.com/b4a/-/b4a-1.6.7.tgz",
- "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==",
- "license": "Apache-2.0"
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz",
+ "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==",
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react-native-b4a": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-b4a": {
+ "optional": true
+ }
+ }
},
"node_modules/balanced-match": {
"version": "1.0.2",
@@ -4734,15 +4510,22 @@
"license": "MIT"
},
"node_modules/bare-events": {
- "version": "2.6.0",
- "resolved": "https://registry.npmmirror.com/bare-events/-/bare-events-2.6.0.tgz",
- "integrity": "sha512-EKZ5BTXYExaNqi3I3f9RtEsaI/xBSGjE0XZCZilPzFAV/goswFHuPd9jEZlPIZ/iNZJwDSao9qRiScySz7MbQg==",
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz",
+ "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==",
"license": "Apache-2.0",
- "optional": true
+ "peerDependencies": {
+ "bare-abort-controller": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ }
+ }
},
"node_modules/base64-js": {
"version": "1.5.1",
- "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
@@ -4760,6 +4543,18 @@
],
"license": "MIT"
},
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
+ "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -4774,7 +4569,7 @@
},
"node_modules/bindings": {
"version": "1.5.0",
- "resolved": "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"license": "MIT",
"dependencies": {
@@ -4782,9 +4577,9 @@
}
},
"node_modules/birpc": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.5.0.tgz",
- "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==",
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
+ "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
@@ -4792,7 +4587,7 @@
},
"node_modules/boolbase": {
"version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"license": "ISC"
},
@@ -4818,9 +4613,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.25.1",
- "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.25.1.tgz",
- "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"funding": [
{
"type": "opencollective",
@@ -4837,10 +4632,11 @@
],
"license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001726",
- "electron-to-chromium": "^1.5.173",
- "node-releases": "^2.0.19",
- "update-browserslist-db": "^1.1.3"
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
},
"bin": {
"browserslist": "cli.js"
@@ -4851,7 +4647,7 @@
},
"node_modules/buffer": {
"version": "6.0.3",
- "resolved": "https://registry.npmmirror.com/buffer/-/buffer-6.0.3.tgz",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
@@ -4875,7 +4671,7 @@
},
"node_modules/buffer-crc32": {
"version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
"license": "MIT",
"engines": {
@@ -4884,25 +4680,13 @@
},
"node_modules/buffer-from": {
"version": "1.1.2",
- "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"license": "MIT"
},
- "node_modules/builtin-modules": {
- "version": "3.3.0",
- "resolved": "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz",
- "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/bundle-name": {
"version": "4.1.0",
- "resolved": "https://registry.npmmirror.com/bundle-name/-/bundle-name-4.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
"integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
"license": "MIT",
"dependencies": {
@@ -4916,26 +4700,26 @@
}
},
"node_modules/c12": {
- "version": "3.1.0",
- "resolved": "https://registry.npmmirror.com/c12/-/c12-3.1.0.tgz",
- "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==",
+ "version": "3.3.3",
+ "resolved": "https://registry.npmmirror.com/c12/-/c12-3.3.3.tgz",
+ "integrity": "sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q==",
"license": "MIT",
"dependencies": {
- "chokidar": "^4.0.3",
+ "chokidar": "^5.0.0",
"confbox": "^0.2.2",
"defu": "^6.1.4",
- "dotenv": "^16.6.1",
- "exsolve": "^1.0.7",
+ "dotenv": "^17.2.3",
+ "exsolve": "^1.0.8",
"giget": "^2.0.0",
- "jiti": "^2.4.2",
+ "jiti": "^2.6.1",
"ohash": "^2.0.11",
"pathe": "^2.0.3",
- "perfect-debounce": "^1.0.0",
- "pkg-types": "^2.2.0",
+ "perfect-debounce": "^2.0.0",
+ "pkg-types": "^2.3.0",
"rc9": "^2.1.2"
},
"peerDependencies": {
- "magicast": "^0.3.5"
+ "magicast": "*"
},
"peerDependenciesMeta": {
"magicast": {
@@ -4943,9 +4727,27 @@
}
}
},
+ "node_modules/c12/node_modules/dotenv": {
+ "version": "17.2.3",
+ "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-17.2.3.tgz",
+ "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/c12/node_modules/perfect-debounce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
+ "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==",
+ "license": "MIT"
+ },
"node_modules/cac": {
"version": "6.7.14",
- "resolved": "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
"license": "MIT",
"engines": {
@@ -4965,27 +4767,6 @@
"node": ">= 6.0.0"
}
},
- "node_modules/cache-content-type/node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/cache-content-type/node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "license": "MIT",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -5015,14 +4796,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/callsite": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/callsite/-/callsite-1.0.0.tgz",
- "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==",
- "engines": {
- "node": "*"
- }
- },
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz",
@@ -5034,7 +4807,7 @@
},
"node_modules/caniuse-api": {
"version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/caniuse-api/-/caniuse-api-3.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
"integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
"license": "MIT",
"dependencies": {
@@ -5045,9 +4818,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001727",
- "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz",
- "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==",
+ "version": "1.0.30001774",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz",
+ "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==",
"funding": [
{
"type": "opencollective",
@@ -5120,15 +4893,15 @@
}
},
"node_modules/chokidar": {
- "version": "4.0.3",
- "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz",
- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
+ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
"license": "MIT",
"dependencies": {
- "readdirp": "^4.0.1"
+ "readdirp": "^5.0.0"
},
"engines": {
- "node": ">= 14.16.0"
+ "node": ">= 20.19.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
@@ -5136,7 +4909,7 @@
},
"node_modules/chownr": {
"version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/chownr/-/chownr-3.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
"license": "BlueOak-1.0.0",
"engines": {
@@ -5154,7 +4927,7 @@
},
"node_modules/clipboardy": {
"version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-4.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz",
"integrity": "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==",
"license": "MIT",
"dependencies": {
@@ -5270,7 +5043,7 @@
},
"node_modules/cluster-key-slot": {
"version": "1.1.2",
- "resolved": "https://registry.npmmirror.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
+ "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"license": "Apache-2.0",
"engines": {
@@ -5287,66 +5060,21 @@
"node": ">= 0.12.0"
}
},
- "node_modules/color": {
- "version": "3.2.1",
- "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz",
- "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
- "license": "MIT",
- "dependencies": {
- "color-convert": "^1.9.3",
- "color-string": "^1.6.0"
- }
- },
- "node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "license": "MIT",
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/color-convert/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "license": "MIT"
- },
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
- "node_modules/color-string": {
- "version": "1.9.1",
- "resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz",
- "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
- "license": "MIT",
- "dependencies": {
- "color-name": "^1.0.0",
- "simple-swizzle": "^0.2.2"
- }
- },
"node_modules/colord": {
"version": "2.9.3",
- "resolved": "https://registry.npmmirror.com/colord/-/colord-2.9.3.tgz",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
"integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
"license": "MIT"
},
- "node_modules/colorspace": {
- "version": "1.1.4",
- "resolved": "https://registry.npmmirror.com/colorspace/-/colorspace-1.1.4.tgz",
- "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
- "license": "MIT",
- "dependencies": {
- "color": "^3.1.3",
- "text-hex": "1.0.x"
- }
- },
"node_modules/combined-stream": {
"version": "1.0.8",
- "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
@@ -5357,35 +5085,31 @@
}
},
"node_modules/commander": {
- "version": "10.0.1",
- "resolved": "https://registry.npmmirror.com/commander/-/commander-10.0.1.tgz",
- "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "version": "13.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
+ "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
"license": "MIT",
+ "optional": true,
+ "peer": true,
"engines": {
- "node": ">=14"
+ "node": ">=18"
}
},
- "node_modules/common-path-prefix": {
- "version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz",
- "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==",
- "license": "ISC"
- },
"node_modules/commondir": {
"version": "1.0.1",
- "resolved": "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"license": "MIT"
},
"node_modules/compatx": {
"version": "0.2.0",
- "resolved": "https://registry.npmmirror.com/compatx/-/compatx-0.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/compatx/-/compatx-0.2.0.tgz",
"integrity": "sha512-6gLRNt4ygsi5NyMVhceOCFv14CIdDFN7fQjX1U4+47qVE/+kjPoXMK65KWK+dWxmFzMTuKazoQ9sch6pM0p5oA==",
"license": "MIT"
},
"node_modules/compress-commons": {
"version": "6.0.2",
- "resolved": "https://registry.npmmirror.com/compress-commons/-/compress-commons-6.0.2.tgz",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz",
"integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==",
"license": "MIT",
"dependencies": {
@@ -5401,7 +5125,7 @@
},
"node_modules/compress-commons/node_modules/is-stream": {
"version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"license": "MIT",
"engines": {
@@ -5418,9 +5142,9 @@
"license": "MIT"
},
"node_modules/confbox": {
- "version": "0.2.2",
- "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz",
- "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz",
+ "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==",
"license": "MIT"
},
"node_modules/consola": {
@@ -5455,22 +5179,13 @@
},
"node_modules/convert-source-map": {
"version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"license": "MIT"
},
- "node_modules/cookie": {
- "version": "1.0.2",
- "resolved": "https://registry.npmmirror.com/cookie/-/cookie-1.0.2.tgz",
- "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/cookie-es": {
"version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/cookie-es/-/cookie-es-2.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.0.tgz",
"integrity": "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==",
"license": "MIT"
},
@@ -5502,31 +5217,23 @@
"url": "https://github.com/sponsors/mesqueeb"
}
},
- "node_modules/copy-file": {
- "version": "11.0.0",
- "resolved": "https://registry.npmmirror.com/copy-file/-/copy-file-11.0.0.tgz",
- "integrity": "sha512-mFsNh/DIANLqFt5VHZoGirdg7bK5+oTWlhnGu6tgRhzBlnEKWaPX2xrFaLltii/6rmhqFMJqffUgknuRdpYlHw==",
- "license": "MIT",
+ "node_modules/copy-paste": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/copy-paste/-/copy-paste-2.2.0.tgz",
+ "integrity": "sha512-jqSL4r9DSeiIvJZStLzY/sMLt9ToTM7RsK237lYOTG+KcbQJHGala3R1TUpa8h1p9adswVgIdV4qGbseVhL4lg==",
"dependencies": {
- "graceful-fs": "^4.2.11",
- "p-event": "^6.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "iconv-lite": "^0.4.8"
}
},
"node_modules/core-util-is": {
"version": "1.0.3",
- "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
"node_modules/crc-32": {
"version": "1.2.2",
- "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
"license": "Apache-2.0",
"bin": {
@@ -5538,7 +5245,7 @@
},
"node_modules/crc32-stream": {
"version": "6.0.0",
- "resolved": "https://registry.npmmirror.com/crc32-stream/-/crc32-stream-6.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz",
"integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==",
"license": "MIT",
"dependencies": {
@@ -5549,21 +5256,9 @@
"node": ">= 14"
}
},
- "node_modules/cron-parser": {
- "version": "4.9.0",
- "resolved": "https://registry.npmmirror.com/cron-parser/-/cron-parser-4.9.0.tgz",
- "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==",
- "license": "MIT",
- "dependencies": {
- "luxon": "^3.2.1"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
"node_modules/croner": {
"version": "9.1.0",
- "resolved": "https://registry.npmmirror.com/croner/-/croner-9.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/croner/-/croner-9.1.0.tgz",
"integrity": "sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==",
"license": "MIT",
"engines": {
@@ -5615,9 +5310,9 @@
}
},
"node_modules/css-declaration-sorter": {
- "version": "7.2.0",
- "resolved": "https://registry.npmmirror.com/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz",
- "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.3.1.tgz",
+ "integrity": "sha512-gz6x+KkgNCjxq3Var03pRYLhyNfwhkKF1g/yoLgDNtFvVu0/fOLV9C8fFEZRjACp/XQLumjAYo7JVjzH3wLbxA==",
"license": "ISC",
"engines": {
"node": "^14 || ^16 || >=18"
@@ -5628,7 +5323,7 @@
},
"node_modules/css-select": {
"version": "5.2.2",
- "resolved": "https://registry.npmmirror.com/css-select/-/css-select-5.2.2.tgz",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
"integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
"license": "BSD-2-Clause",
"dependencies": {
@@ -5644,7 +5339,7 @@
},
"node_modules/css-tree": {
"version": "3.1.0",
- "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-3.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
"integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
"license": "MIT",
"dependencies": {
@@ -5657,7 +5352,7 @@
},
"node_modules/css-what": {
"version": "6.2.2",
- "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.2.2.tgz",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
"integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
"license": "BSD-2-Clause",
"engines": {
@@ -5680,12 +5375,12 @@
}
},
"node_modules/cssnano": {
- "version": "7.1.0",
- "resolved": "https://registry.npmmirror.com/cssnano/-/cssnano-7.1.0.tgz",
- "integrity": "sha512-Pu3rlKkd0ZtlCUzBrKL1Z4YmhKppjC1H9jo7u1o4qaKqyhvixFgu5qLyNIAOjSTg9DjVPtUqdROq2EfpVMEe+w==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.2.tgz",
+ "integrity": "sha512-HYOPBsNvoiFeR1eghKD5C3ASm64v9YVyJB4Ivnl2gqKoQYvjjN/G0rztvKQq8OxocUtC6sjqY8jwYngIB4AByA==",
"license": "MIT",
"dependencies": {
- "cssnano-preset-default": "^7.0.8",
+ "cssnano-preset-default": "^7.0.10",
"lilconfig": "^3.1.3"
},
"engines": {
@@ -5700,26 +5395,26 @@
}
},
"node_modules/cssnano-preset-default": {
- "version": "7.0.8",
- "resolved": "https://registry.npmmirror.com/cssnano-preset-default/-/cssnano-preset-default-7.0.8.tgz",
- "integrity": "sha512-d+3R2qwrUV3g4LEMOjnndognKirBZISylDZAF/TPeCWVjEwlXS2e4eN4ICkoobRe7pD3H6lltinKVyS1AJhdjQ==",
+ "version": "7.0.10",
+ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.10.tgz",
+ "integrity": "sha512-6ZBjW0Lf1K1Z+0OKUAUpEN62tSXmYChXWi2NAA0afxEVsj9a+MbcB1l5qel6BHJHmULai2fCGRthCeKSFbScpA==",
"license": "MIT",
"dependencies": {
- "browserslist": "^4.25.1",
+ "browserslist": "^4.27.0",
"css-declaration-sorter": "^7.2.0",
"cssnano-utils": "^5.0.1",
"postcss-calc": "^10.1.1",
- "postcss-colormin": "^7.0.4",
- "postcss-convert-values": "^7.0.6",
- "postcss-discard-comments": "^7.0.4",
+ "postcss-colormin": "^7.0.5",
+ "postcss-convert-values": "^7.0.8",
+ "postcss-discard-comments": "^7.0.5",
"postcss-discard-duplicates": "^7.0.2",
"postcss-discard-empty": "^7.0.1",
"postcss-discard-overridden": "^7.0.1",
"postcss-merge-longhand": "^7.0.5",
- "postcss-merge-rules": "^7.0.6",
+ "postcss-merge-rules": "^7.0.7",
"postcss-minify-font-values": "^7.0.1",
"postcss-minify-gradients": "^7.0.1",
- "postcss-minify-params": "^7.0.4",
+ "postcss-minify-params": "^7.0.5",
"postcss-minify-selectors": "^7.0.5",
"postcss-normalize-charset": "^7.0.1",
"postcss-normalize-display-values": "^7.0.1",
@@ -5727,11 +5422,11 @@
"postcss-normalize-repeat-style": "^7.0.1",
"postcss-normalize-string": "^7.0.1",
"postcss-normalize-timing-functions": "^7.0.1",
- "postcss-normalize-unicode": "^7.0.4",
+ "postcss-normalize-unicode": "^7.0.5",
"postcss-normalize-url": "^7.0.1",
"postcss-normalize-whitespace": "^7.0.1",
"postcss-ordered-values": "^7.0.2",
- "postcss-reduce-initial": "^7.0.4",
+ "postcss-reduce-initial": "^7.0.5",
"postcss-reduce-transforms": "^7.0.1",
"postcss-svgo": "^7.1.0",
"postcss-unique-selectors": "^7.0.4"
@@ -5745,7 +5440,7 @@
},
"node_modules/cssnano-utils": {
"version": "5.0.1",
- "resolved": "https://registry.npmmirror.com/cssnano-utils/-/cssnano-utils-5.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.1.tgz",
"integrity": "sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg==",
"license": "MIT",
"engines": {
@@ -5757,7 +5452,7 @@
},
"node_modules/csso": {
"version": "5.0.5",
- "resolved": "https://registry.npmmirror.com/csso/-/csso-5.0.5.tgz",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
"license": "MIT",
"dependencies": {
@@ -5770,7 +5465,7 @@
},
"node_modules/csso/node_modules/css-tree": {
"version": "2.2.1",
- "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-2.2.1.tgz",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
"license": "MIT",
"dependencies": {
@@ -5784,29 +5479,20 @@
},
"node_modules/csso/node_modules/mdn-data": {
"version": "2.0.28",
- "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.28.tgz",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
"license": "CC0-1.0"
},
"node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
- "node_modules/data-uri-to-buffer": {
- "version": "4.0.1",
- "resolved": "https://registry.npmmirror.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
- "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
- "license": "MIT",
- "engines": {
- "node": ">= 12"
- }
- },
"node_modules/db0": {
- "version": "0.3.2",
- "resolved": "https://registry.npmmirror.com/db0/-/db0-0.3.2.tgz",
- "integrity": "sha512-xzWNQ6jk/+NtdfLyXEipbX55dmDSeteLFt/ayF+wZUU5bzKgmrDOxmInUTbyVRp46YwnJdkDA1KhB7WIXFofJw==",
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/db0/-/db0-0.3.4.tgz",
+ "integrity": "sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==",
"license": "MIT",
"peerDependencies": {
"@electric-sql/pglite": "*",
@@ -5838,9 +5524,9 @@
}
},
"node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -5854,15 +5540,6 @@
}
}
},
- "node_modules/decache": {
- "version": "4.6.2",
- "resolved": "https://registry.npmmirror.com/decache/-/decache-4.6.2.tgz",
- "integrity": "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==",
- "license": "MIT",
- "dependencies": {
- "callsite": "^1.0.0"
- }
- },
"node_modules/deep-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/deep-equal/-/deep-equal-1.0.1.tgz",
@@ -5871,7 +5548,7 @@
},
"node_modules/deepmerge": {
"version": "4.3.1",
- "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"license": "MIT",
"engines": {
@@ -5879,9 +5556,9 @@
}
},
"node_modules/default-browser": {
- "version": "5.2.1",
- "resolved": "https://registry.npmmirror.com/default-browser/-/default-browser-5.2.1.tgz",
- "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz",
+ "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==",
"license": "MIT",
"dependencies": {
"bundle-name": "^4.1.0",
@@ -5895,9 +5572,9 @@
}
},
"node_modules/default-browser-id": {
- "version": "5.0.0",
- "resolved": "https://registry.npmmirror.com/default-browser-id/-/default-browser-id-5.0.0.tgz",
- "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
+ "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -5908,7 +5585,7 @@
},
"node_modules/define-lazy-prop": {
"version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
"license": "MIT",
"engines": {
@@ -5923,7 +5600,7 @@
},
"node_modules/delayed-stream": {
"version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
@@ -5938,7 +5615,7 @@
},
"node_modules/denque": {
"version": "2.1.0",
- "resolved": "https://registry.npmmirror.com/denque/-/denque-2.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"license": "Apache-2.0",
"engines": {
@@ -5971,153 +5648,18 @@
}
},
"node_modules/detect-libc": {
- "version": "1.0.3",
- "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
- "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"license": "Apache-2.0",
- "bin": {
- "detect-libc": "bin/detect-libc.js"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/detective-amd": {
- "version": "6.0.1",
- "resolved": "https://registry.npmmirror.com/detective-amd/-/detective-amd-6.0.1.tgz",
- "integrity": "sha512-TtyZ3OhwUoEEIhTFoc1C9IyJIud3y+xYkSRjmvCt65+ycQuc3VcBrPRTMWoO/AnuCyOB8T5gky+xf7Igxtjd3g==",
- "license": "MIT",
- "dependencies": {
- "ast-module-types": "^6.0.1",
- "escodegen": "^2.1.0",
- "get-amd-module-type": "^6.0.1",
- "node-source-walk": "^7.0.1"
- },
- "bin": {
- "detective-amd": "bin/cli.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/detective-cjs": {
- "version": "6.0.1",
- "resolved": "https://registry.npmmirror.com/detective-cjs/-/detective-cjs-6.0.1.tgz",
- "integrity": "sha512-tLTQsWvd2WMcmn/60T2inEJNhJoi7a//PQ7DwRKEj1yEeiQs4mrONgsUtEJKnZmrGWBBmE0kJ1vqOG/NAxwaJw==",
- "license": "MIT",
- "dependencies": {
- "ast-module-types": "^6.0.1",
- "node-source-walk": "^7.0.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/detective-es6": {
- "version": "5.0.1",
- "resolved": "https://registry.npmmirror.com/detective-es6/-/detective-es6-5.0.1.tgz",
- "integrity": "sha512-XusTPuewnSUdoxRSx8OOI6xIA/uld/wMQwYsouvFN2LAg7HgP06NF1lHRV3x6BZxyL2Kkoih4ewcq8hcbGtwew==",
- "license": "MIT",
- "dependencies": {
- "node-source-walk": "^7.0.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/detective-postcss": {
- "version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/detective-postcss/-/detective-postcss-7.0.1.tgz",
- "integrity": "sha512-bEOVpHU9picRZux5XnwGsmCN4+8oZo7vSW0O0/Enq/TO5R2pIAP2279NsszpJR7ocnQt4WXU0+nnh/0JuK4KHQ==",
- "license": "MIT",
- "dependencies": {
- "is-url": "^1.2.4",
- "postcss-values-parser": "^6.0.2"
- },
- "engines": {
- "node": "^14.0.0 || >=16.0.0"
- },
- "peerDependencies": {
- "postcss": "^8.4.47"
- }
- },
- "node_modules/detective-sass": {
- "version": "6.0.1",
- "resolved": "https://registry.npmmirror.com/detective-sass/-/detective-sass-6.0.1.tgz",
- "integrity": "sha512-jSGPO8QDy7K7pztUmGC6aiHkexBQT4GIH+mBAL9ZyBmnUIOFbkfZnO8wPRRJFP/QP83irObgsZHCoDHZ173tRw==",
- "license": "MIT",
- "dependencies": {
- "gonzales-pe": "^4.3.0",
- "node-source-walk": "^7.0.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/detective-scss": {
- "version": "5.0.1",
- "resolved": "https://registry.npmmirror.com/detective-scss/-/detective-scss-5.0.1.tgz",
- "integrity": "sha512-MAyPYRgS6DCiS6n6AoSBJXLGVOydsr9huwXORUlJ37K3YLyiN0vYHpzs3AdJOgHobBfispokoqrEon9rbmKacg==",
- "license": "MIT",
- "dependencies": {
- "gonzales-pe": "^4.3.0",
- "node-source-walk": "^7.0.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/detective-stylus": {
- "version": "5.0.1",
- "resolved": "https://registry.npmmirror.com/detective-stylus/-/detective-stylus-5.0.1.tgz",
- "integrity": "sha512-Dgn0bUqdGbE3oZJ+WCKf8Dmu7VWLcmRJGc6RCzBgG31DLIyai9WAoEhYRgIHpt/BCRMrnXLbGWGPQuBUrnF0TA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/detective-typescript": {
- "version": "14.0.0",
- "resolved": "https://registry.npmmirror.com/detective-typescript/-/detective-typescript-14.0.0.tgz",
- "integrity": "sha512-pgN43/80MmWVSEi5LUuiVvO/0a9ss5V7fwVfrJ4QzAQRd3cwqU1SfWGXJFcNKUqoD5cS+uIovhw5t/0rSeC5Mw==",
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/typescript-estree": "^8.23.0",
- "ast-module-types": "^6.0.1",
- "node-source-walk": "^7.0.1"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "typescript": "^5.4.4"
- }
- },
- "node_modules/detective-vue2": {
- "version": "2.2.0",
- "resolved": "https://registry.npmmirror.com/detective-vue2/-/detective-vue2-2.2.0.tgz",
- "integrity": "sha512-sVg/t6O2z1zna8a/UIV6xL5KUa2cMTQbdTIIvqNM0NIPswp52fe43Nwmbahzj3ww4D844u/vC2PYfiGLvD3zFA==",
- "license": "MIT",
- "dependencies": {
- "@dependents/detective-less": "^5.0.1",
- "@vue/compiler-sfc": "^3.5.13",
- "detective-es6": "^5.0.1",
- "detective-sass": "^6.0.1",
- "detective-scss": "^5.0.1",
- "detective-stylus": "^5.0.1",
- "detective-typescript": "^14.0.0"
- },
"engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "typescript": "^5.4.4"
+ "node": ">=8"
}
},
"node_modules/devalue": {
- "version": "5.1.1",
- "resolved": "https://registry.npmmirror.com/devalue/-/devalue-5.1.1.tgz",
- "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==",
+ "version": "5.6.3",
+ "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.3.tgz",
+ "integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==",
"license": "MIT"
},
"node_modules/didyoumean": {
@@ -6127,9 +5669,9 @@
"license": "Apache-2.0"
},
"node_modules/diff": {
- "version": "8.0.2",
- "resolved": "https://registry.npmmirror.com/diff/-/diff-8.0.2.tgz",
- "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==",
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
+ "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
@@ -6143,7 +5685,7 @@
},
"node_modules/dom-serializer": {
"version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
"license": "MIT",
"dependencies": {
@@ -6155,9 +5697,21 @@
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
+ "node_modules/dom-serializer/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/domelementtype": {
"version": "2.3.0",
- "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
"integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
"funding": [
{
@@ -6169,7 +5723,7 @@
},
"node_modules/domhandler": {
"version": "5.0.3",
- "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
"license": "BSD-2-Clause",
"dependencies": {
@@ -6184,7 +5738,7 @@
},
"node_modules/domutils": {
"version": "3.2.2",
- "resolved": "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
"integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
"license": "BSD-2-Clause",
"dependencies": {
@@ -6197,32 +5751,20 @@
}
},
"node_modules/dot-prop": {
- "version": "9.0.0",
- "resolved": "https://registry.npmmirror.com/dot-prop/-/dot-prop-9.0.0.tgz",
- "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-10.1.0.tgz",
+ "integrity": "sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q==",
"license": "MIT",
"dependencies": {
- "type-fest": "^4.18.2"
+ "type-fest": "^5.0.0"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/dotenv": {
- "version": "16.6.1",
- "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz",
- "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://dotenvx.com"
- }
- },
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -6239,7 +5781,7 @@
},
"node_modules/duplexer": {
"version": "0.1.2",
- "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
"license": "MIT"
},
@@ -6256,9 +5798,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.191",
- "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.191.tgz",
- "integrity": "sha512-xcwe9ELcuxYLUFqZZxL19Z6HVKcvNkIwhbHUz7L3us6u12yR+7uY89dSl570f/IqNthx8dAw3tojG7i4Ni4tDA==",
+ "version": "1.5.302",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz",
+ "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==",
"license": "ISC"
},
"node_modules/emoji-regex": {
@@ -6267,34 +5809,19 @@
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"license": "MIT"
},
- "node_modules/enabled": {
- "version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/enabled/-/enabled-2.0.0.tgz",
- "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==",
- "license": "MIT"
- },
"node_modules/encodeurl": {
"version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
- "node_modules/end-of-stream": {
- "version": "1.4.5",
- "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz",
- "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
- "license": "MIT",
- "dependencies": {
- "once": "^1.4.0"
- }
- },
"node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -6303,21 +5830,9 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
- "node_modules/env-paths": {
- "version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/env-paths/-/env-paths-3.0.0.tgz",
- "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==",
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/error-stack-parser-es": {
"version": "1.0.5",
- "resolved": "https://registry.npmmirror.com/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz",
+ "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz",
"integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==",
"license": "MIT",
"funding": {
@@ -6349,9 +5864,9 @@
}
},
"node_modules/es-module-lexer": {
- "version": "1.7.0",
- "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
- "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
+ "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
"license": "MIT"
},
"node_modules/es-object-atoms": {
@@ -6368,7 +5883,7 @@
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
- "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
@@ -6382,9 +5897,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.25.8",
- "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.8.tgz",
- "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
+ "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -6394,32 +5909,32 @@
"node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.25.8",
- "@esbuild/android-arm": "0.25.8",
- "@esbuild/android-arm64": "0.25.8",
- "@esbuild/android-x64": "0.25.8",
- "@esbuild/darwin-arm64": "0.25.8",
- "@esbuild/darwin-x64": "0.25.8",
- "@esbuild/freebsd-arm64": "0.25.8",
- "@esbuild/freebsd-x64": "0.25.8",
- "@esbuild/linux-arm": "0.25.8",
- "@esbuild/linux-arm64": "0.25.8",
- "@esbuild/linux-ia32": "0.25.8",
- "@esbuild/linux-loong64": "0.25.8",
- "@esbuild/linux-mips64el": "0.25.8",
- "@esbuild/linux-ppc64": "0.25.8",
- "@esbuild/linux-riscv64": "0.25.8",
- "@esbuild/linux-s390x": "0.25.8",
- "@esbuild/linux-x64": "0.25.8",
- "@esbuild/netbsd-arm64": "0.25.8",
- "@esbuild/netbsd-x64": "0.25.8",
- "@esbuild/openbsd-arm64": "0.25.8",
- "@esbuild/openbsd-x64": "0.25.8",
- "@esbuild/openharmony-arm64": "0.25.8",
- "@esbuild/sunos-x64": "0.25.8",
- "@esbuild/win32-arm64": "0.25.8",
- "@esbuild/win32-ia32": "0.25.8",
- "@esbuild/win32-x64": "0.25.8"
+ "@esbuild/aix-ppc64": "0.27.3",
+ "@esbuild/android-arm": "0.27.3",
+ "@esbuild/android-arm64": "0.27.3",
+ "@esbuild/android-x64": "0.27.3",
+ "@esbuild/darwin-arm64": "0.27.3",
+ "@esbuild/darwin-x64": "0.27.3",
+ "@esbuild/freebsd-arm64": "0.27.3",
+ "@esbuild/freebsd-x64": "0.27.3",
+ "@esbuild/linux-arm": "0.27.3",
+ "@esbuild/linux-arm64": "0.27.3",
+ "@esbuild/linux-ia32": "0.27.3",
+ "@esbuild/linux-loong64": "0.27.3",
+ "@esbuild/linux-mips64el": "0.27.3",
+ "@esbuild/linux-ppc64": "0.27.3",
+ "@esbuild/linux-riscv64": "0.27.3",
+ "@esbuild/linux-s390x": "0.27.3",
+ "@esbuild/linux-x64": "0.27.3",
+ "@esbuild/netbsd-arm64": "0.27.3",
+ "@esbuild/netbsd-x64": "0.27.3",
+ "@esbuild/openbsd-arm64": "0.27.3",
+ "@esbuild/openbsd-x64": "0.27.3",
+ "@esbuild/openharmony-arm64": "0.27.3",
+ "@esbuild/sunos-x64": "0.27.3",
+ "@esbuild/win32-arm64": "0.27.3",
+ "@esbuild/win32-ia32": "0.27.3",
+ "@esbuild/win32-x64": "0.27.3"
}
},
"node_modules/escalade": {
@@ -6449,71 +5964,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/escodegen": {
- "version": "2.1.0",
- "resolved": "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz",
- "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "esprima": "^4.0.1",
- "estraverse": "^5.2.0",
- "esutils": "^2.0.2"
- },
- "bin": {
- "escodegen": "bin/escodegen.js",
- "esgenerate": "bin/esgenerate.js"
- },
- "engines": {
- "node": ">=6.0"
- },
- "optionalDependencies": {
- "source-map": "~0.6.1"
- }
- },
- "node_modules/escodegen/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "license": "BSD-3-Clause",
- "optional": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/eslint-visitor-keys": {
- "version": "4.2.1",
- "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
- "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
- "license": "BSD-2-Clause",
- "bin": {
- "esparse": "bin/esparse.js",
- "esvalidate": "bin/esvalidate.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=4.0"
- }
- },
"node_modules/estree-walker": {
"version": "3.0.3",
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
@@ -6523,18 +5973,9 @@
"@types/estree": "^1.0.0"
}
},
- "node_modules/esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/etag": {
"version": "1.8.1",
- "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"license": "MIT",
"engines": {
@@ -6543,7 +5984,7 @@
},
"node_modules/event-target-shim": {
"version": "5.0.1",
- "resolved": "https://registry.npmmirror.com/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"license": "MIT",
"engines": {
@@ -6552,16 +5993,25 @@
},
"node_modules/events": {
"version": "3.3.0",
- "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"license": "MIT",
"engines": {
"node": ">=0.8.x"
}
},
+ "node_modules/events-universal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
+ "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.7.0"
+ }
+ },
"node_modules/execa": {
"version": "8.0.1",
- "resolved": "https://registry.npmmirror.com/execa/-/execa-8.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
"integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
"license": "MIT",
"dependencies": {
@@ -6582,50 +6032,15 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
- "node_modules/exsolve": {
- "version": "1.0.7",
- "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.7.tgz",
- "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==",
- "license": "MIT"
- },
- "node_modules/extract-zip": {
- "version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz",
- "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "debug": "^4.1.1",
- "get-stream": "^5.1.0",
- "yauzl": "^2.10.0"
- },
- "bin": {
- "extract-zip": "cli.js"
- },
- "engines": {
- "node": ">= 10.17.0"
- },
- "optionalDependencies": {
- "@types/yauzl": "^2.9.1"
- }
- },
- "node_modules/extract-zip/node_modules/get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "license": "MIT",
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
+ "node_modules/exsolve": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.8.tgz",
+ "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
+ "license": "MIT"
+ },
"node_modules/fast-fifo": {
"version": "1.3.2",
- "resolved": "https://registry.npmmirror.com/fast-fifo/-/fast-fifo-1.3.2.tgz",
+ "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
"license": "MIT"
},
@@ -6646,10 +6061,13 @@
}
},
"node_modules/fast-npm-meta": {
- "version": "0.4.4",
- "resolved": "https://registry.npmmirror.com/fast-npm-meta/-/fast-npm-meta-0.4.4.tgz",
- "integrity": "sha512-cq8EVW3jpX1U3dO1AYanz2BJ6n9ITQgCwE1xjNwI5jO2a9erE369OZNO8Wt/Wbw8YHhCD/dimH9BxRsY+6DinA==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-npm-meta/-/fast-npm-meta-1.3.0.tgz",
+ "integrity": "sha512-Yz48hvMPiD+J5vPQj767Gdd3i6TOzqwBuvc0ylkzyxh2+VEJmtWBBy1OT1/CoeStcKhS6lBK8opUf13BNXBBYw==",
"license": "MIT",
+ "bin": {
+ "fast-npm-meta": "dist/cli.mjs"
+ },
"funding": {
"url": "https://github.com/sponsors/antfu"
}
@@ -6663,20 +6081,14 @@
"reusify": "^1.0.4"
}
},
- "node_modules/fd-slicer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
- "license": "MIT",
- "dependencies": {
- "pend": "~1.2.0"
- }
- },
"node_modules/fdir": {
- "version": "6.4.6",
- "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.6.tgz",
- "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
+ "version": "6.5.0",
+ "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
"peerDependencies": {
"picomatch": "^3 || ^4"
},
@@ -6686,38 +6098,9 @@
}
}
},
- "node_modules/fecha": {
- "version": "4.2.3",
- "resolved": "https://registry.npmmirror.com/fecha/-/fecha-4.2.3.tgz",
- "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==",
- "license": "MIT"
- },
- "node_modules/fetch-blob": {
- "version": "3.2.0",
- "resolved": "https://registry.npmmirror.com/fetch-blob/-/fetch-blob-3.2.0.tgz",
- "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/jimmywarting"
- },
- {
- "type": "paypal",
- "url": "https://paypal.me/jimmywarting"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "node-domexception": "^1.0.0",
- "web-streams-polyfill": "^3.0.3"
- },
- "engines": {
- "node": "^12.20 || >= 14.13"
- }
- },
"node_modules/file-uri-to-path": {
"version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"license": "MIT"
},
@@ -6733,57 +6116,10 @@
"node": ">=8"
}
},
- "node_modules/filter-obj": {
- "version": "6.1.0",
- "resolved": "https://registry.npmmirror.com/filter-obj/-/filter-obj-6.1.0.tgz",
- "integrity": "sha512-xdMtCAODmPloU9qtmPcdBV9Kd27NtMse+4ayThxqIHUES5Z2S6bGpap5PpdmNM56ub7y3i1eyr+vJJIIgWGKmA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/find-up": {
- "version": "7.0.0",
- "resolved": "https://registry.npmmirror.com/find-up/-/find-up-7.0.0.tgz",
- "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==",
- "license": "MIT",
- "dependencies": {
- "locate-path": "^7.2.0",
- "path-exists": "^5.0.0",
- "unicorn-magic": "^0.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/find-up-simple": {
- "version": "1.0.1",
- "resolved": "https://registry.npmmirror.com/find-up-simple/-/find-up-simple-1.0.1.tgz",
- "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/fn.name": {
- "version": "1.1.0",
- "resolved": "https://registry.npmmirror.com/fn.name/-/fn.name-1.1.0.tgz",
- "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==",
- "license": "MIT"
- },
"node_modules/follow-redirects": {
- "version": "1.15.9",
- "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
- "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
@@ -6817,9 +6153,9 @@
}
},
"node_modules/form-data": {
- "version": "4.0.4",
- "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz",
- "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
@@ -6832,55 +6168,37 @@
"node": ">= 6"
}
},
- "node_modules/form-data/node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
"license": "MIT",
"engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/form-data/node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "license": "MIT",
- "dependencies": {
- "mime-db": "1.52.0"
+ "node": "*"
},
- "engines": {
- "node": ">= 0.6"
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
}
},
- "node_modules/formdata-polyfill": {
- "version": "4.0.10",
- "resolved": "https://registry.npmmirror.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
- "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "node_modules/framesync": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmmirror.com/framesync/-/framesync-6.1.2.tgz",
+ "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==",
"license": "MIT",
"dependencies": {
- "fetch-blob": "^3.1.2"
- },
- "engines": {
- "node": ">=12.20.0"
+ "tslib": "2.4.0"
}
},
- "node_modules/fraction.js": {
- "version": "4.3.7",
- "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz",
- "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
- "license": "MIT",
- "engines": {
- "node": "*"
- },
- "funding": {
- "type": "patreon",
- "url": "https://github.com/sponsors/rawify"
- }
+ "node_modules/framesync/node_modules/tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
+ "license": "0BSD"
},
"node_modules/fresh": {
"version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/fresh/-/fresh-2.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
"license": "MIT",
"engines": {
@@ -6933,35 +6251,28 @@
},
"node_modules/fuse.js": {
"version": "7.1.0",
- "resolved": "https://registry.npmmirror.com/fuse.js/-/fuse.js-7.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
"integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=10"
}
},
+ "node_modules/fzf": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fzf/-/fzf-0.5.2.tgz",
+ "integrity": "sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
- "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
- "node_modules/get-amd-module-type": {
- "version": "6.0.1",
- "resolved": "https://registry.npmmirror.com/get-amd-module-type/-/get-amd-module-type-6.0.1.tgz",
- "integrity": "sha512-MtjsmYiCXcYDDrGqtNbeIYdAl85n+5mSv2r3FbzER/YV3ZILw4HNNIw34HuV5pyl0jzs6GFYU1VHVEefhgcNHQ==",
- "license": "MIT",
- "dependencies": {
- "ast-module-types": "^6.0.1",
- "node-source-walk": "^7.0.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -6997,7 +6308,7 @@
},
"node_modules/get-port-please": {
"version": "3.2.0",
- "resolved": "https://registry.npmmirror.com/get-port-please/-/get-port-please-3.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz",
"integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==",
"license": "MIT"
},
@@ -7016,7 +6327,7 @@
},
"node_modules/get-stream": {
"version": "8.0.1",
- "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-8.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
"integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
"license": "MIT",
"engines": {
@@ -7043,29 +6354,11 @@
"giget": "dist/cli.mjs"
}
},
- "node_modules/git-up": {
- "version": "8.1.1",
- "resolved": "https://registry.npmmirror.com/git-up/-/git-up-8.1.1.tgz",
- "integrity": "sha512-FDenSF3fVqBYSaJoYy1KSc2wosx0gCvKP+c+PRBht7cAaiCeQlBtfBDX9vgnNOHmdePlSFITVcn4pFfcgNvx3g==",
- "license": "MIT",
- "dependencies": {
- "is-ssh": "^1.4.0",
- "parse-url": "^9.2.0"
- }
- },
- "node_modules/git-url-parse": {
- "version": "16.1.0",
- "resolved": "https://registry.npmmirror.com/git-url-parse/-/git-url-parse-16.1.0.tgz",
- "integrity": "sha512-cPLz4HuK86wClEW7iDdeAKcCVlWXmrLpb2L+G9goW0Z1dtpNS6BXXSOckUTlJT/LDQViE1QZKstNORzHsLnobw==",
- "license": "MIT",
- "dependencies": {
- "git-up": "^8.1.0"
- }
- },
"node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
@@ -7096,7 +6389,7 @@
},
"node_modules/global-directory": {
"version": "4.0.1",
- "resolved": "https://registry.npmmirror.com/global-directory/-/global-directory-4.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz",
"integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==",
"license": "MIT",
"dependencies": {
@@ -7110,52 +6403,25 @@
}
},
"node_modules/globby": {
- "version": "14.1.0",
- "resolved": "https://registry.npmmirror.com/globby/-/globby-14.1.0.tgz",
- "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==",
+ "version": "16.1.1",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-16.1.1.tgz",
+ "integrity": "sha512-dW7vl+yiAJSp6aCekaVnVJxurRv7DCOLyXqEG3RYMYUg7AuJ2jCqPkZTA8ooqC2vtnkaMcV5WfFBMuEnTu1OQg==",
"license": "MIT",
"dependencies": {
- "@sindresorhus/merge-streams": "^2.1.0",
+ "@sindresorhus/merge-streams": "^4.0.0",
"fast-glob": "^3.3.3",
- "ignore": "^7.0.3",
- "path-type": "^6.0.0",
+ "ignore": "^7.0.5",
+ "is-path-inside": "^4.0.0",
"slash": "^5.1.0",
- "unicorn-magic": "^0.3.0"
- },
- "engines": {
- "node": ">=18"
+ "unicorn-magic": "^0.4.0"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/globby/node_modules/unicorn-magic": {
- "version": "0.3.0",
- "resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
- "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
- "license": "MIT",
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/gonzales-pe": {
- "version": "4.3.0",
- "resolved": "https://registry.npmmirror.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz",
- "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==",
- "license": "MIT",
- "dependencies": {
- "minimist": "^1.2.5"
- },
- "bin": {
- "gonzales": "bin/gonzales.js"
- },
- "engines": {
- "node": ">=0.6.0"
- }
- },
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
@@ -7174,9 +6440,15 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC"
},
+ "node_modules/gsap": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmmirror.com/gsap/-/gsap-3.14.2.tgz",
+ "integrity": "sha512-P8/mMxVLU7o4+55+1TCnQrPmgjPKnwkzkXOK1asnR9Jg2lna4tEY5qBJjMmAaOBDDZWtlRjBXjLa0w53G/uBLA==",
+ "license": "Standard 'no charge' license: https://gsap.com/standard-license."
+ },
"node_modules/gzip-size": {
"version": "7.0.0",
- "resolved": "https://registry.npmmirror.com/gzip-size/-/gzip-size-7.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz",
"integrity": "sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==",
"license": "MIT",
"dependencies": {
@@ -7190,19 +6462,19 @@
}
},
"node_modules/h3": {
- "version": "1.15.3",
- "resolved": "https://registry.npmmirror.com/h3/-/h3-1.15.3.tgz",
- "integrity": "sha512-z6GknHqyX0h9aQaTx22VZDf6QyZn+0Nh+Ym8O/u0SGSkyF5cuTJYKlc8MkzW3Nzf9LE1ivcpmYC3FUGpywhuUQ==",
+ "version": "1.15.5",
+ "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.5.tgz",
+ "integrity": "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==",
"license": "MIT",
"dependencies": {
"cookie-es": "^1.2.2",
- "crossws": "^0.3.4",
+ "crossws": "^0.3.5",
"defu": "^6.1.4",
"destr": "^2.0.5",
"iron-webcrypto": "^1.2.1",
- "node-mock-http": "^1.0.0",
+ "node-mock-http": "^1.0.4",
"radix3": "^1.1.2",
- "ufo": "^1.6.1",
+ "ufo": "^1.6.3",
"uncrypto": "^0.1.3"
}
},
@@ -7260,30 +6532,18 @@
"node": ">= 0.4"
}
},
+ "node_modules/hey-listen": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/hey-listen/-/hey-listen-1.0.8.tgz",
+ "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==",
+ "license": "MIT"
+ },
"node_modules/hookable": {
"version": "5.5.3",
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT"
},
- "node_modules/hosted-git-info": {
- "version": "7.0.2",
- "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
- "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
- "license": "ISC",
- "dependencies": {
- "lru-cache": "^10.0.1"
- },
- "engines": {
- "node": "^16.14.0 || >=18.0.0"
- }
- },
- "node_modules/hosted-git-info/node_modules/lru-cache": {
- "version": "10.4.3",
- "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
- "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
- "license": "ISC"
- },
"node_modules/http-assert": {
"version": "1.5.0",
"resolved": "https://registry.npmmirror.com/http-assert/-/http-assert-1.5.0.tgz",
@@ -7332,33 +6592,28 @@
}
},
"node_modules/http-errors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
"license": "MIT",
"dependencies": {
- "depd": "2.0.0",
- "inherits": "2.0.4",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "toidentifier": "1.0.1"
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
},
"engines": {
"node": ">= 0.8"
- }
- },
- "node_modules/http-errors/node_modules/statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/http-shutdown": {
"version": "1.2.2",
- "resolved": "https://registry.npmmirror.com/http-shutdown/-/http-shutdown-1.2.2.tgz",
+ "resolved": "https://registry.npmjs.org/http-shutdown/-/http-shutdown-1.2.2.tgz",
"integrity": "sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==",
"license": "MIT",
"engines": {
@@ -7368,7 +6623,7 @@
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
- "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
@@ -7381,22 +6636,34 @@
},
"node_modules/httpxy": {
"version": "0.1.7",
- "resolved": "https://registry.npmmirror.com/httpxy/-/httpxy-0.1.7.tgz",
+ "resolved": "https://registry.npmjs.org/httpxy/-/httpxy-0.1.7.tgz",
"integrity": "sha512-pXNx8gnANKAndgga5ahefxc++tJvNL87CXoRwxn1cJE2ZkWEojF3tNfQIEhZX/vfpt+wzeAzpUI4qkediX1MLQ==",
"license": "MIT"
},
"node_modules/human-signals": {
"version": "5.0.0",
- "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-5.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
"integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=16.17.0"
}
},
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/ieee754": {
"version": "1.2.1",
- "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
@@ -7424,9 +6691,9 @@
}
},
"node_modules/image-meta": {
- "version": "0.2.1",
- "resolved": "https://registry.npmmirror.com/image-meta/-/image-meta-0.2.1.tgz",
- "integrity": "sha512-K6acvFaelNxx8wc2VjbIzXKDVB0Khs0QT35U6NkGfTdCmjLNcO2945m7RFNR9/RPVFm48hq7QPzK8uGH18HCGw==",
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/image-meta/-/image-meta-0.2.2.tgz",
+ "integrity": "sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA==",
"license": "MIT"
},
"node_modules/impound": {
@@ -7442,27 +6709,6 @@
"unplugin-utils": "^0.2.4"
}
},
- "node_modules/imurmurhash": {
- "version": "0.1.4",
- "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
- "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.8.19"
- }
- },
- "node_modules/index-to-position": {
- "version": "1.1.0",
- "resolved": "https://registry.npmmirror.com/index-to-position/-/index-to-position-1.1.0.tgz",
- "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
@@ -7482,7 +6728,7 @@
},
"node_modules/ini": {
"version": "4.1.1",
- "resolved": "https://registry.npmmirror.com/ini/-/ini-4.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
"integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
"license": "ISC",
"engines": {
@@ -7490,12 +6736,12 @@
}
},
"node_modules/ioredis": {
- "version": "5.6.1",
- "resolved": "https://registry.npmmirror.com/ioredis/-/ioredis-5.6.1.tgz",
- "integrity": "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==",
+ "version": "5.10.0",
+ "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.10.0.tgz",
+ "integrity": "sha512-HVBe9OFuqs+Z6n64q09PQvP1/R4Bm+30PAyyD4wIEqssh3v9L21QjCVk4kRLucMBcDokJTcLjsGeVRlq/nH6DA==",
"license": "MIT",
"dependencies": {
- "@ioredis/commands": "^1.1.1",
+ "@ioredis/commands": "1.5.1",
"cluster-key-slot": "^1.1.0",
"debug": "^4.3.4",
"denque": "^2.1.0",
@@ -7522,12 +6768,6 @@
"url": "https://github.com/sponsors/brc-dd"
}
},
- "node_modules/is-arrayish": {
- "version": "0.3.2",
- "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz",
- "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
- "license": "MIT"
- },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -7540,21 +6780,6 @@
"node": ">=8"
}
},
- "node_modules/is-builtin-module": {
- "version": "3.2.1",
- "resolved": "https://registry.npmmirror.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
- "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
- "license": "MIT",
- "dependencies": {
- "builtin-modules": "^3.3.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/is-core-module": {
"version": "2.16.1",
"resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz",
@@ -7572,7 +6797,7 @@
},
"node_modules/is-docker": {
"version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
"integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
"license": "MIT",
"bin": {
@@ -7635,7 +6860,7 @@
},
"node_modules/is-inside-container": {
"version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
"integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
"license": "MIT",
"dependencies": {
@@ -7653,7 +6878,7 @@
},
"node_modules/is-installed-globally": {
"version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/is-installed-globally/-/is-installed-globally-1.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz",
"integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==",
"license": "MIT",
"dependencies": {
@@ -7669,7 +6894,7 @@
},
"node_modules/is-module": {
"version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/is-module/-/is-module-1.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
"integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
"license": "MIT"
},
@@ -7684,7 +6909,7 @@
},
"node_modules/is-path-inside": {
"version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-4.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz",
"integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==",
"license": "MIT",
"engines": {
@@ -7694,18 +6919,9 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-plain-obj": {
- "version": "2.1.0",
- "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/is-reference": {
"version": "1.2.1",
- "resolved": "https://registry.npmmirror.com/is-reference/-/is-reference-1.2.1.tgz",
+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
"license": "MIT",
"dependencies": {
@@ -7730,18 +6946,9 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-ssh": {
- "version": "1.4.1",
- "resolved": "https://registry.npmmirror.com/is-ssh/-/is-ssh-1.4.1.tgz",
- "integrity": "sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==",
- "license": "MIT",
- "dependencies": {
- "protocols": "^2.0.1"
- }
- },
"node_modules/is-stream": {
"version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-3.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
"license": "MIT",
"engines": {
@@ -7751,24 +6958,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-url": {
- "version": "1.2.4",
- "resolved": "https://registry.npmmirror.com/is-url/-/is-url-1.2.4.tgz",
- "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
- "license": "MIT"
- },
- "node_modules/is-url-superb": {
- "version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/is-url-superb/-/is-url-superb-4.0.0.tgz",
- "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/is-what": {
"version": "4.1.16",
"resolved": "https://registry.npmmirror.com/is-what/-/is-what-4.1.16.tgz",
@@ -7782,9 +6971,9 @@
}
},
"node_modules/is-wsl": {
- "version": "3.1.0",
- "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-3.1.0.tgz",
- "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
+ "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
"license": "MIT",
"dependencies": {
"is-inside-container": "^1.0.0"
@@ -7798,7 +6987,7 @@
},
"node_modules/is64bit": {
"version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/is64bit/-/is64bit-2.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/is64bit/-/is64bit-2.0.0.tgz",
"integrity": "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==",
"license": "MIT",
"dependencies": {
@@ -7813,17 +7002,17 @@
},
"node_modules/isarray": {
"version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"license": "MIT"
},
"node_modules/isexe": {
- "version": "3.1.1",
- "resolved": "https://registry.npmmirror.com/isexe/-/isexe-3.1.1.tgz",
- "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
- "license": "ISC",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz",
+ "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==",
+ "license": "BlueOak-1.0.0",
"engines": {
- "node": ">=16"
+ "node": ">=18"
}
},
"node_modules/jackspeak": {
@@ -7842,9 +7031,9 @@
}
},
"node_modules/jiti": {
- "version": "2.5.1",
- "resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.5.1.tgz",
- "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"license": "MIT",
"bin": {
"jiti": "lib/jiti-cli.mjs"
@@ -7852,13 +7041,13 @@
},
"node_modules/js-tokens": {
"version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/jsesc": {
"version": "3.1.0",
- "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"license": "MIT",
"bin": {
@@ -7870,7 +7059,7 @@
},
"node_modules/json5": {
"version": "2.2.3",
- "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"license": "MIT",
"bin": {
@@ -7880,37 +7069,16 @@
"node": ">=6"
}
},
- "node_modules/jsonfile": {
- "version": "6.1.0",
- "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
- "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/junk": {
- "version": "4.0.1",
- "resolved": "https://registry.npmmirror.com/junk/-/junk-4.0.1.tgz",
- "integrity": "sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==",
- "license": "MIT",
- "engines": {
- "node": ">=12.20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/jwt-decode": {
- "version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/jwt-decode/-/jwt-decode-4.0.0.tgz",
- "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"license": "MIT",
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
}
},
"node_modules/keygrip": {
@@ -7926,9 +7094,9 @@
}
},
"node_modules/kleur": {
- "version": "3.0.3",
- "resolved": "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
+ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -7944,15 +7112,15 @@
}
},
"node_modules/knitwork": {
- "version": "1.2.0",
- "resolved": "https://registry.npmmirror.com/knitwork/-/knitwork-1.2.0.tgz",
- "integrity": "sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/knitwork/-/knitwork-1.3.0.tgz",
+ "integrity": "sha512-4LqMNoONzR43B1W0ek0fhXMsDNW/zxa1NdFAVMY+k28pgZLovR4G3PB5MrpTxCy1QaZCqNoiaKPr5w5qZHfSNw==",
"license": "MIT"
},
"node_modules/koa": {
- "version": "2.16.1",
- "resolved": "https://registry.npmmirror.com/koa/-/koa-2.16.1.tgz",
- "integrity": "sha512-umfX9d3iuSxTQP4pnzLOz0HKnPg0FaUUIKcye2lOiz3KPu1Y3M3xlz76dISdFPQs37P9eJz1wUpcTS6KDPn9fA==",
+ "version": "2.16.4",
+ "resolved": "https://registry.npmjs.org/koa/-/koa-2.16.4.tgz",
+ "integrity": "sha512-3An0GCLDSR34tsCO4H8Tef8Pp2ngtaZDAZnsWJYelqXUK5wyiHvGItgK/xcSkmHLSTn1Jcho1mRQs2ehRzvKKw==",
"license": "MIT",
"dependencies": {
"accepts": "^1.3.5",
@@ -8124,42 +7292,19 @@
"node": ">= 0.6"
}
},
- "node_modules/kuler": {
- "version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/kuler/-/kuler-2.0.0.tgz",
- "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
- "license": "MIT"
- },
- "node_modules/lambda-local": {
- "version": "2.2.0",
- "resolved": "https://registry.npmmirror.com/lambda-local/-/lambda-local-2.2.0.tgz",
- "integrity": "sha512-bPcgpIXbHnVGfI/omZIlgucDqlf4LrsunwoKue5JdZeGybt8L6KyJz2Zu19ffuZwIwLj2NAI2ZyaqNT6/cetcg==",
- "license": "MIT",
- "dependencies": {
- "commander": "^10.0.1",
- "dotenv": "^16.3.1",
- "winston": "^3.10.0"
- },
- "bin": {
- "lambda-local": "build/cli.js"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/launch-editor": {
- "version": "2.10.0",
- "resolved": "https://registry.npmmirror.com/launch-editor/-/launch-editor-2.10.0.tgz",
- "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==",
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.13.1.tgz",
+ "integrity": "sha512-lPSddlAAluRKJ7/cjRFoXUFzaX7q/YKI7yPHuEvSJVqoXvFnJov1/Ud87Aa4zULIbA9Nja4mSPK8l0z/7eV2wA==",
"license": "MIT",
"dependencies": {
- "picocolors": "^1.0.0",
- "shell-quote": "^1.8.1"
+ "picocolors": "^1.1.1",
+ "shell-quote": "^1.8.3"
}
},
"node_modules/lazystream": {
"version": "1.0.1",
- "resolved": "https://registry.npmmirror.com/lazystream/-/lazystream-1.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
"license": "MIT",
"dependencies": {
@@ -8171,7 +7316,7 @@
},
"node_modules/lazystream/node_modules/readable-stream": {
"version": "2.3.8",
- "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"license": "MIT",
"dependencies": {
@@ -8186,13 +7331,13 @@
},
"node_modules/lazystream/node_modules/safe-buffer": {
"version": "5.1.2",
- "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
"node_modules/lazystream/node_modules/string_decoder": {
"version": "1.1.1",
- "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"license": "MIT",
"dependencies": {
@@ -8219,7 +7364,7 @@
},
"node_modules/listhen": {
"version": "1.9.0",
- "resolved": "https://registry.npmmirror.com/listhen/-/listhen-1.9.0.tgz",
+ "resolved": "https://registry.npmjs.org/listhen/-/listhen-1.9.0.tgz",
"integrity": "sha512-I8oW2+QL5KJo8zXNWX046M134WchxsXC7SawLPvRQpogCbkyQIaFxPE89A2HiwR7vAK2Dm2ERBAmyjTYGYEpBg==",
"license": "MIT",
"dependencies": {
@@ -8249,19 +7394,19 @@
},
"node_modules/listhen/node_modules/pathe": {
"version": "1.1.2",
- "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
"license": "MIT"
},
"node_modules/local-pkg": {
- "version": "1.1.1",
- "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.1.tgz",
- "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz",
+ "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==",
"license": "MIT",
"dependencies": {
"mlly": "^1.7.4",
- "pkg-types": "^2.0.1",
- "quansync": "^0.2.8"
+ "pkg-types": "^2.3.0",
+ "quansync": "^0.2.11"
},
"engines": {
"node": ">=14"
@@ -8270,101 +7415,48 @@
"url": "https://github.com/sponsors/antfu"
}
},
- "node_modules/locate-path": {
- "version": "7.2.0",
- "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-7.2.0.tgz",
- "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
- "license": "MIT",
- "dependencies": {
- "p-locate": "^6.0.0"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/lodash": {
- "version": "4.17.21",
- "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
- "license": "MIT"
- },
- "node_modules/lodash-es": {
- "version": "4.17.21",
- "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
- "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
- "license": "MIT"
- },
- "node_modules/lodash.debounce": {
- "version": "4.0.8",
- "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"license": "MIT"
},
"node_modules/lodash.defaults": {
"version": "4.2.0",
- "resolved": "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
"license": "MIT"
},
"node_modules/lodash.isarguments": {
"version": "3.1.0",
- "resolved": "https://registry.npmmirror.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
"license": "MIT"
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
- "resolved": "https://registry.npmmirror.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
"license": "MIT"
},
"node_modules/lodash.uniq": {
"version": "4.5.0",
- "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
"integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
"license": "MIT"
},
- "node_modules/logform": {
- "version": "2.7.0",
- "resolved": "https://registry.npmmirror.com/logform/-/logform-2.7.0.tgz",
- "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==",
- "license": "MIT",
- "dependencies": {
- "@colors/colors": "1.6.0",
- "@types/triple-beam": "^1.3.2",
- "fecha": "^4.2.0",
- "ms": "^2.1.1",
- "safe-stable-stringify": "^2.3.1",
- "triple-beam": "^1.3.0"
- },
- "engines": {
- "node": ">= 12.0.0"
- }
- },
"node_modules/lru-cache": {
"version": "5.1.1",
- "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"license": "ISC",
"dependencies": {
"yallist": "^3.0.2"
}
},
- "node_modules/luxon": {
- "version": "3.7.1",
- "resolved": "https://registry.npmmirror.com/luxon/-/luxon-3.7.1.tgz",
- "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==",
- "license": "MIT",
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/magic-regexp": {
"version": "0.10.0",
- "resolved": "https://registry.npmmirror.com/magic-regexp/-/magic-regexp-0.10.0.tgz",
+ "resolved": "https://registry.npmjs.org/magic-regexp/-/magic-regexp-0.10.0.tgz",
"integrity": "sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg==",
"license": "MIT",
"dependencies": {
@@ -8378,38 +7470,38 @@
}
},
"node_modules/magic-string": {
- "version": "0.30.17",
- "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
- "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "version": "0.30.21",
+ "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"license": "MIT",
"dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.0"
+ "@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/magic-string-ast": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/magic-string-ast/-/magic-string-ast-1.0.0.tgz",
- "integrity": "sha512-8rbuNizut2gW94kv7pqgt0dvk+AHLPVIm0iJtpSgQJ9dx21eWx5SBel8z3jp1xtC0j6/iyK3AWGhAR1H61s7LA==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-1.0.3.tgz",
+ "integrity": "sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==",
"license": "MIT",
"dependencies": {
- "magic-string": "^0.30.17"
+ "magic-string": "^0.30.19"
},
"engines": {
- "node": ">=20.18.0"
+ "node": ">=20.19.0"
},
"funding": {
"url": "https://github.com/sponsors/sxzz"
}
},
"node_modules/magicast": {
- "version": "0.3.5",
- "resolved": "https://registry.npmmirror.com/magicast/-/magicast-0.3.5.tgz",
- "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==",
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz",
+ "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.25.4",
- "@babel/types": "^7.25.4",
- "source-map-js": "^1.2.0"
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "source-map-js": "^1.2.1"
}
},
"node_modules/math-intrinsics": {
@@ -8423,7 +7515,7 @@
},
"node_modules/mdn-data": {
"version": "2.12.2",
- "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.12.2.tgz",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
"integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
"license": "CC0-1.0"
},
@@ -8436,21 +7528,9 @@
"node": ">= 0.6"
}
},
- "node_modules/merge-options": {
- "version": "3.0.4",
- "resolved": "https://registry.npmmirror.com/merge-options/-/merge-options-3.0.4.tgz",
- "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
- "license": "MIT",
- "dependencies": {
- "is-plain-obj": "^2.1.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/merge-stream": {
"version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
"license": "MIT"
},
@@ -8472,12 +7552,6 @@
"node": ">= 0.6"
}
},
- "node_modules/micro-api-client": {
- "version": "3.3.0",
- "resolved": "https://registry.npmmirror.com/micro-api-client/-/micro-api-client-3.3.0.tgz",
- "integrity": "sha512-y0y6CUB9RLVsy3kfgayU28746QrNMpSm9O/AYGNsBgOkJr/X/Jk0VLGoO8Ude7Bpa8adywzF+MzXNZRFRsNPhg==",
- "license": "ISC"
- },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz",
@@ -8504,9 +7578,9 @@
}
},
"node_modules/mime": {
- "version": "4.0.7",
- "resolved": "https://registry.npmmirror.com/mime/-/mime-4.0.7.tgz",
- "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
+ "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
"funding": [
"https://github.com/sponsors/broofa"
],
@@ -8519,21 +7593,21 @@
}
},
"node_modules/mime-db": {
- "version": "1.54.0",
- "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz",
- "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "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": "3.0.1",
- "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.1.tgz",
- "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "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.54.0"
+ "mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
@@ -8541,7 +7615,7 @@
},
"node_modules/mimic-fn": {
"version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
"license": "MIT",
"engines": {
@@ -8552,12 +7626,12 @@
}
},
"node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"license": "ISC",
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -8566,28 +7640,19 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/minipass": {
- "version": "7.1.2",
- "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz",
- "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
- "license": "ISC",
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "license": "BlueOak-1.0.0",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/minizlib": {
- "version": "3.0.2",
- "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-3.0.2.tgz",
- "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
+ "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
"license": "MIT",
"dependencies": {
"minipass": "^7.1.2"
@@ -8602,31 +7667,16 @@
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"license": "MIT"
},
- "node_modules/mkdirp": {
- "version": "3.0.1",
- "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-3.0.1.tgz",
- "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
- "license": "MIT",
- "bin": {
- "mkdirp": "dist/cjs/src/bin.js"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/mlly": {
- "version": "1.7.4",
- "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.7.4.tgz",
- "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
+ "version": "1.8.0",
+ "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.0.tgz",
+ "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
"license": "MIT",
"dependencies": {
- "acorn": "^8.14.0",
- "pathe": "^2.0.1",
- "pkg-types": "^1.3.0",
- "ufo": "^1.5.4"
+ "acorn": "^8.15.0",
+ "pathe": "^2.0.3",
+ "pkg-types": "^1.3.1",
+ "ufo": "^1.6.1"
}
},
"node_modules/mlly/node_modules/confbox": {
@@ -8652,25 +7702,9 @@
"integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==",
"license": "MIT"
},
- "node_modules/module-definition": {
- "version": "6.0.1",
- "resolved": "https://registry.npmmirror.com/module-definition/-/module-definition-6.0.1.tgz",
- "integrity": "sha512-FeVc50FTfVVQnolk/WQT8MX+2WVcDnTGiq6Wo+/+lJ2ET1bRVi3HG3YlJUfqagNMc/kUlFSoR96AJkxGpKz13g==",
- "license": "MIT",
- "dependencies": {
- "ast-module-types": "^6.0.1",
- "node-source-walk": "^7.0.1"
- },
- "bin": {
- "module-definition": "bin/cli.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/mrmime": {
"version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
"integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
"license": "MIT",
"engines": {
@@ -8683,6 +7717,12 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
+ "node_modules/muggle-string": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
+ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+ "license": "MIT"
+ },
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz",
@@ -8695,9 +7735,9 @@
}
},
"node_modules/nanoid": {
- "version": "5.1.5",
- "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-5.1.5.tgz",
- "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
+ "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==",
"funding": [
{
"type": "github",
@@ -8713,9 +7753,9 @@
}
},
"node_modules/nanotar": {
- "version": "0.2.0",
- "resolved": "https://registry.npmmirror.com/nanotar/-/nanotar-0.2.0.tgz",
- "integrity": "sha512-9ca1h0Xjvo9bEkE4UOxgAzLV0jHKe6LMaxo37ND2DAhhAtd0j8pR1Wxz+/goMrZO8AEZTWCmyaOsFI/W5AdpCQ==",
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/nanotar/-/nanotar-0.2.1.tgz",
+ "integrity": "sha512-MUrzzDUcIOPbv7ubhDV/L4CIfVTATd9XhDE2ixFeCrM5yp9AlzUpn91JrnN0HD6hksdxvz9IW9aKANz0Bta0GA==",
"license": "MIT"
},
"node_modules/negotiator": {
@@ -8727,60 +7767,24 @@
"node": ">= 0.6"
}
},
- "node_modules/netlify": {
- "version": "13.3.5",
- "resolved": "https://registry.npmmirror.com/netlify/-/netlify-13.3.5.tgz",
- "integrity": "sha512-Nc3loyVASW59W+8fLDZT1lncpG7llffyZ2o0UQLx/Fr20i7P8oP+lE7+TEcFvXj9IUWU6LjB9P3BH+iFGyp+mg==",
- "license": "MIT",
- "dependencies": {
- "@netlify/open-api": "^2.37.0",
- "lodash-es": "^4.17.21",
- "micro-api-client": "^3.3.0",
- "node-fetch": "^3.0.0",
- "p-wait-for": "^5.0.0",
- "qs": "^6.9.6"
- },
- "engines": {
- "node": "^14.16.0 || >=16.0.0"
- }
- },
- "node_modules/netlify/node_modules/node-fetch": {
- "version": "3.3.2",
- "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-3.3.2.tgz",
- "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
- "license": "MIT",
- "dependencies": {
- "data-uri-to-buffer": "^4.0.0",
- "fetch-blob": "^3.1.4",
- "formdata-polyfill": "^4.0.10"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/node-fetch"
- }
- },
"node_modules/nitropack": {
- "version": "2.12.4",
- "resolved": "https://registry.npmmirror.com/nitropack/-/nitropack-2.12.4.tgz",
- "integrity": "sha512-MPmPRJWTeH03f/NmpN4q3iI3Woik4uaaWIoX34W3gMJiW06Vm1te/lPzuu5EXpXOK7Q2m3FymGMPXcExqih96Q==",
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/nitropack/-/nitropack-2.13.1.tgz",
+ "integrity": "sha512-2dDj89C4wC2uzG7guF3CnyG+zwkZosPEp7FFBGHB3AJo11AywOolWhyQJFHDzve8COvGxJaqscye9wW2IrUsNw==",
"license": "MIT",
"dependencies": {
- "@cloudflare/kv-asset-handler": "^0.4.0",
- "@netlify/functions": "^3.1.10",
- "@rollup/plugin-alias": "^5.1.1",
- "@rollup/plugin-commonjs": "^28.0.6",
+ "@cloudflare/kv-asset-handler": "^0.4.2",
+ "@rollup/plugin-alias": "^6.0.0",
+ "@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-inject": "^5.0.5",
"@rollup/plugin-json": "^6.1.0",
- "@rollup/plugin-node-resolve": "^16.0.1",
- "@rollup/plugin-replace": "^6.0.2",
+ "@rollup/plugin-node-resolve": "^16.0.3",
+ "@rollup/plugin-replace": "^6.0.3",
"@rollup/plugin-terser": "^0.4.4",
- "@vercel/nft": "^0.29.4",
+ "@vercel/nft": "^1.2.0",
"archiver": "^7.0.1",
- "c12": "^3.1.0",
- "chokidar": "^4.0.3",
+ "c12": "^3.3.3",
+ "chokidar": "^5.0.0",
"citty": "^0.1.6",
"compatx": "^0.2.0",
"confbox": "^0.2.2",
@@ -8788,56 +7792,56 @@
"cookie-es": "^2.0.0",
"croner": "^9.1.0",
"crossws": "^0.3.5",
- "db0": "^0.3.2",
+ "db0": "^0.3.4",
"defu": "^6.1.4",
"destr": "^2.0.5",
- "dot-prop": "^9.0.0",
- "esbuild": "^0.25.6",
+ "dot-prop": "^10.1.0",
+ "esbuild": "^0.27.2",
"escape-string-regexp": "^5.0.0",
"etag": "^1.8.1",
- "exsolve": "^1.0.7",
- "globby": "^14.1.0",
+ "exsolve": "^1.0.8",
+ "globby": "^16.1.0",
"gzip-size": "^7.0.0",
- "h3": "^1.15.3",
+ "h3": "^1.15.5",
"hookable": "^5.5.3",
"httpxy": "^0.1.7",
- "ioredis": "^5.6.1",
- "jiti": "^2.4.2",
+ "ioredis": "^5.9.1",
+ "jiti": "^2.6.1",
"klona": "^2.0.6",
- "knitwork": "^1.2.0",
+ "knitwork": "^1.3.0",
"listhen": "^1.9.0",
- "magic-string": "^0.30.17",
- "magicast": "^0.3.5",
- "mime": "^4.0.7",
- "mlly": "^1.7.4",
- "node-fetch-native": "^1.6.6",
- "node-mock-http": "^1.0.1",
- "ofetch": "^1.4.1",
+ "magic-string": "^0.30.21",
+ "magicast": "^0.5.1",
+ "mime": "^4.1.0",
+ "mlly": "^1.8.0",
+ "node-fetch-native": "^1.6.7",
+ "node-mock-http": "^1.0.4",
+ "ofetch": "^1.5.1",
"ohash": "^2.0.11",
"pathe": "^2.0.3",
- "perfect-debounce": "^1.0.0",
- "pkg-types": "^2.2.0",
- "pretty-bytes": "^6.1.1",
+ "perfect-debounce": "^2.0.0",
+ "pkg-types": "^2.3.0",
+ "pretty-bytes": "^7.1.0",
"radix3": "^1.1.2",
- "rollup": "^4.45.0",
- "rollup-plugin-visualizer": "^6.0.3",
+ "rollup": "^4.55.1",
+ "rollup-plugin-visualizer": "^6.0.5",
"scule": "^1.3.0",
- "semver": "^7.7.2",
+ "semver": "^7.7.3",
"serve-placeholder": "^2.0.2",
- "serve-static": "^2.2.0",
- "source-map": "^0.7.4",
- "std-env": "^3.9.0",
- "ufo": "^1.6.1",
+ "serve-static": "^2.2.1",
+ "source-map": "^0.7.6",
+ "std-env": "^3.10.0",
+ "ufo": "^1.6.3",
"ultrahtml": "^1.6.0",
"uncrypto": "^0.1.3",
- "unctx": "^2.4.1",
- "unenv": "^2.0.0-rc.18",
- "unimport": "^5.1.0",
- "unplugin-utils": "^0.2.4",
- "unstorage": "^1.16.1",
+ "unctx": "^2.5.0",
+ "unenv": "^2.0.0-rc.24",
+ "unimport": "^5.6.0",
+ "unplugin-utils": "^0.3.1",
+ "unstorage": "^1.17.4",
"untyped": "^2.0.0",
- "unwasm": "^0.3.9",
- "youch": "4.1.0-beta.8",
+ "unwasm": "^0.5.3",
+ "youch": "^4.1.0-beta.13",
"youch-core": "^0.3.3"
},
"bin": {
@@ -8845,7 +7849,7 @@
"nitropack": "dist/cli/index.mjs"
},
"engines": {
- "node": "^16.11.0 || >=17.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
"xml2js": "^0.6.2"
@@ -8856,51 +7860,37 @@
}
}
},
- "node_modules/nitropack/node_modules/youch": {
- "version": "4.1.0-beta.8",
- "resolved": "https://registry.npmmirror.com/youch/-/youch-4.1.0-beta.8.tgz",
- "integrity": "sha512-rY2A2lSF7zC+l7HH9Mq+83D1dLlsPnEvy8jTouzaptDZM6geqZ3aJe/b7ULCwRURPtWV3vbDjA2DDMdoBol0HQ==",
+ "node_modules/nitropack/node_modules/perfect-debounce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
+ "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==",
+ "license": "MIT"
+ },
+ "node_modules/nitropack/node_modules/unplugin-utils": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz",
+ "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==",
"license": "MIT",
"dependencies": {
- "@poppinss/colors": "^4.1.4",
- "@poppinss/dumper": "^0.6.3",
- "@speed-highlight/core": "^1.2.7",
- "cookie": "^1.0.2",
- "youch-core": "^0.3.1"
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3"
},
"engines": {
- "node": ">=18"
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
- "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT"
},
- "node_modules/node-domexception": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/node-domexception/-/node-domexception-1.0.0.tgz",
- "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
- "deprecated": "Use your platform's native DOMException instead",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/jimmywarting"
- },
- {
- "type": "github",
- "url": "https://paypal.me/jimmywarting"
- }
- ],
- "license": "MIT",
- "engines": {
- "node": ">=10.5.0"
- }
- },
"node_modules/node-fetch": {
"version": "2.7.0",
- "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT",
"dependencies": {
@@ -8919,15 +7909,15 @@
}
},
"node_modules/node-fetch-native": {
- "version": "1.6.6",
- "resolved": "https://registry.npmmirror.com/node-fetch-native/-/node-fetch-native-1.6.6.tgz",
- "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==",
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
+ "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
"license": "MIT"
},
"node_modules/node-forge": {
- "version": "1.3.1",
- "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-1.3.1.tgz",
- "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz",
+ "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==",
"license": "(BSD-3-Clause OR GPL-2.0)",
"engines": {
"node": ">= 6.13.0"
@@ -8935,7 +7925,7 @@
},
"node_modules/node-gyp-build": {
"version": "4.8.4",
- "resolved": "https://registry.npmmirror.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
"license": "MIT",
"bin": {
@@ -8945,32 +7935,20 @@
}
},
"node_modules/node-mock-http": {
- "version": "1.0.1",
- "resolved": "https://registry.npmmirror.com/node-mock-http/-/node-mock-http-1.0.1.tgz",
- "integrity": "sha512-0gJJgENizp4ghds/Ywu2FCmcRsgBTmRQzYPZm61wy+Em2sBarSka0OhQS5huLBg6od1zkNpnWMCZloQDFVvOMQ==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz",
+ "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==",
"license": "MIT"
},
"node_modules/node-releases": {
- "version": "2.0.19",
- "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.19.tgz",
- "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"license": "MIT"
},
- "node_modules/node-source-walk": {
- "version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/node-source-walk/-/node-source-walk-7.0.1.tgz",
- "integrity": "sha512-3VW/8JpPqPvnJvseXowjZcirPisssnBuDikk6JIZ8jQzF7KJQX52iPFX4RYYxLycYH7IbMRSPUOga/esVjy5Yg==",
- "license": "MIT",
- "dependencies": {
- "@babel/parser": "^7.26.7"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/nopt": {
"version": "8.1.0",
- "resolved": "https://registry.npmmirror.com/nopt/-/nopt-8.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz",
"integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==",
"license": "ISC",
"dependencies": {
@@ -8983,20 +7961,6 @@
"node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/normalize-package-data": {
- "version": "6.0.2",
- "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
- "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "hosted-git-info": "^7.0.0",
- "semver": "^7.3.5",
- "validate-npm-package-license": "^3.0.4"
- },
- "engines": {
- "node": "^16.14.0 || >=18.0.0"
- }
- },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -9006,18 +7970,9 @@
"node": ">=0.10.0"
}
},
- "node_modules/normalize-range": {
- "version": "0.1.2",
- "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz",
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/npm-run-path": {
"version": "5.3.0",
- "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
"license": "MIT",
"dependencies": {
@@ -9032,7 +7987,7 @@
},
"node_modules/npm-run-path/node_modules/path-key": {
"version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/path-key/-/path-key-4.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"license": "MIT",
"engines": {
@@ -9044,7 +7999,7 @@
},
"node_modules/nth-check": {
"version": "2.1.1",
- "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
"integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
"license": "BSD-2-Clause",
"dependencies": {
@@ -9055,75 +8010,68 @@
}
},
"node_modules/nuxt": {
- "version": "4.0.1",
- "resolved": "https://registry.npmmirror.com/nuxt/-/nuxt-4.0.1.tgz",
- "integrity": "sha512-1WbtiX127640PXUJ2Mb32ck0A0/hzBk6+oPQ0YvJnS/HZK3A/oJEW7sYCRPYyEBwUyIQk12QRCBHxmr6LLeXZQ==",
- "license": "MIT",
- "dependencies": {
- "@nuxt/cli": "^3.26.4",
- "@nuxt/devalue": "^2.0.2",
- "@nuxt/devtools": "^2.6.2",
- "@nuxt/kit": "4.0.1",
- "@nuxt/schema": "4.0.1",
- "@nuxt/telemetry": "^2.6.6",
- "@nuxt/vite-builder": "4.0.1",
- "@unhead/vue": "^2.0.12",
- "@vue/shared": "^3.5.17",
- "c12": "^3.1.0",
- "chokidar": "^4.0.3",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/nuxt/-/nuxt-4.3.1.tgz",
+ "integrity": "sha512-bl+0rFcT5Ax16aiWFBFPyWcsTob19NTZaDL5P6t0MQdK63AtgS6fN6fwvwdbXtnTk6/YdCzlmuLzXhSM22h0OA==",
+ "license": "MIT",
+ "dependencies": {
+ "@dxup/nuxt": "^0.3.2",
+ "@nuxt/cli": "^3.33.0",
+ "@nuxt/devtools": "^3.1.1",
+ "@nuxt/kit": "4.3.1",
+ "@nuxt/nitro-server": "4.3.1",
+ "@nuxt/schema": "4.3.1",
+ "@nuxt/telemetry": "^2.7.0",
+ "@nuxt/vite-builder": "4.3.1",
+ "@unhead/vue": "^2.1.3",
+ "@vue/shared": "^3.5.27",
+ "c12": "^3.3.3",
+ "chokidar": "^5.0.0",
"compatx": "^0.2.0",
"consola": "^3.4.2",
"cookie-es": "^2.0.0",
"defu": "^6.1.4",
"destr": "^2.0.5",
- "devalue": "^5.1.1",
+ "devalue": "^5.6.2",
"errx": "^0.1.0",
- "esbuild": "^0.25.8",
"escape-string-regexp": "^5.0.0",
- "estree-walker": "^3.0.3",
- "exsolve": "^1.0.7",
- "h3": "^1.15.3",
+ "exsolve": "^1.0.8",
+ "h3": "^1.15.5",
"hookable": "^5.5.3",
"ignore": "^7.0.5",
"impound": "^1.0.0",
- "jiti": "^2.4.2",
+ "jiti": "^2.6.1",
"klona": "^2.0.6",
- "knitwork": "^1.2.0",
- "magic-string": "^0.30.17",
- "mlly": "^1.7.4",
- "mocked-exports": "^0.1.1",
+ "knitwork": "^1.3.0",
+ "magic-string": "^0.30.21",
+ "mlly": "^1.8.0",
"nanotar": "^0.2.0",
- "nitropack": "^2.12.3",
- "nypm": "^0.6.0",
- "ofetch": "^1.4.1",
+ "nypm": "^0.6.5",
+ "ofetch": "^1.5.1",
"ohash": "^2.0.11",
- "on-change": "^5.0.1",
- "oxc-minify": "^0.77.3",
- "oxc-parser": "^0.77.3",
- "oxc-transform": "^0.77.3",
- "oxc-walker": "^0.4.0",
+ "on-change": "^6.0.2",
+ "oxc-minify": "^0.112.0",
+ "oxc-parser": "^0.112.0",
+ "oxc-transform": "^0.112.0",
+ "oxc-walker": "^0.7.0",
"pathe": "^2.0.3",
- "perfect-debounce": "^1.0.0",
- "pkg-types": "^2.2.0",
- "radix3": "^1.1.2",
+ "perfect-debounce": "^2.1.0",
+ "pkg-types": "^2.3.0",
+ "rou3": "^0.7.12",
"scule": "^1.3.0",
- "semver": "^7.7.2",
- "std-env": "^3.9.0",
- "strip-literal": "^3.0.0",
- "tinyglobby": "0.2.14",
- "ufo": "^1.6.1",
+ "semver": "^7.7.4",
+ "std-env": "^3.10.0",
+ "tinyglobby": "^0.2.15",
+ "ufo": "^1.6.3",
"ultrahtml": "^1.6.0",
"uncrypto": "^0.1.3",
- "unctx": "^2.4.1",
- "unimport": "^5.1.0",
- "unplugin": "^2.3.5",
- "unplugin-vue-router": "^0.14.0",
- "unstorage": "^1.16.1",
+ "unctx": "^2.5.0",
+ "unimport": "^5.6.0",
+ "unplugin": "^3.0.0",
+ "unplugin-vue-router": "^0.19.2",
"untyped": "^2.0.0",
- "vue": "^3.5.17",
- "vue-bundle-renderer": "^2.1.1",
- "vue-devtools-stub": "^0.1.0",
- "vue-router": "^4.5.1"
+ "vue": "^3.5.27",
+ "vue-router": "^4.6.4"
},
"bin": {
"nuxi": "bin/nuxt.mjs",
@@ -9145,29 +8093,47 @@
}
}
},
+ "node_modules/nuxt/node_modules/perfect-debounce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
+ "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==",
+ "license": "MIT"
+ },
+ "node_modules/nuxt/node_modules/unplugin": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz",
+ "integrity": "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.5",
+ "picomatch": "^4.0.3",
+ "webpack-virtual-modules": "^0.6.2"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
"node_modules/nypm": {
- "version": "0.6.0",
- "resolved": "https://registry.npmmirror.com/nypm/-/nypm-0.6.0.tgz",
- "integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==",
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz",
+ "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==",
"license": "MIT",
"dependencies": {
- "citty": "^0.1.6",
- "consola": "^3.4.0",
+ "citty": "^0.2.0",
"pathe": "^2.0.3",
- "pkg-types": "^2.0.0",
- "tinyexec": "^0.3.2"
+ "tinyexec": "^1.0.2"
},
"bin": {
"nypm": "dist/cli.mjs"
},
"engines": {
- "node": "^14.16.0 || >=16.10.0"
+ "node": ">=18"
}
},
- "node_modules/nypm/node_modules/tinyexec": {
- "version": "0.3.2",
- "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz",
- "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+ "node_modules/nypm/node_modules/citty": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz",
+ "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==",
"license": "MIT"
},
"node_modules/object-assign": {
@@ -9188,29 +8154,33 @@
"node": ">= 6"
}
},
- "node_modules/object-inspect": {
- "version": "1.13.4",
- "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
- "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
+ "node_modules/obug": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
+ "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT"
},
"node_modules/ofetch": {
- "version": "1.4.1",
- "resolved": "https://registry.npmmirror.com/ofetch/-/ofetch-1.4.1.tgz",
- "integrity": "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==",
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz",
+ "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==",
"license": "MIT",
"dependencies": {
- "destr": "^2.0.3",
- "node-fetch-native": "^1.6.4",
- "ufo": "^1.5.4"
+ "destr": "^2.0.5",
+ "node-fetch-native": "^1.6.7",
+ "ufo": "^1.6.1"
}
},
+ "node_modules/ogl": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmmirror.com/ogl/-/ogl-1.0.11.tgz",
+ "integrity": "sha512-kUpC154AFfxi16pmZUK4jk3J+8zxwTWGPo03EoYA8QPbzikHoaC82n6pNTbd+oEaJonaE8aPWBlX7ad9zrqLsA==",
+ "license": "Unlicense"
+ },
"node_modules/ohash": {
"version": "2.0.11",
"resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz",
@@ -9218,12 +8188,12 @@
"license": "MIT"
},
"node_modules/on-change": {
- "version": "5.0.1",
- "resolved": "https://registry.npmmirror.com/on-change/-/on-change-5.0.1.tgz",
- "integrity": "sha512-n7THCP7RkyReRSLkJb8kUWoNsxUIBxTkIp3JKno+sEz6o/9AJ3w3P9fzQkITEkMwyTKJjZciF3v/pVoouxZZMg==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/on-change/-/on-change-6.0.2.tgz",
+ "integrity": "sha512-08+12qcOVEA0fS9g/VxKS27HaT94nRutUT77J2dr8zv/unzXopvhBuF8tNLWsoLQ5IgrQ6eptGeGqUYat82U1w==",
"license": "MIT",
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sindresorhus/on-change?sponsor=1"
@@ -9250,18 +8220,9 @@
"wrappy": "1"
}
},
- "node_modules/one-time": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/one-time/-/one-time-1.0.0.tgz",
- "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
- "license": "MIT",
- "dependencies": {
- "fn.name": "1.x.x"
- }
- },
"node_modules/onetime": {
"version": "6.0.0",
- "resolved": "https://registry.npmmirror.com/onetime/-/onetime-6.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
"license": "MIT",
"dependencies": {
@@ -9281,7 +8242,7 @@
},
"node_modules/open": {
"version": "8.4.2",
- "resolved": "https://registry.npmmirror.com/open/-/open-8.4.2.tgz",
+ "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
"integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
"license": "MIT",
"dependencies": {
@@ -9298,7 +8259,7 @@
},
"node_modules/open/node_modules/is-docker": {
"version": "2.2.1",
- "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"license": "MIT",
"bin": {
@@ -9313,7 +8274,7 @@
},
"node_modules/open/node_modules/is-wsl": {
"version": "2.2.0",
- "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"license": "MIT",
"dependencies": {
@@ -9324,190 +8285,120 @@
}
},
"node_modules/oxc-minify": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/oxc-minify/-/oxc-minify-0.77.3.tgz",
- "integrity": "sha512-fYCSYazHno31eATVyHNyP2MEEMrVLaKVglac7bIoJC/qlb3x+Vqhv4eUViseOkoGM46rb9k8ZdDwhsEMtFUQhA==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/oxc-minify/-/oxc-minify-0.112.0.tgz",
+ "integrity": "sha512-rkVSeeIRSt+RYI9uX6xonBpLUpvZyegxIg0UL87ev7YAfUqp7IIZlRjkgQN5Us1lyXD//TOo0Dcuuro/TYOWoQ==",
"license": "MIT",
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/sponsors/Boshen"
},
"optionalDependencies": {
- "@oxc-minify/binding-android-arm64": "0.77.3",
- "@oxc-minify/binding-darwin-arm64": "0.77.3",
- "@oxc-minify/binding-darwin-x64": "0.77.3",
- "@oxc-minify/binding-freebsd-x64": "0.77.3",
- "@oxc-minify/binding-linux-arm-gnueabihf": "0.77.3",
- "@oxc-minify/binding-linux-arm-musleabihf": "0.77.3",
- "@oxc-minify/binding-linux-arm64-gnu": "0.77.3",
- "@oxc-minify/binding-linux-arm64-musl": "0.77.3",
- "@oxc-minify/binding-linux-riscv64-gnu": "0.77.3",
- "@oxc-minify/binding-linux-s390x-gnu": "0.77.3",
- "@oxc-minify/binding-linux-x64-gnu": "0.77.3",
- "@oxc-minify/binding-linux-x64-musl": "0.77.3",
- "@oxc-minify/binding-wasm32-wasi": "0.77.3",
- "@oxc-minify/binding-win32-arm64-msvc": "0.77.3",
- "@oxc-minify/binding-win32-x64-msvc": "0.77.3"
+ "@oxc-minify/binding-android-arm-eabi": "0.112.0",
+ "@oxc-minify/binding-android-arm64": "0.112.0",
+ "@oxc-minify/binding-darwin-arm64": "0.112.0",
+ "@oxc-minify/binding-darwin-x64": "0.112.0",
+ "@oxc-minify/binding-freebsd-x64": "0.112.0",
+ "@oxc-minify/binding-linux-arm-gnueabihf": "0.112.0",
+ "@oxc-minify/binding-linux-arm-musleabihf": "0.112.0",
+ "@oxc-minify/binding-linux-arm64-gnu": "0.112.0",
+ "@oxc-minify/binding-linux-arm64-musl": "0.112.0",
+ "@oxc-minify/binding-linux-ppc64-gnu": "0.112.0",
+ "@oxc-minify/binding-linux-riscv64-gnu": "0.112.0",
+ "@oxc-minify/binding-linux-riscv64-musl": "0.112.0",
+ "@oxc-minify/binding-linux-s390x-gnu": "0.112.0",
+ "@oxc-minify/binding-linux-x64-gnu": "0.112.0",
+ "@oxc-minify/binding-linux-x64-musl": "0.112.0",
+ "@oxc-minify/binding-openharmony-arm64": "0.112.0",
+ "@oxc-minify/binding-wasm32-wasi": "0.112.0",
+ "@oxc-minify/binding-win32-arm64-msvc": "0.112.0",
+ "@oxc-minify/binding-win32-ia32-msvc": "0.112.0",
+ "@oxc-minify/binding-win32-x64-msvc": "0.112.0"
}
},
"node_modules/oxc-parser": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/oxc-parser/-/oxc-parser-0.77.3.tgz",
- "integrity": "sha512-1h7nXjL0IGRT539tReIadfIjgrPPuuD6HmQGsgKdOxMEZGzfMeBk19bfg+sXMQi462cCnu5s5IGTEhOOlcVt1w==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.112.0.tgz",
+ "integrity": "sha512-7rQ3QdJwobMQLMZwQaPuPYMEF2fDRZwf51lZ//V+bA37nejjKW5ifMHbbCwvA889Y4RLhT+/wLJpPRhAoBaZYw==",
"license": "MIT",
"dependencies": {
- "@oxc-project/types": "^0.77.3"
+ "@oxc-project/types": "^0.112.0"
},
"engines": {
- "node": ">=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/sponsors/Boshen"
},
"optionalDependencies": {
- "@oxc-parser/binding-android-arm64": "0.77.3",
- "@oxc-parser/binding-darwin-arm64": "0.77.3",
- "@oxc-parser/binding-darwin-x64": "0.77.3",
- "@oxc-parser/binding-freebsd-x64": "0.77.3",
- "@oxc-parser/binding-linux-arm-gnueabihf": "0.77.3",
- "@oxc-parser/binding-linux-arm-musleabihf": "0.77.3",
- "@oxc-parser/binding-linux-arm64-gnu": "0.77.3",
- "@oxc-parser/binding-linux-arm64-musl": "0.77.3",
- "@oxc-parser/binding-linux-riscv64-gnu": "0.77.3",
- "@oxc-parser/binding-linux-s390x-gnu": "0.77.3",
- "@oxc-parser/binding-linux-x64-gnu": "0.77.3",
- "@oxc-parser/binding-linux-x64-musl": "0.77.3",
- "@oxc-parser/binding-wasm32-wasi": "0.77.3",
- "@oxc-parser/binding-win32-arm64-msvc": "0.77.3",
- "@oxc-parser/binding-win32-x64-msvc": "0.77.3"
+ "@oxc-parser/binding-android-arm-eabi": "0.112.0",
+ "@oxc-parser/binding-android-arm64": "0.112.0",
+ "@oxc-parser/binding-darwin-arm64": "0.112.0",
+ "@oxc-parser/binding-darwin-x64": "0.112.0",
+ "@oxc-parser/binding-freebsd-x64": "0.112.0",
+ "@oxc-parser/binding-linux-arm-gnueabihf": "0.112.0",
+ "@oxc-parser/binding-linux-arm-musleabihf": "0.112.0",
+ "@oxc-parser/binding-linux-arm64-gnu": "0.112.0",
+ "@oxc-parser/binding-linux-arm64-musl": "0.112.0",
+ "@oxc-parser/binding-linux-ppc64-gnu": "0.112.0",
+ "@oxc-parser/binding-linux-riscv64-gnu": "0.112.0",
+ "@oxc-parser/binding-linux-riscv64-musl": "0.112.0",
+ "@oxc-parser/binding-linux-s390x-gnu": "0.112.0",
+ "@oxc-parser/binding-linux-x64-gnu": "0.112.0",
+ "@oxc-parser/binding-linux-x64-musl": "0.112.0",
+ "@oxc-parser/binding-openharmony-arm64": "0.112.0",
+ "@oxc-parser/binding-wasm32-wasi": "0.112.0",
+ "@oxc-parser/binding-win32-arm64-msvc": "0.112.0",
+ "@oxc-parser/binding-win32-ia32-msvc": "0.112.0",
+ "@oxc-parser/binding-win32-x64-msvc": "0.112.0"
}
},
"node_modules/oxc-transform": {
- "version": "0.77.3",
- "resolved": "https://registry.npmmirror.com/oxc-transform/-/oxc-transform-0.77.3.tgz",
- "integrity": "sha512-cFiyrki2/Tgs9i0GUe8zmnJNZsGrHtNoDcyo1zTHQl/Ak0/04PIBHzurX7ibMadxfRNIn0XG0tpNrrkGDJ3k6g==",
+ "version": "0.112.0",
+ "resolved": "https://registry.npmjs.org/oxc-transform/-/oxc-transform-0.112.0.tgz",
+ "integrity": "sha512-cIRRvZgrHfsAHrkt8LWdAX4+Do8R0MzQSfeo9yzErzHeYiuyNiP4PCTPbOy/wBXL4MYzt3ebrBa5jt3akQkKAg==",
"license": "MIT",
"engines": {
- "node": ">=14.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/sponsors/Boshen"
},
"optionalDependencies": {
- "@oxc-transform/binding-android-arm64": "0.77.3",
- "@oxc-transform/binding-darwin-arm64": "0.77.3",
- "@oxc-transform/binding-darwin-x64": "0.77.3",
- "@oxc-transform/binding-freebsd-x64": "0.77.3",
- "@oxc-transform/binding-linux-arm-gnueabihf": "0.77.3",
- "@oxc-transform/binding-linux-arm-musleabihf": "0.77.3",
- "@oxc-transform/binding-linux-arm64-gnu": "0.77.3",
- "@oxc-transform/binding-linux-arm64-musl": "0.77.3",
- "@oxc-transform/binding-linux-riscv64-gnu": "0.77.3",
- "@oxc-transform/binding-linux-s390x-gnu": "0.77.3",
- "@oxc-transform/binding-linux-x64-gnu": "0.77.3",
- "@oxc-transform/binding-linux-x64-musl": "0.77.3",
- "@oxc-transform/binding-wasm32-wasi": "0.77.3",
- "@oxc-transform/binding-win32-arm64-msvc": "0.77.3",
- "@oxc-transform/binding-win32-x64-msvc": "0.77.3"
+ "@oxc-transform/binding-android-arm-eabi": "0.112.0",
+ "@oxc-transform/binding-android-arm64": "0.112.0",
+ "@oxc-transform/binding-darwin-arm64": "0.112.0",
+ "@oxc-transform/binding-darwin-x64": "0.112.0",
+ "@oxc-transform/binding-freebsd-x64": "0.112.0",
+ "@oxc-transform/binding-linux-arm-gnueabihf": "0.112.0",
+ "@oxc-transform/binding-linux-arm-musleabihf": "0.112.0",
+ "@oxc-transform/binding-linux-arm64-gnu": "0.112.0",
+ "@oxc-transform/binding-linux-arm64-musl": "0.112.0",
+ "@oxc-transform/binding-linux-ppc64-gnu": "0.112.0",
+ "@oxc-transform/binding-linux-riscv64-gnu": "0.112.0",
+ "@oxc-transform/binding-linux-riscv64-musl": "0.112.0",
+ "@oxc-transform/binding-linux-s390x-gnu": "0.112.0",
+ "@oxc-transform/binding-linux-x64-gnu": "0.112.0",
+ "@oxc-transform/binding-linux-x64-musl": "0.112.0",
+ "@oxc-transform/binding-openharmony-arm64": "0.112.0",
+ "@oxc-transform/binding-wasm32-wasi": "0.112.0",
+ "@oxc-transform/binding-win32-arm64-msvc": "0.112.0",
+ "@oxc-transform/binding-win32-ia32-msvc": "0.112.0",
+ "@oxc-transform/binding-win32-x64-msvc": "0.112.0"
}
},
"node_modules/oxc-walker": {
- "version": "0.4.0",
- "resolved": "https://registry.npmmirror.com/oxc-walker/-/oxc-walker-0.4.0.tgz",
- "integrity": "sha512-x5TJAZQD3kRnRBGZ+8uryMZUwkTYddwzBftkqyJIcmpBOXmoK/fwriRKATjZroR2d+aS7+2w1B0oz189bBTwfw==",
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/oxc-walker/-/oxc-walker-0.7.0.tgz",
+ "integrity": "sha512-54B4KUhrzbzc4sKvKwVYm7E2PgeROpGba0/2nlNZMqfDyca+yOor5IMb4WLGBatGDT0nkzYdYuzylg7n3YfB7A==",
"license": "MIT",
"dependencies": {
- "estree-walker": "^3.0.3",
"magic-regexp": "^0.10.0"
},
"peerDependencies": {
- "oxc-parser": ">=0.72.0"
- }
- },
- "node_modules/p-event": {
- "version": "6.0.1",
- "resolved": "https://registry.npmmirror.com/p-event/-/p-event-6.0.1.tgz",
- "integrity": "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==",
- "license": "MIT",
- "dependencies": {
- "p-timeout": "^6.1.2"
- },
- "engines": {
- "node": ">=16.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-limit": {
- "version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-4.0.0.tgz",
- "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
- "license": "MIT",
- "dependencies": {
- "yocto-queue": "^1.0.0"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-locate": {
- "version": "6.0.0",
- "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-6.0.0.tgz",
- "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
- "license": "MIT",
- "dependencies": {
- "p-limit": "^4.0.0"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-map": {
- "version": "7.0.3",
- "resolved": "https://registry.npmmirror.com/p-map/-/p-map-7.0.3.tgz",
- "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-timeout": {
- "version": "6.1.4",
- "resolved": "https://registry.npmmirror.com/p-timeout/-/p-timeout-6.1.4.tgz",
- "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==",
- "license": "MIT",
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/p-wait-for": {
- "version": "5.0.2",
- "resolved": "https://registry.npmmirror.com/p-wait-for/-/p-wait-for-5.0.2.tgz",
- "integrity": "sha512-lwx6u1CotQYPVju77R+D0vFomni/AqRfqLmqQ8hekklqZ6gAY9rONh7lBQ0uxWMkC2AuX9b2DVAl8To0NyP1JA==",
- "license": "MIT",
- "dependencies": {
- "p-timeout": "^6.0.0"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "oxc-parser": ">=0.98.0"
}
},
"node_modules/package-json-from-dist": {
@@ -9516,60 +8407,6 @@
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
"license": "BlueOak-1.0.0"
},
- "node_modules/package-manager-detector": {
- "version": "1.3.0",
- "resolved": "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.3.0.tgz",
- "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==",
- "license": "MIT"
- },
- "node_modules/parse-gitignore": {
- "version": "2.0.0",
- "resolved": "https://registry.npmmirror.com/parse-gitignore/-/parse-gitignore-2.0.0.tgz",
- "integrity": "sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==",
- "license": "MIT",
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/parse-json": {
- "version": "8.3.0",
- "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-8.3.0.tgz",
- "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==",
- "license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.26.2",
- "index-to-position": "^1.1.0",
- "type-fest": "^4.39.1"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/parse-path": {
- "version": "7.1.0",
- "resolved": "https://registry.npmmirror.com/parse-path/-/parse-path-7.1.0.tgz",
- "integrity": "sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==",
- "license": "MIT",
- "dependencies": {
- "protocols": "^2.0.0"
- }
- },
- "node_modules/parse-url": {
- "version": "9.2.0",
- "resolved": "https://registry.npmmirror.com/parse-url/-/parse-url-9.2.0.tgz",
- "integrity": "sha512-bCgsFI+GeGWPAvAiUv63ZorMeif3/U0zaXABGJbOWt5OH2KCaPHF6S+0ok4aqM9RuIPGyZdx9tR9l13PsW4AYQ==",
- "license": "MIT",
- "dependencies": {
- "@types/parse-path": "^7.0.0",
- "parse-path": "^7.0.0"
- },
- "engines": {
- "node": ">=14.13.0"
- }
- },
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz",
@@ -9579,14 +8416,11 @@
"node": ">= 0.8"
}
},
- "node_modules/path-exists": {
- "version": "5.0.0",
- "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-5.0.0.tgz",
- "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
- "license": "MIT",
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- }
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+ "license": "MIT"
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
@@ -9640,35 +8474,18 @@
"integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
"license": "MIT"
},
- "node_modules/path-type": {
- "version": "6.0.0",
- "resolved": "https://registry.npmmirror.com/path-type/-/path-type-6.0.0.tgz",
- "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/pathe": {
"version": "2.0.3",
"resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"license": "MIT"
},
- "node_modules/pend": {
- "version": "1.2.0",
- "resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz",
- "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
- "license": "MIT"
- },
"node_modules/perfect-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/picocolors": {
"version": "1.1.1",
@@ -9739,9 +8556,9 @@
}
},
"node_modules/pkg-types": {
- "version": "2.2.0",
- "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.2.0.tgz",
- "integrity": "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz",
+ "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
"license": "MIT",
"dependencies": {
"confbox": "^0.2.2",
@@ -9749,6 +8566,24 @@
"pathe": "^2.0.3"
}
},
+ "node_modules/popmotion": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmmirror.com/popmotion/-/popmotion-11.0.5.tgz",
+ "integrity": "sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA==",
+ "license": "MIT",
+ "dependencies": {
+ "framesync": "6.1.2",
+ "hey-listen": "^1.0.8",
+ "style-value-types": "5.1.2",
+ "tslib": "2.4.0"
+ }
+ },
+ "node_modules/popmotion/node_modules/tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
+ "license": "0BSD"
+ },
"node_modules/portfinder": {
"version": "1.0.37",
"resolved": "https://registry.npmmirror.com/portfinder/-/portfinder-1.0.37.tgz",
@@ -9792,7 +8627,7 @@
},
"node_modules/postcss-calc": {
"version": "10.1.1",
- "resolved": "https://registry.npmmirror.com/postcss-calc/-/postcss-calc-10.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.1.1.tgz",
"integrity": "sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==",
"license": "MIT",
"dependencies": {
@@ -9807,12 +8642,12 @@
}
},
"node_modules/postcss-colormin": {
- "version": "7.0.4",
- "resolved": "https://registry.npmmirror.com/postcss-colormin/-/postcss-colormin-7.0.4.tgz",
- "integrity": "sha512-ziQuVzQZBROpKpfeDwmrG+Vvlr0YWmY/ZAk99XD+mGEBuEojoFekL41NCsdhyNUtZI7DPOoIWIR7vQQK9xwluw==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.5.tgz",
+ "integrity": "sha512-ekIBP/nwzRWhEMmIxHHbXHcMdzd1HIUzBECaj5KEdLz9DVP2HzT065sEhvOx1dkLjYW7jyD0CngThx6bpFi2fA==",
"license": "MIT",
"dependencies": {
- "browserslist": "^4.25.1",
+ "browserslist": "^4.27.0",
"caniuse-api": "^3.0.0",
"colord": "^2.9.3",
"postcss-value-parser": "^4.2.0"
@@ -9825,12 +8660,12 @@
}
},
"node_modules/postcss-convert-values": {
- "version": "7.0.6",
- "resolved": "https://registry.npmmirror.com/postcss-convert-values/-/postcss-convert-values-7.0.6.tgz",
- "integrity": "sha512-MD/eb39Mr60hvgrqpXsgbiqluawYg/8K4nKsqRsuDX9f+xN1j6awZCUv/5tLH8ak3vYp/EMXwdcnXvfZYiejCQ==",
+ "version": "7.0.8",
+ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.8.tgz",
+ "integrity": "sha512-+XNKuPfkHTCEo499VzLMYn94TiL3r9YqRE3Ty+jP7UX4qjewUONey1t7CG21lrlTLN07GtGM8MqFVp86D4uKJg==",
"license": "MIT",
"dependencies": {
- "browserslist": "^4.25.1",
+ "browserslist": "^4.27.0",
"postcss-value-parser": "^4.2.0"
},
"engines": {
@@ -9841,9 +8676,9 @@
}
},
"node_modules/postcss-discard-comments": {
- "version": "7.0.4",
- "resolved": "https://registry.npmmirror.com/postcss-discard-comments/-/postcss-discard-comments-7.0.4.tgz",
- "integrity": "sha512-6tCUoql/ipWwKtVP/xYiFf1U9QgJ0PUvxN7pTcsQ8Ns3Fnwq1pU5D5s1MhT/XySeLq6GXNvn37U46Ded0TckWg==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.5.tgz",
+ "integrity": "sha512-IR2Eja8WfYgN5n32vEGSctVQ1+JARfu4UH8M7bgGh1bC+xI/obsPJXaBpQF7MAByvgwZinhpHpdrmXtvVVlKcQ==",
"license": "MIT",
"dependencies": {
"postcss-selector-parser": "^7.1.0"
@@ -9857,7 +8692,7 @@
},
"node_modules/postcss-discard-duplicates": {
"version": "7.0.2",
- "resolved": "https://registry.npmmirror.com/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.2.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.2.tgz",
"integrity": "sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w==",
"license": "MIT",
"engines": {
@@ -9869,7 +8704,7 @@
},
"node_modules/postcss-discard-empty": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-discard-empty/-/postcss-discard-empty-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.1.tgz",
"integrity": "sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg==",
"license": "MIT",
"engines": {
@@ -9881,7 +8716,7 @@
},
"node_modules/postcss-discard-overridden": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-discard-overridden/-/postcss-discard-overridden-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.1.tgz",
"integrity": "sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg==",
"license": "MIT",
"engines": {
@@ -9908,26 +8743,6 @@
"postcss": "^8.0.0"
}
},
- "node_modules/postcss-import/node_modules/resolve": {
- "version": "1.22.10",
- "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz",
- "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.16.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/postcss-js": {
"version": "4.0.1",
"resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.0.1.tgz",
@@ -9984,7 +8799,7 @@
},
"node_modules/postcss-merge-longhand": {
"version": "7.0.5",
- "resolved": "https://registry.npmmirror.com/postcss-merge-longhand/-/postcss-merge-longhand-7.0.5.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.5.tgz",
"integrity": "sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw==",
"license": "MIT",
"dependencies": {
@@ -9999,12 +8814,12 @@
}
},
"node_modules/postcss-merge-rules": {
- "version": "7.0.6",
- "resolved": "https://registry.npmmirror.com/postcss-merge-rules/-/postcss-merge-rules-7.0.6.tgz",
- "integrity": "sha512-2jIPT4Tzs8K87tvgCpSukRQ2jjd+hH6Bb8rEEOUDmmhOeTcqDg5fEFK8uKIu+Pvc3//sm3Uu6FRqfyv7YF7+BQ==",
+ "version": "7.0.7",
+ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.7.tgz",
+ "integrity": "sha512-njWJrd/Ms6XViwowaaCc+/vqhPG3SmXn725AGrnl+BgTuRPEacjiLEaGq16J6XirMJbtKkTwnt67SS+e2WGoew==",
"license": "MIT",
"dependencies": {
- "browserslist": "^4.25.1",
+ "browserslist": "^4.27.0",
"caniuse-api": "^3.0.0",
"cssnano-utils": "^5.0.1",
"postcss-selector-parser": "^7.1.0"
@@ -10018,7 +8833,7 @@
},
"node_modules/postcss-minify-font-values": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-minify-font-values/-/postcss-minify-font-values-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.1.tgz",
"integrity": "sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ==",
"license": "MIT",
"dependencies": {
@@ -10033,7 +8848,7 @@
},
"node_modules/postcss-minify-gradients": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-minify-gradients/-/postcss-minify-gradients-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.1.tgz",
"integrity": "sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A==",
"license": "MIT",
"dependencies": {
@@ -10049,12 +8864,12 @@
}
},
"node_modules/postcss-minify-params": {
- "version": "7.0.4",
- "resolved": "https://registry.npmmirror.com/postcss-minify-params/-/postcss-minify-params-7.0.4.tgz",
- "integrity": "sha512-3OqqUddfH8c2e7M35W6zIwv7jssM/3miF9cbCSb1iJiWvtguQjlxZGIHK9JRmc8XAKmE2PFGtHSM7g/VcW97sw==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.5.tgz",
+ "integrity": "sha512-FGK9ky02h6Ighn3UihsyeAH5XmLEE2MSGH5Tc4tXMFtEDx7B+zTG6hD/+/cT+fbF7PbYojsmmWjyTwFwW1JKQQ==",
"license": "MIT",
"dependencies": {
- "browserslist": "^4.25.1",
+ "browserslist": "^4.27.0",
"cssnano-utils": "^5.0.1",
"postcss-value-parser": "^4.2.0"
},
@@ -10067,7 +8882,7 @@
},
"node_modules/postcss-minify-selectors": {
"version": "7.0.5",
- "resolved": "https://registry.npmmirror.com/postcss-minify-selectors/-/postcss-minify-selectors-7.0.5.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.5.tgz",
"integrity": "sha512-x2/IvofHcdIrAm9Q+p06ZD1h6FPcQ32WtCRVodJLDR+WMn8EVHI1kvLxZuGKz/9EY5nAmI6lIQIrpo4tBy5+ug==",
"license": "MIT",
"dependencies": {
@@ -10148,7 +8963,7 @@
},
"node_modules/postcss-normalize-charset": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-normalize-charset/-/postcss-normalize-charset-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.1.tgz",
"integrity": "sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ==",
"license": "MIT",
"engines": {
@@ -10160,7 +8975,7 @@
},
"node_modules/postcss-normalize-display-values": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.1.tgz",
"integrity": "sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ==",
"license": "MIT",
"dependencies": {
@@ -10175,7 +8990,7 @@
},
"node_modules/postcss-normalize-positions": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-normalize-positions/-/postcss-normalize-positions-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.1.tgz",
"integrity": "sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ==",
"license": "MIT",
"dependencies": {
@@ -10190,7 +9005,7 @@
},
"node_modules/postcss-normalize-repeat-style": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.1.tgz",
"integrity": "sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ==",
"license": "MIT",
"dependencies": {
@@ -10205,7 +9020,7 @@
},
"node_modules/postcss-normalize-string": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-normalize-string/-/postcss-normalize-string-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.1.tgz",
"integrity": "sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ==",
"license": "MIT",
"dependencies": {
@@ -10220,7 +9035,7 @@
},
"node_modules/postcss-normalize-timing-functions": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.1.tgz",
"integrity": "sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg==",
"license": "MIT",
"dependencies": {
@@ -10234,12 +9049,12 @@
}
},
"node_modules/postcss-normalize-unicode": {
- "version": "7.0.4",
- "resolved": "https://registry.npmmirror.com/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.4.tgz",
- "integrity": "sha512-LvIURTi1sQoZqj8mEIE8R15yvM+OhbR1avynMtI9bUzj5gGKR/gfZFd8O7VMj0QgJaIFzxDwxGl/ASMYAkqO8g==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.5.tgz",
+ "integrity": "sha512-X6BBwiRxVaFHrb2WyBMddIeB5HBjJcAaUHyhLrM2FsxSq5TFqcHSsK7Zu1otag+o0ZphQGJewGH1tAyrD0zX1Q==",
"license": "MIT",
"dependencies": {
- "browserslist": "^4.25.1",
+ "browserslist": "^4.27.0",
"postcss-value-parser": "^4.2.0"
},
"engines": {
@@ -10251,7 +9066,7 @@
},
"node_modules/postcss-normalize-url": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-normalize-url/-/postcss-normalize-url-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.1.tgz",
"integrity": "sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ==",
"license": "MIT",
"dependencies": {
@@ -10266,7 +9081,7 @@
},
"node_modules/postcss-normalize-whitespace": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.1.tgz",
"integrity": "sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA==",
"license": "MIT",
"dependencies": {
@@ -10281,7 +9096,7 @@
},
"node_modules/postcss-ordered-values": {
"version": "7.0.2",
- "resolved": "https://registry.npmmirror.com/postcss-ordered-values/-/postcss-ordered-values-7.0.2.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.2.tgz",
"integrity": "sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw==",
"license": "MIT",
"dependencies": {
@@ -10296,12 +9111,12 @@
}
},
"node_modules/postcss-reduce-initial": {
- "version": "7.0.4",
- "resolved": "https://registry.npmmirror.com/postcss-reduce-initial/-/postcss-reduce-initial-7.0.4.tgz",
- "integrity": "sha512-rdIC9IlMBn7zJo6puim58Xd++0HdbvHeHaPgXsimMfG1ijC5A9ULvNLSE0rUKVJOvNMcwewW4Ga21ngyJjY/+Q==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.5.tgz",
+ "integrity": "sha512-RHagHLidG8hTZcnr4FpyMB2jtgd/OcyAazjMhoy5qmWJOx1uxKh4ntk0Pb46ajKM0rkf32lRH4C8c9qQiPR6IA==",
"license": "MIT",
"dependencies": {
- "browserslist": "^4.25.1",
+ "browserslist": "^4.27.0",
"caniuse-api": "^3.0.0"
},
"engines": {
@@ -10313,7 +9128,7 @@
},
"node_modules/postcss-reduce-transforms": {
"version": "7.0.1",
- "resolved": "https://registry.npmmirror.com/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.1.tgz",
"integrity": "sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g==",
"license": "MIT",
"dependencies": {
@@ -10341,7 +9156,7 @@
},
"node_modules/postcss-svgo": {
"version": "7.1.0",
- "resolved": "https://registry.npmmirror.com/postcss-svgo/-/postcss-svgo-7.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.0.tgz",
"integrity": "sha512-KnAlfmhtoLz6IuU3Sij2ycusNs4jPW+QoFE5kuuUOK8awR6tMxZQrs5Ey3BUz7nFCzT3eqyFgqkyrHiaU2xx3w==",
"license": "MIT",
"dependencies": {
@@ -10357,7 +9172,7 @@
},
"node_modules/postcss-unique-selectors": {
"version": "7.0.4",
- "resolved": "https://registry.npmmirror.com/postcss-unique-selectors/-/postcss-unique-selectors-7.0.4.tgz",
+ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.4.tgz",
"integrity": "sha512-pmlZjsmEAG7cHd7uK3ZiNSW6otSZ13RHuZ/4cDN/bVglS5EpF2r2oxY99SuOHa8m7AWoBCelTS3JPpzsIs8skQ==",
"license": "MIT",
"dependencies": {
@@ -10376,23 +9191,6 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"license": "MIT"
},
- "node_modules/postcss-values-parser": {
- "version": "6.0.2",
- "resolved": "https://registry.npmmirror.com/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz",
- "integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==",
- "license": "MPL-2.0",
- "dependencies": {
- "color-name": "^1.1.4",
- "is-url-superb": "^4.0.0",
- "quote-unquote": "^1.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "postcss": "^8.2.9"
- }
- },
"node_modules/postcss/node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz",
@@ -10411,51 +9209,13 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
- "node_modules/precinct": {
- "version": "12.2.0",
- "resolved": "https://registry.npmmirror.com/precinct/-/precinct-12.2.0.tgz",
- "integrity": "sha512-NFBMuwIfaJ4SocE9YXPU/n4AcNSoFMVFjP72nvl3cx69j/ke61/hPOWFREVxLkFhhEGnA8ZuVfTqJBa+PK3b5w==",
- "license": "MIT",
- "dependencies": {
- "@dependents/detective-less": "^5.0.1",
- "commander": "^12.1.0",
- "detective-amd": "^6.0.1",
- "detective-cjs": "^6.0.1",
- "detective-es6": "^5.0.1",
- "detective-postcss": "^7.0.1",
- "detective-sass": "^6.0.1",
- "detective-scss": "^5.0.1",
- "detective-stylus": "^5.0.1",
- "detective-typescript": "^14.0.0",
- "detective-vue2": "^2.2.0",
- "module-definition": "^6.0.1",
- "node-source-walk": "^7.0.1",
- "postcss": "^8.5.1",
- "typescript": "^5.7.3"
- },
- "bin": {
- "precinct": "bin/cli.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/precinct/node_modules/commander": {
- "version": "12.1.0",
- "resolved": "https://registry.npmmirror.com/commander/-/commander-12.1.0.tgz",
- "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/pretty-bytes": {
- "version": "6.1.1",
- "resolved": "https://registry.npmmirror.com/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
- "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-7.1.0.tgz",
+ "integrity": "sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==",
"license": "MIT",
"engines": {
- "node": "^14.13.1 || >=16.0.0"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -10463,7 +9223,7 @@
},
"node_modules/process": {
"version": "0.11.10",
- "resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
"license": "MIT",
"engines": {
@@ -10472,64 +9232,20 @@
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT"
},
- "node_modules/prompts": {
- "version": "2.4.2",
- "resolved": "https://registry.npmmirror.com/prompts/-/prompts-2.4.2.tgz",
- "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
- "license": "MIT",
- "dependencies": {
- "kleur": "^3.0.3",
- "sisteransi": "^1.0.5"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/protocols": {
- "version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/protocols/-/protocols-2.0.2.tgz",
- "integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==",
- "license": "MIT"
- },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
- "node_modules/pump": {
- "version": "3.0.3",
- "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.3.tgz",
- "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
- "license": "MIT",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "node_modules/qs": {
- "version": "6.14.0",
- "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz",
- "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
- "license": "BSD-3-Clause",
- "dependencies": {
- "side-channel": "^1.1.0"
- },
- "engines": {
- "node": ">=0.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/quansync": {
- "version": "0.2.10",
- "resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.10.tgz",
- "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==",
+ "version": "0.2.11",
+ "resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz",
+ "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
"funding": [
{
"type": "individual",
@@ -10562,12 +9278,6 @@
],
"license": "MIT"
},
- "node_modules/quote-unquote": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/quote-unquote/-/quote-unquote-1.0.0.tgz",
- "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==",
- "license": "MIT"
- },
"node_modules/radix3": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/radix3/-/radix3-1.1.2.tgz",
@@ -10576,7 +9286,7 @@
},
"node_modules/randombytes": {
"version": "2.1.0",
- "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"license": "MIT",
"dependencies": {
@@ -10585,7 +9295,7 @@
},
"node_modules/range-parser": {
"version": "1.2.1",
- "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"license": "MIT",
"engines": {
@@ -10611,45 +9321,9 @@
"pify": "^2.3.0"
}
},
- "node_modules/read-package-up": {
- "version": "11.0.0",
- "resolved": "https://registry.npmmirror.com/read-package-up/-/read-package-up-11.0.0.tgz",
- "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==",
- "license": "MIT",
- "dependencies": {
- "find-up-simple": "^1.0.0",
- "read-pkg": "^9.0.0",
- "type-fest": "^4.6.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/read-pkg": {
- "version": "9.0.1",
- "resolved": "https://registry.npmmirror.com/read-pkg/-/read-pkg-9.0.1.tgz",
- "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==",
- "license": "MIT",
- "dependencies": {
- "@types/normalize-package-data": "^2.4.3",
- "normalize-package-data": "^6.0.0",
- "parse-json": "^8.0.0",
- "type-fest": "^4.6.0",
- "unicorn-magic": "^0.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/readable-stream": {
"version": "4.7.0",
- "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-4.7.0.tgz",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
"license": "MIT",
"dependencies": {
@@ -10665,7 +9339,7 @@
},
"node_modules/readdir-glob": {
"version": "1.1.3",
- "resolved": "https://registry.npmmirror.com/readdir-glob/-/readdir-glob-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
"integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
"license": "Apache-2.0",
"dependencies": {
@@ -10673,9 +9347,9 @@
}
},
"node_modules/readdir-glob/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -10685,12 +9359,12 @@
}
},
"node_modules/readdirp": {
- "version": "4.1.2",
- "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
- "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
+ "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
"license": "MIT",
"engines": {
- "node": ">= 14.18.0"
+ "node": ">= 20.19.0"
},
"funding": {
"type": "individual",
@@ -10699,7 +9373,7 @@
},
"node_modules/redis-errors": {
"version": "1.2.0",
- "resolved": "https://registry.npmmirror.com/redis-errors/-/redis-errors-1.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
"license": "MIT",
"engines": {
@@ -10708,7 +9382,7 @@
},
"node_modules/redis-parser": {
"version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/redis-parser/-/redis-parser-3.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"license": "MIT",
"dependencies": {
@@ -10720,19 +9394,13 @@
},
"node_modules/regexp-tree": {
"version": "0.1.27",
- "resolved": "https://registry.npmmirror.com/regexp-tree/-/regexp-tree-0.1.27.tgz",
+ "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz",
"integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==",
"license": "MIT",
"bin": {
"regexp-tree": "bin/regexp-tree"
}
},
- "node_modules/remove-trailing-separator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmmirror.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
- "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
- "license": "ISC"
- },
"node_modules/replace-in-file": {
"version": "6.3.5",
"resolved": "https://registry.npmmirror.com/replace-in-file/-/replace-in-file-6.3.5.tgz",
@@ -10782,9 +9450,9 @@
}
},
"node_modules/replace-in-file/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
@@ -10802,32 +9470,29 @@
"node": ">=0.10.0"
}
},
- "node_modules/require-package-name": {
- "version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/require-package-name/-/require-package-name-2.0.1.tgz",
- "integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==",
- "license": "MIT"
- },
"node_modules/resolve": {
- "version": "2.0.0-next.5",
- "resolved": "https://registry.npmmirror.com/resolve/-/resolve-2.0.0-next.5.tgz",
- "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==",
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
"license": "MIT",
"dependencies": {
- "is-core-module": "^2.13.0",
+ "is-core-module": "^2.16.1",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/resolve-from": {
"version": "5.0.0",
- "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"license": "MIT",
"engines": {
@@ -10909,9 +9574,9 @@
"license": "MIT"
},
"node_modules/rollup": {
- "version": "4.45.1",
- "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.45.1.tgz",
- "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==",
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
+ "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.8"
@@ -10924,33 +9589,38 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.45.1",
- "@rollup/rollup-android-arm64": "4.45.1",
- "@rollup/rollup-darwin-arm64": "4.45.1",
- "@rollup/rollup-darwin-x64": "4.45.1",
- "@rollup/rollup-freebsd-arm64": "4.45.1",
- "@rollup/rollup-freebsd-x64": "4.45.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.45.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.45.1",
- "@rollup/rollup-linux-arm64-gnu": "4.45.1",
- "@rollup/rollup-linux-arm64-musl": "4.45.1",
- "@rollup/rollup-linux-loongarch64-gnu": "4.45.1",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.45.1",
- "@rollup/rollup-linux-riscv64-musl": "4.45.1",
- "@rollup/rollup-linux-s390x-gnu": "4.45.1",
- "@rollup/rollup-linux-x64-gnu": "4.45.1",
- "@rollup/rollup-linux-x64-musl": "4.45.1",
- "@rollup/rollup-win32-arm64-msvc": "4.45.1",
- "@rollup/rollup-win32-ia32-msvc": "4.45.1",
- "@rollup/rollup-win32-x64-msvc": "4.45.1",
+ "@rollup/rollup-android-arm-eabi": "4.59.0",
+ "@rollup/rollup-android-arm64": "4.59.0",
+ "@rollup/rollup-darwin-arm64": "4.59.0",
+ "@rollup/rollup-darwin-x64": "4.59.0",
+ "@rollup/rollup-freebsd-arm64": "4.59.0",
+ "@rollup/rollup-freebsd-x64": "4.59.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.59.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.59.0",
+ "@rollup/rollup-linux-arm64-musl": "4.59.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.59.0",
+ "@rollup/rollup-linux-loong64-musl": "4.59.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.59.0",
+ "@rollup/rollup-linux-ppc64-musl": "4.59.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.59.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.59.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.59.0",
+ "@rollup/rollup-linux-x64-gnu": "4.59.0",
+ "@rollup/rollup-linux-x64-musl": "4.59.0",
+ "@rollup/rollup-openbsd-x64": "4.59.0",
+ "@rollup/rollup-openharmony-arm64": "4.59.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.59.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.59.0",
+ "@rollup/rollup-win32-x64-gnu": "4.59.0",
+ "@rollup/rollup-win32-x64-msvc": "4.59.0",
"fsevents": "~2.3.2"
}
},
"node_modules/rollup-plugin-visualizer": {
- "version": "6.0.3",
- "resolved": "https://registry.npmmirror.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.3.tgz",
- "integrity": "sha512-ZU41GwrkDcCpVoffviuM9Clwjy5fcUxlz0oMoTXTYsK+tcIFzbdacnrr2n8TXcHxbGKKXtOdjxM2HUS4HjkwIw==",
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.5.tgz",
+ "integrity": "sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==",
"license": "MIT",
"dependencies": {
"open": "^8.0.0",
@@ -10977,10 +9647,16 @@
}
}
},
+ "node_modules/rou3": {
+ "version": "0.7.12",
+ "resolved": "https://registry.npmjs.org/rou3/-/rou3-0.7.12.tgz",
+ "integrity": "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==",
+ "license": "MIT"
+ },
"node_modules/run-applescript": {
- "version": "7.0.0",
- "resolved": "https://registry.npmmirror.com/run-applescript/-/run-applescript-7.0.0.tgz",
- "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
+ "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -11049,20 +9725,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/safe-stable-stringify": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
- "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- }
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
},
"node_modules/sax": {
- "version": "1.4.1",
- "resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
- "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
- "license": "ISC"
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz",
+ "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
},
"node_modules/scule": {
"version": "1.3.0",
@@ -11071,9 +9747,9 @@
"license": "MIT"
},
"node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -11083,39 +9759,77 @@
}
},
"node_modules/send": {
- "version": "1.2.0",
- "resolved": "https://registry.npmmirror.com/send/-/send-1.2.0.tgz",
- "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
"license": "MIT",
"dependencies": {
- "debug": "^4.3.5",
+ "debug": "^4.4.3",
"encodeurl": "^2.0.0",
"escape-html": "^1.0.3",
"etag": "^1.8.1",
"fresh": "^2.0.0",
- "http-errors": "^2.0.0",
- "mime-types": "^3.0.1",
+ "http-errors": "^2.0.1",
+ "mime-types": "^3.0.2",
"ms": "^2.1.3",
"on-finished": "^2.4.1",
"range-parser": "^1.2.1",
- "statuses": "^2.0.1"
+ "statuses": "^2.0.2"
},
"engines": {
"node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/send/node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/send/node_modules/mime-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
+ "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/serialize-javascript": {
"version": "6.0.2",
- "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"license": "BSD-3-Clause",
"dependencies": {
"randombytes": "^2.1.0"
}
},
+ "node_modules/seroval": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.0.tgz",
+ "integrity": "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/serve-placeholder": {
"version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/serve-placeholder/-/serve-placeholder-2.0.2.tgz",
+ "resolved": "https://registry.npmjs.org/serve-placeholder/-/serve-placeholder-2.0.2.tgz",
"integrity": "sha512-/TMG8SboeiQbZJWRlfTCqMs2DD3SZgWp0kDQePz9yUuCnDfDh/92gf7/PxGhzXTKBIPASIHxFcZndoNbp6QOLQ==",
"license": "MIT",
"dependencies": {
@@ -11123,9 +9837,9 @@
}
},
"node_modules/serve-static": {
- "version": "2.2.0",
- "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-2.2.0.tgz",
- "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
+ "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
"license": "MIT",
"dependencies": {
"encodeurl": "^2.0.0",
@@ -11135,6 +9849,10 @@
},
"engines": {
"node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/setprototypeof": {
@@ -11166,7 +9884,7 @@
},
"node_modules/shell-quote": {
"version": "1.8.3",
- "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.3.tgz",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
"license": "MIT",
"engines": {
@@ -11176,78 +9894,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/side-channel": {
- "version": "1.1.0",
- "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3",
- "side-channel-list": "^1.0.0",
- "side-channel-map": "^1.0.1",
- "side-channel-weakmap": "^1.0.2"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-list": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz",
- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-map": {
- "version": "1.0.1",
- "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz",
- "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-weakmap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
- "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
- "license": "MIT",
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3",
- "side-channel-map": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz",
@@ -11261,9 +9907,9 @@
}
},
"node_modules/simple-git": {
- "version": "3.28.0",
- "resolved": "https://registry.npmmirror.com/simple-git/-/simple-git-3.28.0.tgz",
- "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==",
+ "version": "3.32.3",
+ "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.32.3.tgz",
+ "integrity": "sha512-56a5oxFdWlsGygOXHWrG+xjj5w9ZIt2uQbzqiIGdR/6i5iococ7WQ/bNPzWxCJdEUGUCmyMH0t9zMpRJTaKxmw==",
"license": "MIT",
"dependencies": {
"@kwsites/file-exists": "^1.1.1",
@@ -11275,19 +9921,10 @@
"url": "https://github.com/steveukx/git-js?sponsor=1"
}
},
- "node_modules/simple-swizzle": {
- "version": "0.2.2",
- "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
- "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
- "license": "MIT",
- "dependencies": {
- "is-arrayish": "^0.3.1"
- }
- },
"node_modules/sirv": {
- "version": "3.0.1",
- "resolved": "https://registry.npmmirror.com/sirv/-/sirv-3.0.1.tgz",
- "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
+ "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
"license": "MIT",
"dependencies": {
"@polka/url": "^1.0.0-next.24",
@@ -11300,13 +9937,13 @@
},
"node_modules/sisteransi": {
"version": "1.0.5",
- "resolved": "https://registry.npmmirror.com/sisteransi/-/sisteransi-1.0.5.tgz",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
"license": "MIT"
},
"node_modules/slash": {
"version": "5.1.0",
- "resolved": "https://registry.npmmirror.com/slash/-/slash-5.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
"integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
"license": "MIT",
"engines": {
@@ -11317,14 +9954,17 @@
}
},
"node_modules/smob": {
- "version": "1.5.0",
- "resolved": "https://registry.npmmirror.com/smob/-/smob-1.5.0.tgz",
- "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==",
- "license": "MIT"
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.1.tgz",
+ "integrity": "sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ }
},
"node_modules/source-map": {
"version": "0.7.6",
- "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.6.tgz",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
"integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
"license": "BSD-3-Clause",
"engines": {
@@ -11342,7 +9982,7 @@
},
"node_modules/source-map-support": {
"version": "0.5.21",
- "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"license": "MIT",
"dependencies": {
@@ -11352,45 +9992,13 @@
},
"node_modules/source-map-support/node_modules/source-map": {
"version": "0.6.1",
- "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
- "node_modules/spdx-correct": {
- "version": "3.2.0",
- "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz",
- "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-expression-parse": "^3.0.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-exceptions": {
- "version": "2.5.0",
- "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
- "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
- "license": "CC-BY-3.0"
- },
- "node_modules/spdx-expression-parse": {
- "version": "3.0.1",
- "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
- "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
- "license": "MIT",
- "dependencies": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-license-ids": {
- "version": "3.0.21",
- "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz",
- "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==",
- "license": "CC0-1.0"
- },
"node_modules/speakingurl": {
"version": "14.0.1",
"resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz",
@@ -11400,24 +10008,27 @@
"node": ">=0.10.0"
}
},
- "node_modules/stack-trace": {
- "version": "0.0.10",
- "resolved": "https://registry.npmmirror.com/stack-trace/-/stack-trace-0.0.10.tgz",
- "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
+ "node_modules/srvx": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/srvx/-/srvx-0.11.8.tgz",
+ "integrity": "sha512-2n9t0YnAXPJjinytvxccNgs7rOA5gmE7Wowt/8Dy2dx2fDC6sBhfBpbrCvjYKALlVukPS/Uq3QwkolKNa7P/2Q==",
"license": "MIT",
+ "bin": {
+ "srvx": "bin/srvx.mjs"
+ },
"engines": {
- "node": "*"
+ "node": ">=20.16.0"
}
},
"node_modules/standard-as-callback": {
"version": "2.1.0",
- "resolved": "https://registry.npmmirror.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
"license": "MIT"
},
"node_modules/statuses": {
"version": "2.0.2",
- "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
"license": "MIT",
"engines": {
@@ -11425,27 +10036,25 @@
}
},
"node_modules/std-env": {
- "version": "3.9.0",
- "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.9.0.tgz",
- "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
+ "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
"license": "MIT"
},
"node_modules/streamx": {
- "version": "2.22.1",
- "resolved": "https://registry.npmmirror.com/streamx/-/streamx-2.22.1.tgz",
- "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==",
+ "version": "2.23.0",
+ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz",
+ "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==",
"license": "MIT",
"dependencies": {
+ "events-universal": "^1.0.0",
"fast-fifo": "^1.3.2",
"text-decoder": "^1.1.0"
- },
- "optionalDependencies": {
- "bare-events": "^2.2.0"
}
},
"node_modules/string_decoder": {
"version": "1.3.0",
- "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"license": "MIT",
"dependencies": {
@@ -11550,7 +10159,7 @@
},
"node_modules/strip-final-newline": {
"version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
"license": "MIT",
"engines": {
@@ -11561,9 +10170,9 @@
}
},
"node_modules/strip-literal": {
- "version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-3.0.0.tgz",
- "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz",
+ "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==",
"license": "MIT",
"dependencies": {
"js-tokens": "^9.0.1"
@@ -11574,23 +10183,39 @@
},
"node_modules/strip-literal/node_modules/js-tokens": {
"version": "9.0.1",
- "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
"license": "MIT"
},
"node_modules/structured-clone-es": {
"version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/structured-clone-es/-/structured-clone-es-1.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/structured-clone-es/-/structured-clone-es-1.0.0.tgz",
"integrity": "sha512-FL8EeKFFyNQv5cMnXI31CIMCsFarSVI2bF0U0ImeNE3g/F1IvJQyqzOXxPBRXiwQfyBTlbNe88jh1jFW0O/jiQ==",
"license": "ISC"
},
+ "node_modules/style-value-types": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmmirror.com/style-value-types/-/style-value-types-5.1.2.tgz",
+ "integrity": "sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "hey-listen": "^1.0.8",
+ "tslib": "2.4.0"
+ }
+ },
+ "node_modules/style-value-types/node_modules/tslib": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz",
+ "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==",
+ "license": "0BSD"
+ },
"node_modules/stylehacks": {
- "version": "7.0.6",
- "resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-7.0.6.tgz",
- "integrity": "sha512-iitguKivmsueOmTO0wmxURXBP8uqOO+zikLGZ7Mm9e/94R4w5T999Js2taS/KBOnQ/wdC3jN3vNSrkGDrlnqQg==",
+ "version": "7.0.7",
+ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.7.tgz",
+ "integrity": "sha512-bJkD0JkEtbRrMFtwgpJyBbFIwfDDONQ1Ov3sDLZQP8HuJ73kBOyx66H4bOcAbVWmnfLdvQ0AJwXxOMkpujcO6g==",
"license": "MIT",
"dependencies": {
- "browserslist": "^4.25.1",
+ "browserslist": "^4.27.0",
"postcss-selector-parser": "^7.1.0"
},
"engines": {
@@ -11644,9 +10269,9 @@
}
},
"node_modules/supports-color": {
- "version": "10.0.0",
- "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-10.0.0.tgz",
- "integrity": "sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==",
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
"license": "MIT",
"engines": {
"node": ">=18"
@@ -11669,7 +10294,7 @@
},
"node_modules/svgo": {
"version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/svgo/-/svgo-4.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz",
"integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==",
"license": "MIT",
"dependencies": {
@@ -11694,7 +10319,7 @@
},
"node_modules/svgo/node_modules/commander": {
"version": "11.1.0",
- "resolved": "https://registry.npmmirror.com/commander/-/commander-11.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
"license": "MIT",
"engines": {
@@ -11703,7 +10328,7 @@
},
"node_modules/system-architecture": {
"version": "0.1.0",
- "resolved": "https://registry.npmmirror.com/system-architecture/-/system-architecture-0.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz",
"integrity": "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==",
"license": "MIT",
"engines": {
@@ -11713,6 +10338,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/tagged-tag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",
+ "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/tailwind-config-viewer": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/tailwind-config-viewer/-/tailwind-config-viewer-2.0.4.tgz",
@@ -11922,37 +10559,16 @@
"node": ">=8.10.0"
}
},
- "node_modules/tailwindcss/node_modules/resolve": {
- "version": "1.22.10",
- "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz",
- "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
- "license": "MIT",
- "dependencies": {
- "is-core-module": "^2.16.0",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/tar": {
- "version": "7.4.3",
- "resolved": "https://registry.npmmirror.com/tar/-/tar-7.4.3.tgz",
- "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
- "license": "ISC",
+ "version": "7.5.9",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz",
+ "integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==",
+ "license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
"chownr": "^3.0.0",
"minipass": "^7.1.2",
- "minizlib": "^3.0.1",
- "mkdirp": "^3.0.1",
+ "minizlib": "^3.1.0",
"yallist": "^5.0.0"
},
"engines": {
@@ -11961,7 +10577,7 @@
},
"node_modules/tar-stream": {
"version": "3.1.7",
- "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-3.1.7.tgz",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
"integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
"license": "MIT",
"dependencies": {
@@ -11972,7 +10588,7 @@
},
"node_modules/tar/node_modules/yallist": {
"version": "5.0.0",
- "resolved": "https://registry.npmmirror.com/yallist/-/yallist-5.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
"license": "BlueOak-1.0.0",
"engines": {
@@ -11980,13 +10596,13 @@
}
},
"node_modules/terser": {
- "version": "5.43.1",
- "resolved": "https://registry.npmmirror.com/terser/-/terser-5.43.1.tgz",
- "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
+ "version": "5.46.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz",
+ "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==",
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.14.0",
+ "acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},
@@ -11999,25 +10615,19 @@
},
"node_modules/terser/node_modules/commander": {
"version": "2.20.3",
- "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"license": "MIT"
},
"node_modules/text-decoder": {
- "version": "1.2.3",
- "resolved": "https://registry.npmmirror.com/text-decoder/-/text-decoder-1.2.3.tgz",
- "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
+ "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
"license": "Apache-2.0",
"dependencies": {
"b4a": "^1.6.4"
}
},
- "node_modules/text-hex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/text-hex/-/text-hex-1.0.0.tgz",
- "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
- "license": "MIT"
- },
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz",
@@ -12041,24 +10651,27 @@
},
"node_modules/tiny-invariant": {
"version": "1.3.3",
- "resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"license": "MIT"
},
"node_modules/tinyexec": {
- "version": "1.0.1",
- "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.1.tgz",
- "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==",
- "license": "MIT"
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
+ "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/tinyglobby": {
- "version": "0.2.14",
- "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz",
- "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"license": "MIT",
"dependencies": {
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2"
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
@@ -12067,24 +10680,6 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
- "node_modules/tmp": {
- "version": "0.2.3",
- "resolved": "https://registry.npmmirror.com/tmp/-/tmp-0.2.3.tgz",
- "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
- "license": "MIT",
- "engines": {
- "node": ">=14.14"
- }
- },
- "node_modules/tmp-promise": {
- "version": "3.0.3",
- "resolved": "https://registry.npmmirror.com/tmp-promise/-/tmp-promise-3.0.3.tgz",
- "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
- "license": "MIT",
- "dependencies": {
- "tmp": "^0.2.0"
- }
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -12106,15 +10701,9 @@
"node": ">=0.6"
}
},
- "node_modules/toml": {
- "version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/toml/-/toml-3.0.0.tgz",
- "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
- "license": "MIT"
- },
"node_modules/totalist": {
"version": "3.0.1",
- "resolved": "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
"license": "MIT",
"engines": {
@@ -12123,31 +10712,10 @@
},
"node_modules/tr46": {
"version": "0.0.3",
- "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
- "node_modules/triple-beam": {
- "version": "1.4.1",
- "resolved": "https://registry.npmmirror.com/triple-beam/-/triple-beam-1.4.1.tgz",
- "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
- "license": "MIT",
- "engines": {
- "node": ">= 14.0.0"
- }
- },
- "node_modules/ts-api-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
- "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
- "license": "MIT",
- "engines": {
- "node": ">=18.12"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4"
- }
- },
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
@@ -12156,9 +10724,10 @@
},
"node_modules/tslib": {
"version": "2.8.1",
- "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
- "license": "0BSD"
+ "license": "0BSD",
+ "optional": true
},
"node_modules/tsscmp": {
"version": "1.0.6",
@@ -12170,12 +10739,15 @@
}
},
"node_modules/type-fest": {
- "version": "4.41.0",
- "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-4.41.0.tgz",
- "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.4.4.tgz",
+ "integrity": "sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==",
"license": "(MIT OR CC0-1.0)",
+ "dependencies": {
+ "tagged-tag": "^1.0.0"
+ },
"engines": {
- "node": ">=16"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -12194,30 +10766,9 @@
"node": ">= 0.6"
}
},
- "node_modules/type-is/node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/type-is/node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "license": "MIT",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/type-level-regexp": {
"version": "0.1.17",
- "resolved": "https://registry.npmmirror.com/type-level-regexp/-/type-level-regexp-0.1.17.tgz",
+ "resolved": "https://registry.npmjs.org/type-level-regexp/-/type-level-regexp-0.1.17.tgz",
"integrity": "sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==",
"license": "MIT"
},
@@ -12226,6 +10777,8 @@
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"license": "Apache-2.0",
+ "optional": true,
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -12235,14 +10788,14 @@
}
},
"node_modules/ufo": {
- "version": "1.6.1",
- "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz",
- "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
+ "version": "1.6.3",
+ "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.3.tgz",
+ "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==",
"license": "MIT"
},
"node_modules/ultrahtml": {
"version": "1.6.0",
- "resolved": "https://registry.npmmirror.com/ultrahtml/-/ultrahtml-1.6.0.tgz",
+ "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz",
"integrity": "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==",
"license": "MIT"
},
@@ -12253,127 +10806,115 @@
"license": "MIT"
},
"node_modules/unctx": {
- "version": "2.4.1",
- "resolved": "https://registry.npmmirror.com/unctx/-/unctx-2.4.1.tgz",
- "integrity": "sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==",
+ "version": "2.5.0",
+ "resolved": "https://registry.npmmirror.com/unctx/-/unctx-2.5.0.tgz",
+ "integrity": "sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==",
"license": "MIT",
"dependencies": {
- "acorn": "^8.14.0",
+ "acorn": "^8.15.0",
"estree-walker": "^3.0.3",
- "magic-string": "^0.30.17",
- "unplugin": "^2.1.0"
+ "magic-string": "^0.30.21",
+ "unplugin": "^2.3.11"
}
},
- "node_modules/undici-types": {
- "version": "7.8.0",
- "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.8.0.tgz",
- "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
- "license": "MIT",
- "optional": true
- },
- "node_modules/unenv": {
- "version": "2.0.0-rc.18",
- "resolved": "https://registry.npmmirror.com/unenv/-/unenv-2.0.0-rc.18.tgz",
- "integrity": "sha512-O0oVQVJ2X3Q8H4HITJr4e2cWxMYBeZ+p8S25yoKCxVCgDWtIJDcgwWNonYz12tI3ylVQCRyPV/Bdq0KJeXo7AA==",
- "license": "MIT",
- "dependencies": {
- "defu": "^6.1.4",
- "exsolve": "^1.0.7",
- "ohash": "^2.0.11",
- "pathe": "^2.0.3",
- "ufo": "^1.6.1"
+ "node_modules/unenv": {
+ "version": "2.0.0-rc.24",
+ "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz",
+ "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==",
+ "license": "MIT",
+ "dependencies": {
+ "pathe": "^2.0.3"
}
},
"node_modules/unhead": {
- "version": "2.0.12",
- "resolved": "https://registry.npmmirror.com/unhead/-/unhead-2.0.12.tgz",
- "integrity": "sha512-5oo0lwz81XDXCmrHGzgmbaNOxM8R9MZ3FkEs2ROHeW8e16xsrv7qXykENlISrcxr3RLPHQEsD1b6js9P2Oj/Ow==",
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/unhead/-/unhead-2.1.9.tgz",
+ "integrity": "sha512-4GvP6YeJQzo9J3g9fFZUJOH6jacUp5JgJ0/zC8eZrt8Dwompg9SuOSfrYbZaEzsfMPgQc4fsEjMoY9WzGPOChg==",
"license": "MIT",
"dependencies": {
- "hookable": "^5.5.3"
+ "hookable": "^6.0.1"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
}
},
+ "node_modules/unhead/node_modules/hookable": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.0.1.tgz",
+ "integrity": "sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==",
+ "license": "MIT"
+ },
"node_modules/unicorn-magic": {
- "version": "0.1.0",
- "resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
- "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz",
+ "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==",
"license": "MIT",
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/unimport": {
- "version": "5.2.0",
- "resolved": "https://registry.npmmirror.com/unimport/-/unimport-5.2.0.tgz",
- "integrity": "sha512-bTuAMMOOqIAyjV4i4UH7P07pO+EsVxmhOzQ2YJ290J6mkLUdozNhb5I/YoOEheeNADC03ent3Qj07X0fWfUpmw==",
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/unimport/-/unimport-5.7.0.tgz",
+ "integrity": "sha512-njnL6sp8lEA8QQbZrt+52p/g4X0rw3bnGGmUcJnt1jeG8+iiqO779aGz0PirCtydAIVcuTBRlJ52F0u46z309Q==",
"license": "MIT",
"dependencies": {
- "acorn": "^8.15.0",
+ "acorn": "^8.16.0",
"escape-string-regexp": "^5.0.0",
"estree-walker": "^3.0.3",
- "local-pkg": "^1.1.1",
- "magic-string": "^0.30.17",
- "mlly": "^1.7.4",
+ "local-pkg": "^1.1.2",
+ "magic-string": "^0.30.21",
+ "mlly": "^1.8.0",
"pathe": "^2.0.3",
"picomatch": "^4.0.3",
- "pkg-types": "^2.2.0",
+ "pkg-types": "^2.3.0",
"scule": "^1.3.0",
- "strip-literal": "^3.0.0",
- "tinyglobby": "^0.2.14",
- "unplugin": "^2.3.5",
- "unplugin-utils": "^0.2.4"
+ "strip-literal": "^3.1.0",
+ "tinyglobby": "^0.2.15",
+ "unplugin": "^2.3.11",
+ "unplugin-utils": "^0.3.1"
},
"engines": {
"node": ">=18.12.0"
}
},
- "node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/unixify": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/unixify/-/unixify-1.0.0.tgz",
- "integrity": "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==",
+ "node_modules/unimport/node_modules/unplugin-utils": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz",
+ "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==",
"license": "MIT",
"dependencies": {
- "normalize-path": "^2.1.1"
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3"
},
"engines": {
- "node": ">=0.10.0"
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
}
},
- "node_modules/unixify/node_modules/normalize-path": {
- "version": "2.1.1",
- "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
"license": "MIT",
- "dependencies": {
- "remove-trailing-separator": "^1.0.1"
- },
"engines": {
- "node": ">=0.10.0"
+ "node": ">= 10.0.0"
}
},
"node_modules/unplugin": {
- "version": "2.3.5",
- "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.5.tgz",
- "integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==",
+ "version": "2.3.11",
+ "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.11.tgz",
+ "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==",
"license": "MIT",
"dependencies": {
- "acorn": "^8.14.1",
- "picomatch": "^4.0.2",
+ "@jridgewell/remapping": "^2.3.5",
+ "acorn": "^8.15.0",
+ "picomatch": "^4.0.3",
"webpack-virtual-modules": "^0.6.2"
},
"engines": {
@@ -12397,29 +10938,33 @@
}
},
"node_modules/unplugin-vue-router": {
- "version": "0.14.0",
- "resolved": "https://registry.npmmirror.com/unplugin-vue-router/-/unplugin-vue-router-0.14.0.tgz",
- "integrity": "sha512-ipjunvS5e2aFHBAUFuLbHl2aHKbXXXBhTxGT9wZx66fNVPdEQzVVitF8nODr1plANhTTa3UZ+DQu9uyLngMzoQ==",
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/unplugin-vue-router/-/unplugin-vue-router-0.19.2.tgz",
+ "integrity": "sha512-u5dgLBarxE5cyDK/hzJGfpCTLIAyiTXGlo85COuD4Nssj6G7NxS+i9mhCWz/1p/ud1eMwdcUbTXehQe41jYZUA==",
+ "deprecated": "Merged into vuejs/router. Migrate: https://router.vuejs.org/guide/migration/v4-to-v5.html",
"license": "MIT",
"dependencies": {
- "@vue-macros/common": "3.0.0-beta.15",
- "ast-walker-scope": "^0.8.1",
- "chokidar": "^4.0.3",
- "fast-glob": "^3.3.3",
+ "@babel/generator": "^7.28.5",
+ "@vue-macros/common": "^3.1.1",
+ "@vue/language-core": "^3.2.1",
+ "ast-walker-scope": "^0.8.3",
+ "chokidar": "^5.0.0",
"json5": "^2.2.3",
- "local-pkg": "^1.1.1",
- "magic-string": "^0.30.17",
- "mlly": "^1.7.4",
+ "local-pkg": "^1.1.2",
+ "magic-string": "^0.30.21",
+ "mlly": "^1.8.0",
+ "muggle-string": "^0.4.1",
"pathe": "^2.0.3",
- "picomatch": "^4.0.2",
+ "picomatch": "^4.0.3",
"scule": "^1.3.0",
- "unplugin": "^2.3.5",
- "unplugin-utils": "^0.2.4",
- "yaml": "^2.8.0"
+ "tinyglobby": "^0.2.15",
+ "unplugin": "^2.3.11",
+ "unplugin-utils": "^0.3.1",
+ "yaml": "^2.8.2"
},
"peerDependencies": {
"@vue/compiler-sfc": "^3.5.17",
- "vue-router": "^4.5.1"
+ "vue-router": "^4.6.0"
},
"peerDependenciesMeta": {
"vue-router": {
@@ -12427,20 +10972,36 @@
}
}
},
+ "node_modules/unplugin-vue-router/node_modules/unplugin-utils": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz",
+ "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==",
+ "license": "MIT",
+ "dependencies": {
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
"node_modules/unstorage": {
- "version": "1.16.1",
- "resolved": "https://registry.npmmirror.com/unstorage/-/unstorage-1.16.1.tgz",
- "integrity": "sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ==",
+ "version": "1.17.4",
+ "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.4.tgz",
+ "integrity": "sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==",
"license": "MIT",
"dependencies": {
"anymatch": "^3.1.3",
- "chokidar": "^4.0.3",
+ "chokidar": "^5.0.0",
"destr": "^2.0.5",
- "h3": "^1.15.3",
- "lru-cache": "^10.4.3",
- "node-fetch-native": "^1.6.6",
- "ofetch": "^1.4.1",
- "ufo": "^1.6.1"
+ "h3": "^1.15.5",
+ "lru-cache": "^11.2.0",
+ "node-fetch-native": "^1.6.7",
+ "ofetch": "^1.5.1",
+ "ufo": "^1.6.3"
},
"peerDependencies": {
"@azure/app-configuration": "^1.8.0",
@@ -12449,13 +11010,14 @@
"@azure/identity": "^4.6.0",
"@azure/keyvault-secrets": "^4.9.0",
"@azure/storage-blob": "^12.26.0",
- "@capacitor/preferences": "^6.0.3 || ^7.0.0",
+ "@capacitor/preferences": "^6 || ^7 || ^8",
"@deno/kv": ">=0.9.0",
"@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0",
"@planetscale/database": "^1.19.0",
"@upstash/redis": "^1.34.3",
"@vercel/blob": ">=0.27.1",
- "@vercel/kv": "^1.0.1",
+ "@vercel/functions": "^2.2.12 || ^3.0.0",
+ "@vercel/kv": "^1 || ^2 || ^3",
"aws4fetch": "^1.0.20",
"db0": ">=0.2.1",
"idb-keyval": "^6.2.1",
@@ -12499,6 +11061,9 @@
"@vercel/blob": {
"optional": true
},
+ "@vercel/functions": {
+ "optional": true
+ },
"@vercel/kv": {
"optional": true
},
@@ -12520,14 +11085,17 @@
}
},
"node_modules/unstorage/node_modules/lru-cache": {
- "version": "10.4.3",
- "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
- "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
- "license": "ISC"
+ "version": "11.2.6",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
+ "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
},
"node_modules/untun": {
"version": "0.1.3",
- "resolved": "https://registry.npmmirror.com/untun/-/untun-0.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/untun/-/untun-0.1.3.tgz",
"integrity": "sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ==",
"license": "MIT",
"dependencies": {
@@ -12541,7 +11109,7 @@
},
"node_modules/untun/node_modules/pathe": {
"version": "1.1.2",
- "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
"license": "MIT"
},
@@ -12562,65 +11130,23 @@
}
},
"node_modules/unwasm": {
- "version": "0.3.9",
- "resolved": "https://registry.npmmirror.com/unwasm/-/unwasm-0.3.9.tgz",
- "integrity": "sha512-LDxTx/2DkFURUd+BU1vUsF/moj0JsoTvl+2tcg2AUOiEzVturhGGx17/IMgGvKUYdZwr33EJHtChCJuhu9Ouvg==",
- "license": "MIT",
- "dependencies": {
- "knitwork": "^1.0.0",
- "magic-string": "^0.30.8",
- "mlly": "^1.6.1",
- "pathe": "^1.1.2",
- "pkg-types": "^1.0.3",
- "unplugin": "^1.10.0"
- }
- },
- "node_modules/unwasm/node_modules/confbox": {
- "version": "0.1.8",
- "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz",
- "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
- "license": "MIT"
- },
- "node_modules/unwasm/node_modules/pathe": {
- "version": "1.1.2",
- "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz",
- "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
- "license": "MIT"
- },
- "node_modules/unwasm/node_modules/pkg-types": {
- "version": "1.3.1",
- "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz",
- "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
- "license": "MIT",
- "dependencies": {
- "confbox": "^0.1.8",
- "mlly": "^1.7.4",
- "pathe": "^2.0.1"
- }
- },
- "node_modules/unwasm/node_modules/pkg-types/node_modules/pathe": {
- "version": "2.0.3",
- "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
- "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
- "license": "MIT"
- },
- "node_modules/unwasm/node_modules/unplugin": {
- "version": "1.16.1",
- "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.16.1.tgz",
- "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==",
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/unwasm/-/unwasm-0.5.3.tgz",
+ "integrity": "sha512-keBgTSfp3r6+s9ZcSma+0chwxQdmLbB5+dAD9vjtB21UTMYuKAxHXCU1K2CbCtnP09EaWeRvACnXk0EJtUx+hw==",
"license": "MIT",
"dependencies": {
- "acorn": "^8.14.0",
- "webpack-virtual-modules": "^0.6.2"
- },
- "engines": {
- "node": ">=14.0.0"
+ "exsolve": "^1.0.8",
+ "knitwork": "^1.3.0",
+ "magic-string": "^0.30.21",
+ "mlly": "^1.8.0",
+ "pathe": "^2.0.3",
+ "pkg-types": "^2.3.0"
}
},
"node_modules/update-browserslist-db": {
- "version": "1.1.3",
- "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
- "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"funding": [
{
"type": "opencollective",
@@ -12649,45 +11175,16 @@
},
"node_modules/uqr": {
"version": "0.1.2",
- "resolved": "https://registry.npmmirror.com/uqr/-/uqr-0.1.2.tgz",
+ "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz",
"integrity": "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==",
"license": "MIT"
},
- "node_modules/urlpattern-polyfill": {
- "version": "8.0.2",
- "resolved": "https://registry.npmmirror.com/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz",
- "integrity": "sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==",
- "license": "MIT"
- },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
- "node_modules/uuid": {
- "version": "11.1.0",
- "resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz",
- "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "license": "MIT",
- "bin": {
- "uuid": "dist/esm/bin/uuid"
- }
- },
- "node_modules/validate-npm-package-license": {
- "version": "3.0.4",
- "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
- "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-correct": "^3.0.0",
- "spdx-expression-parse": "^3.0.0"
- }
- },
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",
@@ -12698,17 +11195,17 @@
}
},
"node_modules/vite": {
- "version": "7.0.6",
- "resolved": "https://registry.npmmirror.com/vite/-/vite-7.0.6.tgz",
- "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
+ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"license": "MIT",
"dependencies": {
- "esbuild": "^0.25.0",
- "fdir": "^6.4.6",
+ "esbuild": "^0.27.0",
+ "fdir": "^6.5.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
- "rollup": "^4.40.0",
- "tinyglobby": "^0.2.14"
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
@@ -12773,7 +11270,7 @@
},
"node_modules/vite-dev-rpc": {
"version": "1.1.0",
- "resolved": "https://registry.npmmirror.com/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz",
"integrity": "sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==",
"license": "MIT",
"dependencies": {
@@ -12789,7 +11286,7 @@
},
"node_modules/vite-hot-client": {
"version": "2.1.0",
- "resolved": "https://registry.npmmirror.com/vite-hot-client/-/vite-hot-client-2.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.1.0.tgz",
"integrity": "sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==",
"license": "MIT",
"funding": {
@@ -12800,54 +11297,54 @@
}
},
"node_modules/vite-node": {
- "version": "3.2.4",
- "resolved": "https://registry.npmmirror.com/vite-node/-/vite-node-3.2.4.tgz",
- "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-5.3.0.tgz",
+ "integrity": "sha512-8f20COPYJujc3OKPX6OuyBy3ZIv2det4eRRU4GY1y2MjbeGSUmPjedxg1b72KnTagCofwvZ65ThzjxDW2AtQFQ==",
"license": "MIT",
"dependencies": {
"cac": "^6.7.14",
- "debug": "^4.4.1",
- "es-module-lexer": "^1.7.0",
+ "es-module-lexer": "^2.0.0",
+ "obug": "^2.1.1",
"pathe": "^2.0.3",
- "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
+ "vite": "^7.3.1"
},
"bin": {
- "vite-node": "vite-node.mjs"
+ "vite-node": "dist/cli.mjs"
},
"engines": {
- "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
- "url": "https://opencollective.com/vitest"
+ "url": "https://opencollective.com/antfu"
}
},
"node_modules/vite-plugin-checker": {
- "version": "0.10.1",
- "resolved": "https://registry.npmmirror.com/vite-plugin-checker/-/vite-plugin-checker-0.10.1.tgz",
- "integrity": "sha512-imiBsmYTPdjQHIZiEi5BhJ7K8Z/kCjTFMn+Qa4+5ao/a4Yql4yWFcf81FDJqlMiM57iY4Q3Z7PdoEe4KydULYQ==",
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.12.0.tgz",
+ "integrity": "sha512-CmdZdDOGss7kdQwv73UyVgLPv0FVYe5czAgnmRX2oKljgEvSrODGuClaV3PDR2+3ou7N/OKGauDDBjy2MB07Rg==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.27.1",
"chokidar": "^4.0.3",
"npm-run-path": "^6.0.0",
"picocolors": "^1.1.1",
- "picomatch": "^4.0.2",
- "strip-ansi": "^7.1.0",
+ "picomatch": "^4.0.3",
"tiny-invariant": "^1.3.3",
- "tinyglobby": "^0.2.14",
+ "tinyglobby": "^0.2.15",
"vscode-uri": "^3.1.0"
},
"engines": {
- "node": ">=14.16"
+ "node": ">=16.11"
},
"peerDependencies": {
"@biomejs/biome": ">=1.7",
- "eslint": ">=7",
+ "eslint": ">=9.39.1",
"meow": "^13.2.0",
"optionator": "^0.9.4",
+ "oxlint": ">=1",
"stylelint": ">=16",
"typescript": "*",
- "vite": ">=2.0.0",
+ "vite": ">=5.4.21",
"vls": "*",
"vti": "*",
"vue-tsc": "~2.2.10 || ^3.0.0"
@@ -12865,6 +11362,9 @@
"optionator": {
"optional": true
},
+ "oxlint": {
+ "optional": true
+ },
"stylelint": {
"optional": true
},
@@ -12882,9 +11382,24 @@
}
}
},
+ "node_modules/vite-plugin-checker/node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/vite-plugin-checker/node_modules/npm-run-path": {
"version": "6.0.0",
- "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-6.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz",
"integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
"license": "MIT",
"dependencies": {
@@ -12900,7 +11415,7 @@
},
"node_modules/vite-plugin-checker/node_modules/path-key": {
"version": "4.0.0",
- "resolved": "https://registry.npmmirror.com/path-key/-/path-key-4.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"license": "MIT",
"engines": {
@@ -12910,9 +11425,22 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/vite-plugin-checker/node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/vite-plugin-checker/node_modules/unicorn-magic": {
"version": "0.3.0",
- "resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
"license": "MIT",
"engines": {
@@ -12923,19 +11451,19 @@
}
},
"node_modules/vite-plugin-inspect": {
- "version": "11.3.0",
- "resolved": "https://registry.npmmirror.com/vite-plugin-inspect/-/vite-plugin-inspect-11.3.0.tgz",
- "integrity": "sha512-vmt7K1WVKQkuiwvsM6e5h3HDJ2pSWTnzoj+JP9Kvu3Sh2G+nFap1F1V7tqpyA4qFxM1GQ84ryffWFGQrwShERQ==",
+ "version": "11.3.3",
+ "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.3.3.tgz",
+ "integrity": "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==",
"license": "MIT",
"dependencies": {
"ansis": "^4.1.0",
"debug": "^4.4.1",
"error-stack-parser-es": "^1.0.5",
"ohash": "^2.0.11",
- "open": "^10.1.2",
- "perfect-debounce": "^1.0.0",
+ "open": "^10.2.0",
+ "perfect-debounce": "^2.0.0",
"sirv": "^3.0.1",
- "unplugin-utils": "^0.2.4",
+ "unplugin-utils": "^0.3.0",
"vite-dev-rpc": "^1.1.0"
},
"engines": {
@@ -12955,7 +11483,7 @@
},
"node_modules/vite-plugin-inspect/node_modules/define-lazy-prop": {
"version": "3.0.0",
- "resolved": "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
"integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
"license": "MIT",
"engines": {
@@ -12967,7 +11495,7 @@
},
"node_modules/vite-plugin-inspect/node_modules/open": {
"version": "10.2.0",
- "resolved": "https://registry.npmmirror.com/open/-/open-10.2.0.tgz",
+ "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
"integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
"license": "MIT",
"dependencies": {
@@ -12983,15 +11511,37 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/vite-plugin-inspect/node_modules/perfect-debounce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
+ "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==",
+ "license": "MIT"
+ },
+ "node_modules/vite-plugin-inspect/node_modules/unplugin-utils": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz",
+ "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==",
+ "license": "MIT",
+ "dependencies": {
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
"node_modules/vite-plugin-vue-tracer": {
- "version": "1.0.0",
- "resolved": "https://registry.npmmirror.com/vite-plugin-vue-tracer/-/vite-plugin-vue-tracer-1.0.0.tgz",
- "integrity": "sha512-a+UB9IwGx5uwS4uG/a9kM6fCMnxONDkOTbgCUbhFpiGhqfxrrC1+9BibV7sWwUnwj1Dg6MnRxG0trLgUZslDXA==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-vue-tracer/-/vite-plugin-vue-tracer-1.2.0.tgz",
+ "integrity": "sha512-a9Z/TLpxwmoE9kIcv28wqQmiszM7ec4zgndXWEsVD/2lEZLRGzcg7ONXmplzGF/UP5W59QNtS809OdywwpUWQQ==",
"license": "MIT",
"dependencies": {
"estree-walker": "^3.0.3",
- "exsolve": "^1.0.7",
- "magic-string": "^0.30.17",
+ "exsolve": "^1.0.8",
+ "magic-string": "^0.30.21",
"pathe": "^2.0.3",
"source-map-js": "^1.2.1"
},
@@ -13005,21 +11555,21 @@
},
"node_modules/vscode-uri": {
"version": "3.1.0",
- "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
"integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==",
"license": "MIT"
},
"node_modules/vue": {
- "version": "3.5.18",
- "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.18.tgz",
- "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==",
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.29.tgz",
+ "integrity": "sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.18",
- "@vue/compiler-sfc": "3.5.18",
- "@vue/runtime-dom": "3.5.18",
- "@vue/server-renderer": "3.5.18",
- "@vue/shared": "3.5.18"
+ "@vue/compiler-dom": "3.5.29",
+ "@vue/compiler-sfc": "3.5.29",
+ "@vue/runtime-dom": "3.5.29",
+ "@vue/server-renderer": "3.5.29",
+ "@vue/shared": "3.5.29"
},
"peerDependencies": {
"typescript": "*"
@@ -13031,24 +11581,24 @@
}
},
"node_modules/vue-bundle-renderer": {
- "version": "2.1.1",
- "resolved": "https://registry.npmmirror.com/vue-bundle-renderer/-/vue-bundle-renderer-2.1.1.tgz",
- "integrity": "sha512-+qALLI5cQncuetYOXp4yScwYvqh8c6SMXee3B+M7oTZxOgtESP0l4j/fXdEJoZ+EdMxkGWIj+aSEyjXkOdmd7g==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/vue-bundle-renderer/-/vue-bundle-renderer-2.2.0.tgz",
+ "integrity": "sha512-sz/0WEdYH1KfaOm0XaBmRZOWgYTEvUDt6yPYaUzl4E52qzgWLlknaPPTTZmp6benaPTlQAI/hN1x3tAzZygycg==",
"license": "MIT",
"dependencies": {
- "ufo": "^1.5.4"
+ "ufo": "^1.6.1"
}
},
"node_modules/vue-devtools-stub": {
"version": "0.1.0",
- "resolved": "https://registry.npmmirror.com/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz",
"integrity": "sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==",
"license": "MIT"
},
"node_modules/vue-router": {
- "version": "4.5.1",
- "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz",
- "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==",
+ "version": "4.6.4",
+ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
+ "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^6.6.4"
@@ -13057,21 +11607,12 @@
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
- "vue": "^3.2.0"
- }
- },
- "node_modules/web-streams-polyfill": {
- "version": "3.3.3",
- "resolved": "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
- "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
- "license": "MIT",
- "engines": {
- "node": ">= 8"
+ "vue": "^3.5.0"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
- "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
@@ -13083,7 +11624,7 @@
},
"node_modules/whatwg-url": {
"version": "5.0.0",
- "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"dependencies": {
@@ -13093,7 +11634,7 @@
},
"node_modules/which": {
"version": "5.0.0",
- "resolved": "https://registry.npmmirror.com/which/-/which-5.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
"integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
"license": "ISC",
"dependencies": {
@@ -13106,82 +11647,6 @@
"node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/winston": {
- "version": "3.17.0",
- "resolved": "https://registry.npmmirror.com/winston/-/winston-3.17.0.tgz",
- "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==",
- "license": "MIT",
- "dependencies": {
- "@colors/colors": "^1.6.0",
- "@dabh/diagnostics": "^2.0.2",
- "async": "^3.2.3",
- "is-stream": "^2.0.0",
- "logform": "^2.7.0",
- "one-time": "^1.0.0",
- "readable-stream": "^3.4.0",
- "safe-stable-stringify": "^2.3.1",
- "stack-trace": "0.0.x",
- "triple-beam": "^1.3.0",
- "winston-transport": "^4.9.0"
- },
- "engines": {
- "node": ">= 12.0.0"
- }
- },
- "node_modules/winston-transport": {
- "version": "4.9.0",
- "resolved": "https://registry.npmmirror.com/winston-transport/-/winston-transport-4.9.0.tgz",
- "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==",
- "license": "MIT",
- "dependencies": {
- "logform": "^2.7.0",
- "readable-stream": "^3.6.2",
- "triple-beam": "^1.3.0"
- },
- "engines": {
- "node": ">= 12.0.0"
- }
- },
- "node_modules/winston-transport/node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/winston/node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/winston/node_modules/readable-stream": {
- "version": "3.6.2",
- "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
- "license": "MIT",
- "dependencies": {
- "inherits": "^2.0.3",
- "string_decoder": "^1.1.1",
- "util-deprecate": "^1.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/wrap-ansi": {
"version": "8.1.0",
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
@@ -13291,23 +11756,10 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
- "node_modules/write-file-atomic": {
- "version": "6.0.0",
- "resolved": "https://registry.npmmirror.com/write-file-atomic/-/write-file-atomic-6.0.0.tgz",
- "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==",
- "license": "ISC",
- "dependencies": {
- "imurmurhash": "^0.1.4",
- "signal-exit": "^4.0.1"
- },
- "engines": {
- "node": "^18.17.0 || >=20.5.0"
- }
- },
"node_modules/ws": {
- "version": "8.18.3",
- "resolved": "https://registry.npmmirror.com/ws/-/ws-8.18.3.tgz",
- "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "version": "8.19.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
+ "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -13327,7 +11779,7 @@
},
"node_modules/wsl-utils": {
"version": "0.1.0",
- "resolved": "https://registry.npmmirror.com/wsl-utils/-/wsl-utils-0.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
"integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
"license": "MIT",
"dependencies": {
@@ -13351,20 +11803,23 @@
},
"node_modules/yallist": {
"version": "3.1.1",
- "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"license": "ISC"
},
"node_modules/yaml": {
- "version": "2.8.0",
- "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.8.0.tgz",
- "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
+ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/yargs": {
@@ -13435,25 +11890,6 @@
"node": ">=8"
}
},
- "node_modules/yauzl": {
- "version": "2.10.0",
- "resolved": "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
- "license": "MIT",
- "dependencies": {
- "buffer-crc32": "~0.2.3",
- "fd-slicer": "~1.1.0"
- }
- },
- "node_modules/yauzl/node_modules/buffer-crc32": {
- "version": "0.2.13",
- "resolved": "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
- "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
"node_modules/ylru": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/ylru/-/ylru-1.4.0.tgz",
@@ -13463,34 +11899,22 @@
"node": ">= 4.0.0"
}
},
- "node_modules/yocto-queue": {
- "version": "1.2.1",
- "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-1.2.1.tgz",
- "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==",
- "license": "MIT",
- "engines": {
- "node": ">=12.20"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/youch": {
- "version": "4.1.0-beta.10",
- "resolved": "https://registry.npmmirror.com/youch/-/youch-4.1.0-beta.10.tgz",
- "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0.tgz",
+ "integrity": "sha512-cYekNh2tUoU+voS11X0D0UQntVCSO6LQ1h10VriQGmfbpf0mnGTruwZICts23UUNiZCXm8H8hQBtRrdsbhuNNg==",
"license": "MIT",
"dependencies": {
- "@poppinss/colors": "^4.1.5",
- "@poppinss/dumper": "^0.6.4",
- "@speed-highlight/core": "^1.2.7",
- "cookie": "^1.0.2",
+ "@poppinss/colors": "^4.1.6",
+ "@poppinss/dumper": "^0.7.0",
+ "@speed-highlight/core": "^1.2.14",
+ "cookie-es": "^2.0.0",
"youch-core": "^0.3.3"
}
},
"node_modules/youch-core": {
"version": "0.3.3",
- "resolved": "https://registry.npmmirror.com/youch-core/-/youch-core-0.3.3.tgz",
+ "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz",
"integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==",
"license": "MIT",
"dependencies": {
@@ -13500,7 +11924,7 @@
},
"node_modules/zip-stream": {
"version": "6.0.1",
- "resolved": "https://registry.npmmirror.com/zip-stream/-/zip-stream-6.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz",
"integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==",
"license": "MIT",
"dependencies": {
@@ -13511,15 +11935,6 @@
"engines": {
"node": ">= 14"
}
- },
- "node_modules/zod": {
- "version": "3.25.76",
- "resolved": "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz",
- "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/colinhacks"
- }
}
}
}
diff --git a/frontend/package.json b/frontend/package.json
index 685e338..b527734 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -12,9 +12,15 @@
"dependencies": {
"@nuxtjs/tailwindcss": "^6.14.0",
"@pinia/nuxt": "^0.11.2",
+ "@vueuse/motion": "^3.0.3",
"axios": "^1.11.0",
+ "gsap": "^3.14.2",
"nuxt": "^4.0.1",
+ "ogl": "^1.0.11",
"vue": "^3.5.17",
"vue-router": "^4.5.1"
+ },
+ "devDependencies": {
+ "tailwindcss": "3.4.17"
}
}
diff --git a/frontend/pages/biz.vue b/frontend/pages/biz.vue
new file mode 100644
index 0000000..143d4ea
--- /dev/null
+++ b/frontend/pages/biz.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/frontend/pages/chat/[[username]].vue b/frontend/pages/chat/[[username]].vue
index 48ed479..6ec51e6 100644
--- a/frontend/pages/chat/[[username]].vue
+++ b/frontend/pages/chat/[[username]].vue
@@ -1,6003 +1,759 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ contactsError }}
-
-
- 暂无会话
-
-
-
-
-
-
-
-
-
-
- {{ contact.name.charAt(0) }}
-
-
-
-
-
-
-
{{ contact.name }}
-
-
- {{ contact.unreadCount > 99 ? '99+' : contact.unreadCount }}
-
- {{ contact.lastMessageTime }}
-
-
-
{{ contact.lastMessage }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 已定位到搜索结果(上下文模式)
-
-
-
- 退出定位
-
-
- 返回最新
-
-
-
-
-
-
-
-
- {{ isLoadingMessages ? '加载中...' : '继续上滑加载更多' }}
-
-
-
-
- 加载中...
-
-
- {{ messagesError }}
-
-
- 暂无聊天记录
-
-
-
-
-
- {{ message.timeDivider }}
-
-
-
-
-
- {{ message.content }}
-
-
-
-
-
-
-
-
-
-
-
- {{ message.sender.charAt(0) }}
-
-
-
-
-
-
- {{ message.senderDisplayName }}
-
-
- {{ message.fullTime }}
-
-
-
-
-
-
- {{ message.title || message.content || '文件' }}
- {{ formatFileSize(message.fileSize) }}
-
-
-
-
-
-
微信电脑版
-
-
-
-
-
-
- {{ message.content }}
-
-
-
-
-
-
-
- {{ message.content }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ getVoiceDurationInSeconds(message.voiceDuration) }}"
-
-
-
-
-
-
-
-
-
-
{{ message.content || '通话' }}
-
-
-
-
-
-
- {{ message._emojiDownloading ? '下载中...' : (message._emojiDownloaded ? '已下载' : '下载') }}
-
-
-
- {{ message.content }}
-
-
-
-
-
- {{ seg.content }}
-
-
-
-
-
-
-
{{ message.quoteTitle }}:
-
-
-
-
-
-
- {{ getVoiceDurationInSeconds(message.quoteVoiceLength) }}"
- 语音
-
-
-
-
{{ message.quoteTitle ? (message.quoteTitle + ': ') : '' }}{{ message.quoteContent }}
-
-
-
-
-
-
-
{{ message.title || '聊天记录' }}
-
-
-
- 聊天记录
-
-
-
-
-
-
-
-
- ¥{{ formatTransferAmount(message.amount) }}
- {{ getTransferTitle(message) }}
-
-
-
- 微信转账
-
-
-
-
-
-
-
-
- {{ getRedPacketText(message) }}
- 已领取
-
-
-
- 微信红包
-
-
-
-
-
- {{ seg.content }}
-
-
-
-
-
-
- {{ message.content || ('[' + (message.type || 'unknown') + '] 消息组件已移除') }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
选择一个会话
-
- 从左侧列表选择联系人查看聊天记录
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ chatHistoryModalTitle || '聊天记录' }}
-
-
-
-
-
-
-
-
-
- 没有可显示的聊天记录
-
-
-
-
-
-
- {{ (rec.senderDisplayName || rec.sourcename || '?').charAt(0) }}
-
-
-
-
-
-
-
- {{ rec.senderDisplayName || rec.sourcename }}
-
-
-
- {{ rec.fullTime || rec.sourcetime }}
-
-
-
-
-
-
-
-
{{ rec.content || '[视频]' }}
-
-
-
-
-
-
- {{ formatChatHistoryVideoDuration(rec.videoDuration) }}
-
-
-
-
-
-
-
{{ rec.content || '[图片]' }}
-
-
-
-
-
-
{{ rec.content || '[表情]' }}
-
-
-
-
-
-
-
-
- {{ rec.quote?.kind === 'video' ? '视频' : (rec.quote?.kind === 'image' ? '图片' : '表情') }}
-
-
-
-
- {{ rec.quote?.label || (rec.quote?.kind === 'video' ? '[视频]' : (rec.quote?.kind === 'image' ? '[图片]' : '[表情]')) }}
-
-
-
- {{ formatChatHistoryVideoDuration(rec.quote.duration) }}
-
-
-
-
-
- {{ seg.content }}
-
-
-
-
-
-
-
-
- {{ seg.content }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 复制文本
-
-
- 复制消息 JSON
-
-
- 定位引用消息
-
-
- 打开文件夹
-
-
-
-
-
-
-
-
-
导出聊天记录(离线 ZIP)
-
-
-
-
-
-
-
-
-
{{ exportError }}
-
- 已开启隐私模式:导出将隐藏会话/用户名/内容,并且不会打包头像与媒体。
-
-
-
-
-
范围
-
-
-
- 当前会话
-
-
-
- 选择会话(批量)
-
-
-
- 全部会话
-
-
-
- 仅群聊
-
-
-
- 仅单聊
-
-
-
-
-
-
- 全部 {{ exportContactCounts.total }}
-
-
- 群聊 {{ exportContactCounts.groups }}
-
-
- 单聊 {{ exportContactCounts.singles }}
-
-
点击 tab 筛选
-
-
-
-
-
-
-
-
-
-
- {{ (c.name || c.username || '?').charAt(0) }}
-
-
-
-
- {{ c.name }}
- {{ c.isGroup ? '(群)' : '' }}
-
-
{{ c.username }}
-
-
-
- 无匹配会话
-
-
-
- 已选 {{ exportSelectedUsernames.length }} 个会话
-
-
-
-
-
-
-
-
-
时间范围(可选)
-
-
-
-
-
- 全部
- 最近7天
- 最近30天
-
-
-
-
-
消息类型(导出内容)
-
-
-
- 全部消息(不筛选)
-
-
-
- 按类型筛选(可多选)
-
-
-
-
-
x.value)"
- >
- 全选
-
-
- 只语音
-
-
- 只转账
-
-
- 只红包
-
-
已选 {{ exportMessageTypes.length }} 项
-
-
-
-
- {{ opt.label }}
-
-
-
- 仅导出所选类型的消息(影响导出消息条数与进度统计)。
-
-
-
- 默认导出会话内全部消息;如需只导出语音/转账/红包等,请选择“按类型筛选”。
-
-
-
-
-
-
-
文件名(可选)
-
-
不填则自动生成(输出位置:output/exports/{账号}/)。
-
-
-
-
-
- 包含隐藏会话(仅对“全部/群/单”有效)
-
-
-
- 包含公众号/官方会话(仅对“全部/群/单”有效)
-
-
-
-
-
-
-
-
任务:{{ exportJob.exportId }}
-
状态:{{ exportJob.status }}
-
-
-
-
会话:{{ exportJob.progress?.conversationsDone || 0 }}/{{ exportJob.progress?.conversationsTotal || 0 }}
-
{{ exportOverallPercent }}%
-
-
-
-
-
-
- 当前:{{ exportJob.progress?.currentConversationName || exportJob.progress?.currentConversationUsername }}
- ({{ exportJob.progress?.currentConversationMessagesExported || 0 }}/{{ exportJob.progress?.currentConversationMessagesTotal || 0 }})
-
-
- {{ exportCurrentPercent }}%
- …
-
-
-
-
-
-
消息:{{ exportJob.progress?.messagesExported || 0 }};媒体:{{ exportJob.progress?.mediaCopied || 0 }};缺失:{{ exportJob.progress?.mediaMissing || 0 }}
-
-
-
-
-
- {{ exportJob.error || '导出失败' }}
-
-
-
-
-
-
- 关闭
-
-
- {{ isExportCreating ? '创建中...' : '开始导出' }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ desktopAutoLaunchError }}
-
-
-
-
-
关闭窗口行为
-
点击关闭按钮时:默认最小化到托盘
-
-
- 最小化到托盘
- 直接退出
-
-
-
- {{ desktopCloseBehaviorError }}
-
-
-
-
-
启动后自动开启实时获取
-
进入聊天页后自动打开“实时开关”(默认关闭)
-
-
-
-
-
-
-
有数据时默认进入聊天页
-
有已解密账号时,打开应用默认跳转到 /chat(默认关闭)
-
-
-
-
-
-
-
- 关闭
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/frontend/pages/contacts.vue b/frontend/pages/contacts.vue
new file mode 100644
index 0000000..2dbc9bf
--- /dev/null
+++ b/frontend/pages/contacts.vue
@@ -0,0 +1,501 @@
+
+
+
+
+
+
+
+
+
+
+
+ 好友 {{ counts.friends }}
+
+
+
+ 群聊 {{ counts.groups }}
+
+
+
+ 公众号 {{ counts.officials }}
+
+ 总计 {{ counts.total }}
+
+
+
+
加载中…
+
{{ error }}
+
暂无联系人
+
+
+
+ {{ group.key }}
+
+
+
+
+
{{ contact.displayName?.charAt(0) || '?' }}
+
+
+
{{ contact.displayName }}
+
{{ contact.username }}
+
+ 地区:{{ contact.region }}
+ ·
+ 来源:{{ contact.source }}
+
+
+
+ {{ typeLabel(contact.type) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/pages/decrypt-result.vue b/frontend/pages/decrypt-result.vue
index df8d405..676d3d3 100644
--- a/frontend/pages/decrypt-result.vue
+++ b/frontend/pages/decrypt-result.vue
@@ -1,5 +1,5 @@
-
+
@@ -171,4 +171,4 @@ onMounted(() => {
linear-gradient(90deg, rgba(7, 193, 96, 0.1) 1px, transparent 1px);
background-size: 50px 50px;
}
-
\ No newline at end of file
+
diff --git a/frontend/pages/decrypt.vue b/frontend/pages/decrypt.vue
index fb76aec..4e90eed 100644
--- a/frontend/pages/decrypt.vue
+++ b/frontend/pages/decrypt.vue
@@ -1,5 +1,5 @@
-
+
@@ -26,24 +26,40 @@
+
+
+
+
+
+ {{ dbDecryptProgress.message || (loading ? '解密中...' : '') }}
+
+
+ {{ dbDecryptProgress.current }} / {{ dbDecryptProgress.total }}
+
+
+
+
+
+
+ {{ dbDecryptProgress.current_file }}
+
+
+
+
+
{{ dbDecryptProgress.success_count }}
+
成功
+
+
+
{{ dbDecryptProgress.fail_count }}
+
失败
+
+
+
@@ -131,35 +181,39 @@
+
+
+ 此步骤将为您解密微信聊天中的图片
+
+
+
+
+
+ 如果您在第一步使用了“一键获取”或触发了云端解析,下方输入框已被自动填充。您也可可以使用wx_key 等工具手动获取。
+
+
-
-
-
-
-
- 使用 wx_key 获取图片密钥;AES 可选(V4-V2 需要)
-
@@ -325,6 +379,19 @@
+
+
+
+
+
@@ -364,15 +431,17 @@
+
+
+
+
+
diff --git a/frontend/pages/index.vue b/frontend/pages/index.vue
index cf06df5..95995d7 100644
--- a/frontend/pages/index.vue
+++ b/frontend/pages/index.vue
@@ -1,5 +1,5 @@
-
+
@@ -49,6 +49,17 @@
聊天预览
+
+
+
+
+
+
+
+
+ 年度总结
+
@@ -57,17 +68,12 @@
diff --git a/frontend/pages/wrapped/index.vue b/frontend/pages/wrapped/index.vue
new file mode 100644
index 0000000..1529d66
--- /dev/null
+++ b/frontend/pages/wrapped/index.vue
@@ -0,0 +1,668 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ year }}年
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/plugins/api-check.client.js b/frontend/plugins/api-check.client.js
index bda4bdc..5f86269 100644
--- a/frontend/plugins/api-check.client.js
+++ b/frontend/plugins/api-check.client.js
@@ -1,7 +1,8 @@
// 客户端插件:检查API连接状态
-export default defineNuxtPlugin(async (nuxtApp) => {
+export default defineNuxtPlugin((nuxtApp) => {
const { healthCheck } = useApi()
const appStore = useAppStore()
+ let intervalId = 0
// 检查API连接
const checkApiConnection = async () => {
@@ -17,10 +18,14 @@ export default defineNuxtPlugin(async (nuxtApp) => {
console.error('API连接失败:', error)
}
}
-
- // 初始检查
- await checkApiConnection()
-
- // 定期检查(每30秒)
- setInterval(checkApiConnection, 30000)
-})
\ No newline at end of file
+
+ nuxtApp.hook('app:mounted', () => {
+ void checkApiConnection()
+
+ if (!intervalId) {
+ intervalId = window.setInterval(() => {
+ void checkApiConnection()
+ }, 30000)
+ }
+ })
+})
diff --git a/frontend/plugins/desktop-debug.client.js b/frontend/plugins/desktop-debug.client.js
new file mode 100644
index 0000000..065e94c
--- /dev/null
+++ b/frontend/plugins/desktop-debug.client.js
@@ -0,0 +1,83 @@
+const isDesktopShell = () => {
+ if (typeof window === 'undefined') return false
+ return !!window.wechatDesktop?.__brand
+}
+
+const formatError = (error) => {
+ if (!error) return ''
+ if (error instanceof Error) {
+ return {
+ name: String(error.name || 'Error'),
+ message: String(error.message || ''),
+ stack: String(error.stack || '')
+ }
+ }
+ if (typeof error === 'object') {
+ try {
+ return JSON.parse(JSON.stringify(error))
+ } catch {}
+ }
+ return String(error)
+}
+
+const logDesktopDebug = (phase, details = {}) => {
+ if (!isDesktopShell()) return
+ try {
+ window.wechatDesktop?.logDebug?.('nuxt-bootstrap', phase, {
+ href: String(window.location?.href || ''),
+ ...details
+ })
+ } catch {}
+ try {
+ console.info(`[nuxt-bootstrap] ${phase}`, details)
+ } catch {}
+}
+
+export default defineNuxtPlugin((nuxtApp) => {
+ logDesktopDebug('plugin:setup')
+
+ if (typeof window !== 'undefined') {
+ window.addEventListener('error', (event) => {
+ logDesktopDebug('window:error', {
+ message: String(event?.message || ''),
+ filename: String(event?.filename || ''),
+ lineno: Number(event?.lineno || 0),
+ colno: Number(event?.colno || 0),
+ error: formatError(event?.error)
+ })
+ })
+
+ window.addEventListener('unhandledrejection', (event) => {
+ logDesktopDebug('window:unhandledrejection', {
+ reason: formatError(event?.reason)
+ })
+ })
+ }
+
+ nuxtApp.hook('app:created', () => {
+ logDesktopDebug('app:created')
+ })
+
+ nuxtApp.hook('app:beforeMount', () => {
+ logDesktopDebug('app:beforeMount')
+ })
+
+ nuxtApp.hook('app:mounted', () => {
+ logDesktopDebug('app:mounted')
+ })
+
+ nuxtApp.hook('page:start', () => {
+ logDesktopDebug('page:start')
+ })
+
+ nuxtApp.hook('page:finish', () => {
+ logDesktopDebug('page:finish')
+ })
+
+ nuxtApp.hook('vue:error', (error, _instance, info) => {
+ logDesktopDebug('vue:error', {
+ info: String(info || ''),
+ error: formatError(error)
+ })
+ })
+})
diff --git a/frontend/public/AnnualSummary1.png b/frontend/public/AnnualSummary1.png
new file mode 100644
index 0000000..1d05ca4
Binary files /dev/null and b/frontend/public/AnnualSummary1.png differ
diff --git a/frontend/public/AnnualSummary2.png b/frontend/public/AnnualSummary2.png
new file mode 100644
index 0000000..e01d13b
Binary files /dev/null and b/frontend/public/AnnualSummary2.png differ
diff --git a/frontend/public/AnnualSummary3.png b/frontend/public/AnnualSummary3.png
new file mode 100644
index 0000000..cc57330
Binary files /dev/null and b/frontend/public/AnnualSummary3.png differ
diff --git a/frontend/public/AnnualSummary4.gif b/frontend/public/AnnualSummary4.gif
new file mode 100644
index 0000000..14f6a3d
Binary files /dev/null and b/frontend/public/AnnualSummary4.gif differ
diff --git a/frontend/public/AnnualSummary5.gif b/frontend/public/AnnualSummary5.gif
new file mode 100644
index 0000000..bf35baf
Binary files /dev/null and b/frontend/public/AnnualSummary5.gif differ
diff --git a/frontend/public/AnnualSummary6.png b/frontend/public/AnnualSummary6.png
new file mode 100644
index 0000000..3a15932
Binary files /dev/null and b/frontend/public/AnnualSummary6.png differ
diff --git a/frontend/public/AnnualSummary7.png b/frontend/public/AnnualSummary7.png
new file mode 100644
index 0000000..37177c0
Binary files /dev/null and b/frontend/public/AnnualSummary7.png differ
diff --git a/frontend/public/AnnualSummary8.png b/frontend/public/AnnualSummary8.png
new file mode 100644
index 0000000..1c370dd
Binary files /dev/null and b/frontend/public/AnnualSummary8.png differ
diff --git a/frontend/public/Contact.png b/frontend/public/Contact.png
new file mode 100644
index 0000000..a32a652
Binary files /dev/null and b/frontend/public/Contact.png differ
diff --git a/frontend/public/QQImage_1770190010691_1103312318341691201.jpg b/frontend/public/QQImage_1770190010691_1103312318341691201.jpg
new file mode 100644
index 0000000..0d9c087
Binary files /dev/null and b/frontend/public/QQImage_1770190010691_1103312318341691201.jpg differ
diff --git a/frontend/public/RealTimeMessages.gif b/frontend/public/RealTimeMessages.gif
new file mode 100644
index 0000000..5186298
Binary files /dev/null and b/frontend/public/RealTimeMessages.gif differ
diff --git a/frontend/public/assets/images/LuckyBlock.png b/frontend/public/assets/images/LuckyBlock.png
new file mode 100644
index 0000000..92d8a98
Binary files /dev/null and b/frontend/public/assets/images/LuckyBlock.png differ
diff --git a/frontend/public/assets/images/wechat/channels-logo.svg b/frontend/public/assets/images/wechat/channels-logo.svg
new file mode 100644
index 0000000..17f9698
--- /dev/null
+++ b/frontend/public/assets/images/wechat/channels-logo.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/frontend/public/assets/images/wechat/overdue.png b/frontend/public/assets/images/wechat/overdue.png
new file mode 100644
index 0000000..9bf8016
Binary files /dev/null and b/frontend/public/assets/images/wechat/overdue.png differ
diff --git a/frontend/public/assets/images/wechat/wechat-trans-icon2.png b/frontend/public/assets/images/wechat/wechat-trans-icon2.png
index b9f72da..6d500a2 100644
Binary files a/frontend/public/assets/images/wechat/wechat-trans-icon2.png and b/frontend/public/assets/images/wechat/wechat-trans-icon2.png differ
diff --git a/frontend/public/edit.gif b/frontend/public/edit.gif
new file mode 100644
index 0000000..48547a6
Binary files /dev/null and b/frontend/public/edit.gif differ
diff --git a/frontend/public/export.png b/frontend/public/export.png
index 59345dd..b9de9a9 100644
Binary files a/frontend/public/export.png and b/frontend/public/export.png differ
diff --git a/frontend/public/message.png b/frontend/public/message.png
index a693d5d..304ae11 100644
Binary files a/frontend/public/message.png and b/frontend/public/message.png differ
diff --git a/frontend/public/search.png b/frontend/public/search.png
index 1d42332..663b583 100644
Binary files a/frontend/public/search.png and b/frontend/public/search.png differ
diff --git a/frontend/public/setting.png b/frontend/public/setting.png
new file mode 100644
index 0000000..23315a8
Binary files /dev/null and b/frontend/public/setting.png differ
diff --git a/frontend/public/sns.png b/frontend/public/sns.png
new file mode 100644
index 0000000..bdd3216
Binary files /dev/null and b/frontend/public/sns.png differ
diff --git a/frontend/public/style1.png b/frontend/public/style1.png
new file mode 100644
index 0000000..49f2b58
Binary files /dev/null and b/frontend/public/style1.png differ
diff --git a/frontend/stores/chatAccounts.js b/frontend/stores/chatAccounts.js
new file mode 100644
index 0000000..517cd94
--- /dev/null
+++ b/frontend/stores/chatAccounts.js
@@ -0,0 +1,111 @@
+import { defineStore } from 'pinia'
+
+const SELECTED_ACCOUNT_KEY = 'ui.selected_account'
+
+export const useChatAccountsStore = defineStore('chatAccounts', () => {
+ const accounts = ref([])
+ const selectedAccount = ref(null)
+ const loading = ref(false)
+ const error = ref('')
+ const loaded = ref(false)
+
+ // Capture apiBase during synchronous store setup when Nuxt context is available.
+ // useApiBase() calls useRuntimeConfig() which requires the Nuxt app context;
+ // that context can be lost inside deferred async functions (e.g. onMounted callbacks).
+ const _apiBase = useApiBase()
+
+ let loadPromise = null
+
+ const readSelectedAccount = () => {
+ if (!process.client) return null
+ try {
+ const raw = localStorage.getItem(SELECTED_ACCOUNT_KEY)
+ const v = String(raw || '').trim()
+ return v || null
+ } catch {
+ return null
+ }
+ }
+
+ const writeSelectedAccount = (value) => {
+ if (!process.client) return
+ try {
+ const v = String(value || '').trim()
+ if (!v) {
+ localStorage.removeItem(SELECTED_ACCOUNT_KEY)
+ return
+ }
+ localStorage.setItem(SELECTED_ACCOUNT_KEY, v)
+ } catch {}
+ }
+
+ const setSelectedAccount = (next) => {
+ selectedAccount.value = next ? String(next) : null
+ writeSelectedAccount(selectedAccount.value)
+ }
+
+ if (process.client) {
+ watch(selectedAccount, (next) => {
+ writeSelectedAccount(next)
+ })
+ }
+
+ const ensureLoaded = async ({ force = false } = {}) => {
+ if (!process.client) return
+ if (loaded.value && !force) return
+
+ if (loadPromise && !force) {
+ await loadPromise
+ return
+ }
+
+ loadPromise = (async () => {
+ loading.value = true
+ error.value = ''
+
+ if (!selectedAccount.value) {
+ const cached = readSelectedAccount()
+ if (cached) selectedAccount.value = cached
+ }
+
+ try {
+ const resp = await $fetch('/chat/accounts', { baseURL: _apiBase })
+ const nextAccounts = Array.isArray(resp?.accounts) ? resp.accounts : []
+ accounts.value = nextAccounts
+
+ const preferred = String(selectedAccount.value || '').trim()
+ const defaultAccount = String(resp?.default_account || '').trim()
+ const fallback = defaultAccount || nextAccounts[0] || ''
+ const nextSelected = preferred && nextAccounts.includes(preferred) ? preferred : (fallback || null)
+
+ selectedAccount.value = nextSelected
+ writeSelectedAccount(nextSelected)
+ loaded.value = true
+ } catch (e) {
+ accounts.value = []
+ selectedAccount.value = null
+ writeSelectedAccount(null)
+ loaded.value = true
+ error.value = e?.message || '加载账号失败'
+ } finally {
+ loading.value = false
+ }
+ })()
+
+ try {
+ await loadPromise
+ } finally {
+ loadPromise = null
+ }
+ }
+
+ return {
+ accounts,
+ selectedAccount,
+ loading,
+ error,
+ loaded,
+ ensureLoaded,
+ setSelectedAccount,
+ }
+})
diff --git a/frontend/stores/chatRealtime.js b/frontend/stores/chatRealtime.js
new file mode 100644
index 0000000..2d1a015
--- /dev/null
+++ b/frontend/stores/chatRealtime.js
@@ -0,0 +1,225 @@
+import { defineStore } from 'pinia'
+
+import { useChatAccountsStore } from '~/stores/chatAccounts'
+
+export const useChatRealtimeStore = defineStore('chatRealtime', () => {
+ const chatAccounts = useChatAccountsStore()
+
+ const enabled = ref(false)
+ const available = ref(false)
+ const checking = ref(false)
+ const statusInfo = ref(null)
+ const statusError = ref('')
+ const toggling = ref(false)
+ const toggleSeq = ref(0)
+ const lastToggleAction = ref('')
+ const changeSeq = ref(0)
+ const priorityUsername = ref('')
+
+ let eventSource = null
+ let changeDebounceTimer = null
+
+ const getAccount = () => String(chatAccounts.selectedAccount || '').trim()
+
+ const setPriorityUsername = (username) => {
+ priorityUsername.value = String(username || '').trim()
+ }
+
+ const ensureReadyAccount = async () => {
+ if (!process.client) return false
+ await chatAccounts.ensureLoaded()
+ return !!getAccount()
+ }
+
+ const fetchStatus = async () => {
+ if (!process.client) return
+ const account = getAccount()
+ if (!account) {
+ available.value = false
+ statusInfo.value = null
+ statusError.value = '未检测到已解密账号,请先解密数据库。'
+ return
+ }
+
+ const api = useApi()
+ checking.value = true
+ statusError.value = ''
+ try {
+ const resp = await api.getChatRealtimeStatus({ account })
+ available.value = !!resp?.available
+ statusInfo.value = resp?.realtime || null
+ statusError.value = ''
+ } catch (e) {
+ available.value = false
+ statusInfo.value = null
+ statusError.value = e?.message || '实时状态获取失败'
+ } finally {
+ checking.value = false
+ }
+ }
+
+ const stopStream = () => {
+ if (eventSource) {
+ try {
+ eventSource.close()
+ } catch {}
+ eventSource = null
+ }
+ if (changeDebounceTimer) {
+ try {
+ clearTimeout(changeDebounceTimer)
+ } catch {}
+ changeDebounceTimer = null
+ }
+ }
+
+ const bumpChangeSeqDebounced = () => {
+ if (changeDebounceTimer) return
+ changeDebounceTimer = setTimeout(() => {
+ changeDebounceTimer = null
+ changeSeq.value += 1
+ }, 500)
+ }
+
+ const startStream = () => {
+ stopStream()
+ if (!process.client || typeof window === 'undefined') return
+ if (!enabled.value) return
+ const account = getAccount()
+ if (!account) return
+ if (typeof EventSource === 'undefined') return
+
+ const apiBase = useApiBase()
+ const url = `${apiBase}/chat/realtime/stream?account=${encodeURIComponent(account)}`
+
+ try {
+ eventSource = new EventSource(url)
+ } catch {
+ eventSource = null
+ return
+ }
+
+ eventSource.onmessage = (ev) => {
+ try {
+ const data = JSON.parse(String(ev.data || '{}'))
+ if (String(data?.type || '') === 'change') {
+ bumpChangeSeqDebounced()
+ }
+ } catch {}
+ }
+
+ eventSource.onerror = () => {
+ // Keep `enabled` as-is; same behavior as the old in-page implementation.
+ stopStream()
+ }
+ }
+
+ const enable = async ({ silent = false } = {}) => {
+ if (toggling.value) return false
+ toggling.value = true
+ try {
+ const ok = await ensureReadyAccount()
+ if (!ok) {
+ if (!silent && process.client && typeof window !== 'undefined') {
+ window.alert('未检测到已解密账号,请先解密数据库。')
+ }
+ statusError.value = '未检测到已解密账号,请先解密数据库。'
+ return false
+ }
+
+ await fetchStatus()
+ if (!available.value) {
+ if (!silent && process.client && typeof window !== 'undefined') {
+ window.alert(statusError.value || '实时模式不可用:缺少密钥或 db_storage 路径。')
+ }
+ return false
+ }
+
+ enabled.value = true
+ startStream()
+ lastToggleAction.value = 'enabled'
+ toggleSeq.value += 1
+ return true
+ } finally {
+ toggling.value = false
+ }
+ }
+
+ const disable = async ({ silent = false } = {}) => {
+ if (toggling.value) return false
+ toggling.value = true
+ try {
+ const account = getAccount()
+ enabled.value = false
+ stopStream()
+
+ if (!account) {
+ lastToggleAction.value = 'disabled'
+ toggleSeq.value += 1
+ return true
+ }
+
+ try {
+ const api = useApi()
+ await api.syncChatRealtimeAll({
+ account,
+ max_scan: 200,
+ priority_username: priorityUsername.value || '',
+ priority_max_scan: 5000,
+ include_hidden: true,
+ include_official: true,
+ })
+ } catch (e) {
+ if (!silent && process.client && typeof window !== 'undefined') {
+ window.alert(e?.message || '关闭实时模式时同步失败')
+ }
+ }
+
+ lastToggleAction.value = 'disabled'
+ toggleSeq.value += 1
+ return true
+ } finally {
+ toggling.value = false
+ }
+ }
+
+ const toggle = async (opts = {}) => {
+ return enabled.value ? await disable(opts) : await enable(opts)
+ }
+
+ if (process.client) {
+ watch(
+ () => chatAccounts.selectedAccount,
+ async () => {
+ setPriorityUsername('')
+ await fetchStatus()
+ if (enabled.value) {
+ startStream()
+ }
+ },
+ { immediate: true }
+ )
+ }
+
+ return {
+ enabled,
+ available,
+ checking,
+ statusInfo,
+ statusError,
+ toggling,
+ toggleSeq,
+ lastToggleAction,
+ changeSeq,
+ priorityUsername,
+
+ setPriorityUsername,
+ ensureReadyAccount,
+ fetchStatus,
+ startStream,
+ stopStream,
+ enable,
+ disable,
+ toggle,
+ }
+})
diff --git a/frontend/stores/privacy.js b/frontend/stores/privacy.js
new file mode 100644
index 0000000..d32a331
--- /dev/null
+++ b/frontend/stores/privacy.js
@@ -0,0 +1,31 @@
+import { defineStore } from 'pinia'
+
+import { readPrivacyMode, writePrivacyMode } from '~/lib/privacy-mode'
+
+export const usePrivacyStore = defineStore('privacy', () => {
+ const privacyMode = ref(false)
+ const initialized = ref(false)
+
+ const init = () => {
+ if (initialized.value) return
+ initialized.value = true
+ privacyMode.value = readPrivacyMode(false)
+ }
+
+ const set = (enabled) => {
+ privacyMode.value = !!enabled
+ writePrivacyMode(privacyMode.value)
+ }
+
+ const toggle = () => {
+ set(!privacyMode.value)
+ }
+
+ return {
+ privacyMode,
+ init,
+ set,
+ toggle,
+ }
+})
+
diff --git a/frontend/stores/theme.js b/frontend/stores/theme.js
new file mode 100644
index 0000000..0626389
--- /dev/null
+++ b/frontend/stores/theme.js
@@ -0,0 +1,46 @@
+import { defineStore } from 'pinia'
+
+import {
+ UI_THEME_DARK,
+ UI_THEME_LIGHT,
+ applyUiTheme,
+ normalizeUiTheme,
+ readUiTheme,
+ writeUiTheme,
+} from '~/lib/ui-theme'
+
+export const useThemeStore = defineStore('theme', () => {
+ const theme = ref(UI_THEME_LIGHT)
+ const initialized = ref(false)
+
+ const isDark = computed(() => theme.value === UI_THEME_DARK)
+
+ const set = (nextTheme) => {
+ theme.value = normalizeUiTheme(nextTheme, UI_THEME_LIGHT)
+ writeUiTheme(theme.value)
+ applyUiTheme(theme.value)
+ }
+
+ const init = () => {
+ if (initialized.value) {
+ applyUiTheme(theme.value)
+ return
+ }
+ initialized.value = true
+ theme.value = readUiTheme(UI_THEME_LIGHT)
+ applyUiTheme(theme.value)
+ }
+
+ const toggle = () => {
+ set(isDark.value ? UI_THEME_LIGHT : UI_THEME_DARK)
+ }
+
+ return {
+ theme,
+ initialized,
+ isDark,
+ init,
+ set,
+ toggle,
+ }
+})
diff --git a/generate_config_template.py b/generate_config_template.py
index 6e5d88a..87a6b42 100644
--- a/generate_config_template.py
+++ b/generate_config_template.py
@@ -6,6 +6,7 @@
import sqlite3
import json
+import argparse
from pathlib import Path
from typing import Dict, List, Any
from collections import defaultdict
@@ -127,6 +128,82 @@ def analyze_database_structure(self, db_path: Path) -> Dict[str, Any]:
try:
cursor = conn.cursor()
+
+ def parse_columns_from_create_sql(create_sql: str) -> list[tuple[str, str]]:
+ """
+ 从建表 SQL 中尽力解析列名(用于 FTS5/缺失 tokenizer 扩展导致 PRAGMA 失败的情况)。
+ 返回 (name, type);类型缺失时默认 TEXT。
+ """
+ out: list[tuple[str, str]] = []
+ if not create_sql:
+ return out
+ try:
+ start = create_sql.find("(")
+ end = create_sql.rfind(")")
+ if start == -1 or end == -1 or end <= start:
+ return out
+ inner = create_sql[start + 1:end]
+
+ parts: list[str] = []
+ buf = ""
+ depth = 0
+ for ch in inner:
+ if ch == "(":
+ depth += 1
+ elif ch == ")":
+ depth -= 1
+ if ch == "," and depth == 0:
+ parts.append(buf.strip())
+ buf = ""
+ else:
+ buf += ch
+ if buf.strip():
+ parts.append(buf.strip())
+
+ for part in parts:
+ token = part.strip()
+ if not token:
+ continue
+ low = token.lower()
+ # 跳过约束/外键等
+ if low.startswith(("constraint", "primary", "unique", "foreign", "check")):
+ continue
+ # fts5 选项(tokenize/prefix/content/content_rowid 等)
+ if "=" in token:
+ key = token.split("=", 1)[0].strip().lower()
+ if key in ("tokenize", "prefix", "content", "content_rowid", "compress", "uncompress"):
+ continue
+ tokens = token.split()
+ if not tokens:
+ continue
+ name = tokens[0].strip("`\"[]")
+ typ = tokens[1].upper() if len(tokens) > 1 and "=" not in tokens[1] else "TEXT"
+ out.append((name, typ))
+ except Exception:
+ return out
+ return out
+
+ def get_table_columns(table_name: str) -> list[tuple[str, str]]:
+ # 先尝试 PRAGMA
+ try:
+ cursor.execute(f"PRAGMA table_info({table_name})")
+ columns = cursor.fetchall()
+ if columns:
+ return [(col[1], col[2]) for col in columns]
+ except Exception:
+ pass
+
+ # 兜底:从 sqlite_master.sql 解析
+ try:
+ cursor.execute(
+ "SELECT sql FROM sqlite_master WHERE type='table' AND name=?",
+ (table_name,),
+ )
+ row = cursor.fetchone()
+ create_sql = row[0] if row and len(row) > 0 else ""
+ return parse_columns_from_create_sql(create_sql or "")
+ except Exception:
+ return []
# 获取所有表名
cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
@@ -152,13 +229,10 @@ def analyze_database_structure(self, db_path: Path) -> Dict[str, Any]:
table_key = f"{prefix}_*" # 使用模式名
# 获取代表表的字段信息
- cursor.execute(f"PRAGMA table_info({representative_table})")
- columns = cursor.fetchall()
+ columns = get_table_columns(representative_table)
fields = {}
- for col in columns:
- field_name = col[1]
- field_type = col[2]
+ for field_name, field_type in columns:
fields[field_name] = {
"type": field_type,
"meaning": "", # 留空供用户填写
@@ -188,13 +262,10 @@ def analyze_database_structure(self, db_path: Path) -> Dict[str, Any]:
try:
# 获取表字段信息
- cursor.execute(f"PRAGMA table_info({table_name})")
- columns = cursor.fetchall()
+ columns = get_table_columns(table_name)
fields = {}
- for col in columns:
- field_name = col[1]
- field_type = col[2]
+ for field_name, field_type in columns:
fields[field_name] = {
"type": field_type,
"meaning": "", # 留空供用户填写
@@ -219,16 +290,23 @@ def analyze_database_structure(self, db_path: Path) -> Dict[str, Any]:
finally:
conn.close()
- def generate_template(self, output_file: str = "wechat_db_config_template.json"):
+ def generate_template(
+ self,
+ output_file: str = "wechat_db_config_template.json",
+ *,
+ include_excluded: bool = False,
+ include_message_shards: bool = False,
+ exclude_db_stems: set[str] | None = None,
+ ):
"""生成配置模板"""
print("开始生成微信数据库配置模板...")
# 定义要排除的数据库模式和描述
- excluded_patterns = {
- r'biz_message_\d+\.db$': '企业微信聊天记录数据库',
- r'bizchat\.db$': '企业微信联系人数据库',
- r'contact_fts\.db$': '搜索联系人数据库',
- r'favorite_fts\.db$': '搜索收藏数据库'
+ excluded_patterns = {} if include_excluded else {
+ r'biz_message_\d+\.db$': '公众号/企业微信聊天记录数据库(通常不参与个人聊天分析)',
+ r'bizchat\.db$': '企业微信联系人/会话数据库(通常不参与个人聊天分析)',
+ r'contact_fts\.db$': '联系人搜索索引数据库(FTS)',
+ r'favorite_fts\.db$': '收藏搜索索引数据库(FTS)'
}
# 查找所有数据库文件
@@ -263,29 +341,38 @@ def generate_template(self, output_file: str = "wechat_db_config_template.json")
for excluded_file, description in excluded_files:
print(f" - {excluded_file.name} ({description})")
+ # 显式排除指定 stem(不含 .db)
+ if exclude_db_stems:
+ before = len(db_files)
+ db_files = [p for p in db_files if p.stem not in exclude_db_stems]
+ after = len(db_files)
+ if before != after:
+ print(f"\n按 --exclude-db-stem 排除 {before - after} 个数据库: {sorted(exclude_db_stems)}")
+
print(f"\n实际处理 {len(db_files)} 个数据库文件")
# 过滤message数据库,只保留倒数第二个(与主脚本逻辑一致)
- message_numbered_dbs = []
- message_other_dbs = []
-
- for db in db_files:
- if re.match(r'message_\d+$', db.stem): # message_{数字}.db
- message_numbered_dbs.append(db)
- elif db.stem.startswith('message_'): # message_fts.db, message_resource.db等
- message_other_dbs.append(db)
-
- if len(message_numbered_dbs) > 1:
- # 按数字编号排序(提取数字进行排序)
- message_numbered_dbs.sort(key=lambda x: int(re.search(r'message_(\d+)', x.stem).group(1)))
- # 选择倒数第二个(按编号排序)
- selected_message_db = message_numbered_dbs[-2] # 倒数第二个
- print(f"检测到 {len(message_numbered_dbs)} 个message_{{数字}}.db数据库")
- print(f"选择倒数第二个: {selected_message_db.name}")
-
- # 从db_files中移除其他message_{数字}.db数据库,但保留message_fts.db等
- db_files = [db for db in db_files if not re.match(r'message_\d+$', db.stem)]
- db_files.append(selected_message_db)
+ if not include_message_shards:
+ message_numbered_dbs = []
+ message_other_dbs = []
+
+ for db in db_files:
+ if re.match(r'message_\d+$', db.stem): # message_{数字}.db
+ message_numbered_dbs.append(db)
+ elif db.stem.startswith('message_'): # message_fts.db, message_resource.db等
+ message_other_dbs.append(db)
+
+ if len(message_numbered_dbs) > 1:
+ # 按数字编号排序(提取数字进行排序)
+ message_numbered_dbs.sort(key=lambda x: int(re.search(r'message_(\d+)', x.stem).group(1)))
+ # 选择倒数第二个(按编号排序)
+ selected_message_db = message_numbered_dbs[-2] # 倒数第二个
+ print(f"检测到 {len(message_numbered_dbs)} 个message_{{数字}}.db数据库")
+ print(f"选择倒数第二个: {selected_message_db.name}")
+
+ # 从db_files中移除其他message_{数字}.db数据库,但保留message_fts.db等
+ db_files = [db for db in db_files if not re.match(r'message_\d+$', db.stem)]
+ db_files.append(selected_message_db)
print(f"实际分析 {len(db_files)} 个数据库文件")
@@ -370,11 +457,24 @@ def generate_template(self, output_file: str = "wechat_db_config_template.json")
def main():
"""主函数"""
+ parser = argparse.ArgumentParser(description="微信数据库字段配置模板生成器")
+ parser.add_argument("--databases-path", default="output/databases", help="解密后的数据库根目录(按账号分目录)")
+ parser.add_argument("--output", default="wechat_db_config_template.json", help="输出 JSON 模板路径")
+ parser.add_argument("--include-excluded", action="store_true", help="包含默认会被排除的数据库(如 bizchat/contact_fts/favorite_fts 等)")
+ parser.add_argument("--include-message-shards", action="store_true", help="包含所有 message_{n}.db(否则仅保留倒数第二个作代表)")
+ parser.add_argument("--exclude-db-stem", action="append", default=[], help="按 stem(不含 .db)排除数据库,可重复,例如: --exclude-db-stem digital_twin")
+ args = parser.parse_args()
+
print("微信数据库配置模板生成器")
print("=" * 50)
-
- generator = ConfigTemplateGenerator()
- generator.generate_template()
+
+ generator = ConfigTemplateGenerator(databases_path=args.databases_path)
+ generator.generate_template(
+ output_file=args.output,
+ include_excluded=bool(args.include_excluded),
+ include_message_shards=bool(args.include_message_shards),
+ exclude_db_stems=set(args.exclude_db_stem or []),
+ )
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/main.py b/main.py
index 426d786..924dc94 100644
--- a/main.py
+++ b/main.py
@@ -5,23 +5,30 @@
使用方法:
uv run main.py
-默认在8000端口启动API服务
+默认在10392端口启动API服务
"""
import uvicorn
import os
from pathlib import Path
+from wechat_decrypt_tool.runtime_settings import read_effective_backend_port
def main():
"""启动微信解密工具API服务"""
host = os.environ.get("WECHAT_TOOL_HOST", "127.0.0.1")
- port = int(os.environ.get("WECHAT_TOOL_PORT", "8000"))
+ port, port_source = read_effective_backend_port(default=10392)
access_host = "127.0.0.1" if host in {"0.0.0.0", "::"} else host
print("=" * 60)
print("微信解密工具 API 服务")
print("=" * 60)
print("正在启动服务...")
+ if port_source == "env":
+ print("端口来源: 环境变量 WECHAT_TOOL_PORT")
+ elif port_source == "settings":
+ print("端口来源: 配置文件 output/runtime_settings.json(由网页/桌面设置写入)")
+ else:
+ print("端口来源: 默认值")
print(f"API文档: http://{access_host}:{port}/docs")
print(f"健康检查: http://{access_host}:{port}/api/health")
print("按 Ctrl+C 停止服务")
diff --git a/pyproject.toml b/pyproject.toml
index 78cd6c2..e37fa6d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "wechat-decrypt-tool"
-version = "0.1.0"
+version = "1.3.0"
description = "Modern WeChat database decryption tool with React frontend"
readme = "README.md"
requires-python = ">=3.11"
@@ -18,6 +18,11 @@ dependencies = [
"loguru>=0.7.0",
"zstandard>=0.23.0",
"pilk>=0.2.4",
+ "pypinyin>=0.53.0",
+ "jieba>=0.42.1",
+ "wx_key>=1.1.0",
+ "packaging",
+ "httpx",
]
[project.optional-dependencies]
@@ -39,3 +44,6 @@ include = [
"src/wechat_decrypt_tool/native/wcdb_api.dll",
"src/wechat_decrypt_tool/native/WCDB.dll",
]
+
+[tool.uv]
+find-links = ["./tools/key_wheels/"]
diff --git a/src/wechat_decrypt_tool/__init__.py b/src/wechat_decrypt_tool/__init__.py
index f0cef38..eb13771 100644
--- a/src/wechat_decrypt_tool/__init__.py
+++ b/src/wechat_decrypt_tool/__init__.py
@@ -1,5 +1,5 @@
"""微信数据库解密工具
"""
-__version__ = "0.1.0"
-__author__ = "WeChat Decrypt Tool"
\ No newline at end of file
+__version__ = "1.3.0"
+__author__ = "WeChat Decrypt Tool"
diff --git a/src/wechat_decrypt_tool/api.py b/src/wechat_decrypt_tool/api.py
index df3773f..bc2df4d 100644
--- a/src/wechat_decrypt_tool/api.py
+++ b/src/wechat_decrypt_tool/api.py
@@ -5,30 +5,43 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
+from starlette.requests import Request
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.responses import FileResponse
from starlette.staticfiles import StaticFiles
from .logging_config import setup_logging, get_logger
+
+# 初始化日志系统
+setup_logging()
+logger = get_logger(__name__)
+request_logger = get_logger("wechat_decrypt_tool.request")
+
+from . import __version__ as APP_VERSION
from .path_fix import PathFixRoute
+from .chat_realtime_autosync import CHAT_REALTIME_AUTOSYNC
from .routers.chat import router as _chat_router
+from .routers.chat_contacts import router as _chat_contacts_router
from .routers.chat_export import router as _chat_export_router
from .routers.chat_media import router as _chat_media_router
from .routers.decrypt import router as _decrypt_router
from .routers.health import router as _health_router
+from .routers.admin import router as _admin_router
from .routers.keys import router as _keys_router
from .routers.media import router as _media_router
+from .routers.sns import router as _sns_router
+from .routers.sns_export import router as _sns_export_router
from .routers.wechat_detection import router as _wechat_detection_router
+from .routers.wrapped import router as _wrapped_router
+from .request_logging import log_server_errors_middleware
+from .sns_stage_timing import add_sns_stage_timing_headers
from .wcdb_realtime import WCDB_REALTIME, shutdown as _wcdb_shutdown
-
-# 初始化日志系统
-setup_logging()
-logger = get_logger(__name__)
+from .routers.biz import router as _biz_router
app = FastAPI(
title="微信数据库解密工具",
description="现代化的微信数据库解密工具,支持微信信息检测和数据库解密功能",
- version="0.1.0",
+ version=APP_VERSION,
)
# 设置自定义路由类
@@ -41,16 +54,50 @@
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
+ expose_headers=["X-SNS-Source", "X-SNS-Hit-Type", "X-SNS-X-Enc"],
)
+
+@app.middleware("http")
+async def _add_sns_stage_timing_headers(request: Request, call_next):
+ """Expose SNS stage metadata to the frontend without extra requests.
+
+ ` ` elements can't read response headers, but browsers can surface `Server-Timing`
+ via `performance.getEntriesByName(...).serverTiming` when `Timing-Allow-Origin` is set.
+ """
+
+ response = await call_next(request)
+ try:
+ add_sns_stage_timing_headers(
+ response.headers,
+ source=str(response.headers.get("X-SNS-Source") or ""),
+ hit_type=str(response.headers.get("X-SNS-Hit-Type") or ""),
+ x_enc=str(response.headers.get("X-SNS-X-Enc") or ""),
+ )
+ except Exception:
+ pass
+ return response
+
+
+@app.middleware("http")
+async def _log_server_errors(request: Request, call_next):
+ return await log_server_errors_middleware(request_logger, request, call_next)
+
+
app.include_router(_health_router)
+app.include_router(_admin_router)
app.include_router(_wechat_detection_router)
app.include_router(_decrypt_router)
app.include_router(_keys_router)
app.include_router(_media_router)
app.include_router(_chat_router)
+app.include_router(_chat_contacts_router)
app.include_router(_chat_export_router)
app.include_router(_chat_media_router)
+app.include_router(_sns_router)
+app.include_router(_sns_export_router)
+app.include_router(_wrapped_router)
+app.include_router(_biz_router)
class _SPAStaticFiles(StaticFiles):
@@ -61,9 +108,36 @@ def __init__(self, *args, **kwargs):
self._fallback_200 = Path(str(self.directory)) / "200.html"
self._fallback_index = Path(str(self.directory)) / "index.html"
+ @staticmethod
+ def _normalize_path(path: str) -> str:
+ return str(path or "").strip().lstrip("/")
+
+ @classmethod
+ def _is_shell_path(cls, path: str) -> bool:
+ normalized = cls._normalize_path(path)
+ return normalized in {"", "index.html", "200.html", "_payload.json"} or normalized.startswith(
+ "_payload.json/"
+ )
+
+ @classmethod
+ def _apply_cache_headers(cls, path: str, response):
+ normalized = cls._normalize_path(path)
+ try:
+ if cls._is_shell_path(normalized):
+ response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate"
+ response.headers["Pragma"] = "no-cache"
+ response.headers["Expires"] = "0"
+ elif normalized.startswith("_nuxt/"):
+ response.headers.setdefault("Cache-Control", "public, max-age=31536000, immutable")
+ except Exception:
+ pass
+ return response
+
async def get_response(self, path: str, scope): # type: ignore[override]
+ normalized = self._normalize_path(path)
try:
- return await super().get_response(path, scope)
+ response = await super().get_response(path, scope)
+ return self._apply_cache_headers(normalized, response)
except StarletteHTTPException as exc:
if exc.status_code != 404:
raise
@@ -74,8 +148,8 @@ async def get_response(self, path: str, scope): # type: ignore[override]
raise
if self._fallback_200.exists():
- return FileResponse(str(self._fallback_200))
- return FileResponse(str(self._fallback_index))
+ return self._apply_cache_headers("200.html", FileResponse(str(self._fallback_200)))
+ return self._apply_cache_headers("index.html", FileResponse(str(self._fallback_index)))
def _maybe_mount_frontend() -> None:
@@ -117,21 +191,48 @@ def _maybe_mount_frontend() -> None:
_maybe_mount_frontend()
+@app.on_event("startup")
+async def _startup_background_jobs() -> None:
+ try:
+ CHAT_REALTIME_AUTOSYNC.start()
+ except Exception:
+ logger.exception("Failed to start realtime autosync service")
+
+
@app.on_event("shutdown")
async def _shutdown_wcdb_realtime() -> None:
try:
- WCDB_REALTIME.close_all()
+ CHAT_REALTIME_AUTOSYNC.stop()
except Exception:
pass
+ close_ok = False
+ lock_timeout_s: float | None = 0.2
try:
- _wcdb_shutdown()
+ raw = str(os.environ.get("WECHAT_TOOL_WCDB_SHUTDOWN_LOCK_TIMEOUT_S", "0.2") or "").strip()
+ lock_timeout_s = float(raw) if raw else 0.2
+ if lock_timeout_s <= 0:
+ lock_timeout_s = None
except Exception:
- pass
+ lock_timeout_s = 0.2
+ try:
+ close_ok = WCDB_REALTIME.close_all(lock_timeout_s=lock_timeout_s)
+ except Exception:
+ close_ok = False
+ if close_ok:
+ try:
+ _wcdb_shutdown()
+ except Exception:
+ pass
+ else:
+ # If some conn locks were busy, other threads may still be running WCDB calls; avoid shutting down the lib.
+ logger.warning("[wcdb] close_all not fully completed; skip wcdb_shutdown")
if __name__ == "__main__":
import uvicorn
+ from .runtime_settings import read_effective_backend_port
+
host = os.environ.get("WECHAT_TOOL_HOST", "127.0.0.1")
- port = int(os.environ.get("WECHAT_TOOL_PORT", "8000"))
+ port, _ = read_effective_backend_port(default=10392)
uvicorn.run(app, host=host, port=port)
diff --git a/src/wechat_decrypt_tool/app_paths.py b/src/wechat_decrypt_tool/app_paths.py
index caab0a3..1da1fd2 100644
--- a/src/wechat_decrypt_tool/app_paths.py
+++ b/src/wechat_decrypt_tool/app_paths.py
@@ -3,6 +3,9 @@
import os
from pathlib import Path
+ENV_DATA_DIR_KEY = "WECHAT_TOOL_DATA_DIR"
+ENV_OUTPUT_DIR_KEY = "WECHAT_TOOL_OUTPUT_DIR"
+
def get_data_dir() -> Path:
"""Base writable directory for all runtime output (logs, databases, key store).
@@ -12,13 +15,16 @@ def get_data_dir() -> Path:
- Dev defaults to the current working directory (repo root).
"""
- v = os.environ.get("WECHAT_TOOL_DATA_DIR", "").strip()
+ v = os.environ.get(ENV_DATA_DIR_KEY, "").strip()
if v:
- return Path(v)
+ return Path(v).expanduser()
return Path.cwd()
def get_output_dir() -> Path:
+ v = os.environ.get(ENV_OUTPUT_DIR_KEY, "").strip()
+ if v:
+ return Path(v).expanduser()
return get_data_dir() / "output"
@@ -28,4 +34,3 @@ def get_output_databases_dir() -> Path:
def get_account_keys_path() -> Path:
return get_output_dir() / "account_keys.json"
-
diff --git a/src/wechat_decrypt_tool/avatar_cache.py b/src/wechat_decrypt_tool/avatar_cache.py
new file mode 100644
index 0000000..c37eaee
--- /dev/null
+++ b/src/wechat_decrypt_tool/avatar_cache.py
@@ -0,0 +1,454 @@
+from __future__ import annotations
+
+import hashlib
+import os
+import re
+import sqlite3
+import time
+from email.utils import formatdate
+from pathlib import Path
+from typing import Any, Optional
+from urllib.parse import urlsplit, urlunsplit
+
+from .app_paths import get_output_dir
+from .logging_config import get_logger
+
+logger = get_logger(__name__)
+
+AVATAR_CACHE_TTL_SECONDS = 7 * 24 * 60 * 60
+
+
+def is_avatar_cache_enabled() -> bool:
+ v = str(os.environ.get("WECHAT_TOOL_AVATAR_CACHE_ENABLED", "1") or "").strip().lower()
+ return v not in {"", "0", "false", "off", "no"}
+
+
+def get_avatar_cache_root_dir() -> Path:
+ return get_output_dir() / "avatar_cache"
+
+
+def _safe_segment(value: str) -> str:
+ cleaned = re.sub(r"[^0-9A-Za-z._-]+", "_", str(value or "").strip())
+ cleaned = cleaned.strip("._-")
+ return cleaned or "default"
+
+
+def _account_layout(account: str) -> tuple[Path, Path, Path, Path]:
+ account_dir = get_avatar_cache_root_dir() / _safe_segment(account)
+ files_dir = account_dir / "files"
+ tmp_dir = account_dir / "tmp"
+ db_path = account_dir / "avatar_cache.db"
+ return account_dir, files_dir, tmp_dir, db_path
+
+
+def _ensure_account_layout(account: str) -> tuple[Path, Path, Path, Path]:
+ account_dir, files_dir, tmp_dir, db_path = _account_layout(account)
+ account_dir.mkdir(parents=True, exist_ok=True)
+ files_dir.mkdir(parents=True, exist_ok=True)
+ tmp_dir.mkdir(parents=True, exist_ok=True)
+ return account_dir, files_dir, tmp_dir, db_path
+
+
+def _connect(account: str) -> sqlite3.Connection:
+ _, _, _, db_path = _ensure_account_layout(account)
+ conn = sqlite3.connect(str(db_path), timeout=5)
+ conn.row_factory = sqlite3.Row
+ _ensure_schema(conn)
+ return conn
+
+
+def _ensure_schema(conn: sqlite3.Connection) -> None:
+ conn.execute(
+ """
+ CREATE TABLE IF NOT EXISTS avatar_cache_entries (
+ account TEXT NOT NULL,
+ cache_key TEXT NOT NULL,
+ source_kind TEXT NOT NULL,
+ username TEXT NOT NULL DEFAULT '',
+ source_url TEXT NOT NULL DEFAULT '',
+ source_md5 TEXT NOT NULL DEFAULT '',
+ source_update_time INTEGER NOT NULL DEFAULT 0,
+ rel_path TEXT NOT NULL DEFAULT '',
+ media_type TEXT NOT NULL DEFAULT 'application/octet-stream',
+ size_bytes INTEGER NOT NULL DEFAULT 0,
+ etag TEXT NOT NULL DEFAULT '',
+ last_modified TEXT NOT NULL DEFAULT '',
+ fetched_at INTEGER NOT NULL DEFAULT 0,
+ checked_at INTEGER NOT NULL DEFAULT 0,
+ expires_at INTEGER NOT NULL DEFAULT 0,
+ PRIMARY KEY (account, cache_key)
+ )
+ """
+ )
+ conn.execute(
+ "CREATE INDEX IF NOT EXISTS idx_avatar_cache_entries_account_username ON avatar_cache_entries(account, username)"
+ )
+ conn.execute(
+ "CREATE INDEX IF NOT EXISTS idx_avatar_cache_entries_account_source ON avatar_cache_entries(account, source_kind, source_url)"
+ )
+ conn.commit()
+
+
+def _row_to_dict(row: Optional[sqlite3.Row]) -> Optional[dict[str, Any]]:
+ if row is None:
+ return None
+ out: dict[str, Any] = {}
+ for k in row.keys():
+ out[str(k)] = row[k]
+ return out
+
+
+def normalize_avatar_source_url(url: str) -> str:
+ raw = str(url or "").strip()
+ if not raw:
+ return ""
+ try:
+ p = urlsplit(raw)
+ except Exception:
+ return raw
+ scheme = str(p.scheme or "").lower()
+ host = str(p.hostname or "").lower()
+ if not scheme or not host:
+ return raw
+ netloc = host
+ if p.port:
+ netloc = f"{host}:{int(p.port)}"
+ path = p.path or "/"
+ return urlunsplit((scheme, netloc, path, p.query or "", ""))
+
+
+def cache_key_for_avatar_user(username: str) -> str:
+ u = str(username or "").strip()
+ return hashlib.sha1(f"user:{u}".encode("utf-8", errors="ignore")).hexdigest()
+
+
+def cache_key_for_avatar_url(url: str) -> str:
+ u = normalize_avatar_source_url(url)
+ return hashlib.sha1(f"url:{u}".encode("utf-8", errors="ignore")).hexdigest()
+
+
+def get_avatar_cache_entry(account: str, cache_key: str) -> Optional[dict[str, Any]]:
+ if (not is_avatar_cache_enabled()) or (not cache_key):
+ return None
+ try:
+ conn = _connect(account)
+ except Exception:
+ return None
+ try:
+ row = conn.execute(
+ "SELECT * FROM avatar_cache_entries WHERE account = ? AND cache_key = ? LIMIT 1",
+ (str(account or ""), str(cache_key or "")),
+ ).fetchone()
+ return _row_to_dict(row)
+ except Exception:
+ return None
+ finally:
+ try:
+ conn.close()
+ except Exception:
+ pass
+
+
+def get_avatar_cache_user_entry(account: str, username: str) -> Optional[dict[str, Any]]:
+ if not username:
+ return None
+ return get_avatar_cache_entry(account, cache_key_for_avatar_user(username))
+
+
+def get_avatar_cache_url_entry(account: str, source_url: str) -> Optional[dict[str, Any]]:
+ if not source_url:
+ return None
+ return get_avatar_cache_entry(account, cache_key_for_avatar_url(source_url))
+
+
+def resolve_avatar_cache_entry_path(account: str, entry: Optional[dict[str, Any]]) -> Optional[Path]:
+ if not entry:
+ return None
+ rel = str(entry.get("rel_path") or "").strip().replace("\\", "/")
+ if not rel:
+ return None
+ account_dir, _, _, _ = _account_layout(account)
+ p = account_dir / rel
+ try:
+ account_dir_resolved = account_dir.resolve()
+ p_resolved = p.resolve()
+ if p_resolved != account_dir_resolved and account_dir_resolved not in p_resolved.parents:
+ return None
+ return p_resolved
+ except Exception:
+ return p
+
+
+def avatar_cache_entry_file_exists(account: str, entry: Optional[dict[str, Any]]) -> Optional[Path]:
+ p = resolve_avatar_cache_entry_path(account, entry)
+ if not p:
+ return None
+ try:
+ if p.exists() and p.is_file():
+ return p
+ except Exception:
+ return None
+ return None
+
+
+def avatar_cache_entry_is_fresh(entry: Optional[dict[str, Any]], now_ts: Optional[int] = None) -> bool:
+ if not entry:
+ return False
+ try:
+ expires = int(entry.get("expires_at") or 0)
+ except Exception:
+ expires = 0
+ if expires <= 0:
+ return False
+ now0 = int(now_ts or time.time())
+ return expires > now0
+
+
+def _guess_ext(media_type: str) -> str:
+ mt = str(media_type or "").strip().lower()
+ if mt == "image/jpeg":
+ return "jpg"
+ if mt == "image/png":
+ return "png"
+ if mt == "image/gif":
+ return "gif"
+ if mt == "image/webp":
+ return "webp"
+ if mt == "image/bmp":
+ return "bmp"
+ if mt == "image/svg+xml":
+ return "svg"
+ if mt == "image/avif":
+ return "avif"
+ if mt.startswith("image/"):
+ return mt.split("/", 1)[1].split("+", 1)[0].split(";", 1)[0] or "img"
+ return "dat"
+
+
+def _http_date_from_ts(ts: Optional[int]) -> str:
+ try:
+ t = int(ts or 0)
+ except Exception:
+ t = 0
+ if t <= 0:
+ return ""
+ try:
+ return formatdate(timeval=float(t), usegmt=True)
+ except Exception:
+ return ""
+
+
+def upsert_avatar_cache_entry(
+ account: str,
+ *,
+ cache_key: str,
+ source_kind: str,
+ username: str = "",
+ source_url: str = "",
+ source_md5: str = "",
+ source_update_time: int = 0,
+ rel_path: str = "",
+ media_type: str = "application/octet-stream",
+ size_bytes: int = 0,
+ etag: str = "",
+ last_modified: str = "",
+ fetched_at: Optional[int] = None,
+ checked_at: Optional[int] = None,
+ expires_at: Optional[int] = None,
+) -> Optional[dict[str, Any]]:
+ if (not is_avatar_cache_enabled()) or (not cache_key):
+ return None
+
+ acct = str(account or "").strip()
+ ck = str(cache_key or "").strip()
+ sk = str(source_kind or "").strip().lower()
+ if not acct or not ck or not sk:
+ return None
+
+ source_url_norm = normalize_avatar_source_url(source_url) if source_url else ""
+
+ now_ts = int(time.time())
+ fetched = int(fetched_at if fetched_at is not None else now_ts)
+ checked = int(checked_at if checked_at is not None else now_ts)
+ expire_ts = int(expires_at if expires_at is not None else (checked + AVATAR_CACHE_TTL_SECONDS))
+
+ try:
+ conn = _connect(acct)
+ except Exception as e:
+ logger.warning(f"[avatar_cache_error] open db failed account={acct} err={e}")
+ return None
+ try:
+ conn.execute(
+ """
+ INSERT INTO avatar_cache_entries (
+ account, cache_key, source_kind, username, source_url,
+ source_md5, source_update_time, rel_path, media_type, size_bytes,
+ etag, last_modified, fetched_at, checked_at, expires_at
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ ON CONFLICT(account, cache_key) DO UPDATE SET
+ source_kind=excluded.source_kind,
+ username=excluded.username,
+ source_url=excluded.source_url,
+ source_md5=excluded.source_md5,
+ source_update_time=excluded.source_update_time,
+ rel_path=excluded.rel_path,
+ media_type=excluded.media_type,
+ size_bytes=excluded.size_bytes,
+ etag=excluded.etag,
+ last_modified=excluded.last_modified,
+ fetched_at=excluded.fetched_at,
+ checked_at=excluded.checked_at,
+ expires_at=excluded.expires_at
+ """,
+ (
+ acct,
+ ck,
+ sk,
+ str(username or "").strip(),
+ source_url_norm,
+ str(source_md5 or "").strip().lower(),
+ int(source_update_time or 0),
+ str(rel_path or "").strip().replace("\\", "/"),
+ str(media_type or "application/octet-stream").strip() or "application/octet-stream",
+ int(size_bytes or 0),
+ str(etag or "").strip(),
+ str(last_modified or "").strip(),
+ fetched,
+ checked,
+ expire_ts,
+ ),
+ )
+ conn.commit()
+ row = conn.execute(
+ "SELECT * FROM avatar_cache_entries WHERE account = ? AND cache_key = ? LIMIT 1",
+ (acct, ck),
+ ).fetchone()
+ return _row_to_dict(row)
+ except Exception as e:
+ logger.warning(f"[avatar_cache_error] upsert failed account={acct} cache_key={ck} err={e}")
+ return None
+ finally:
+ try:
+ conn.close()
+ except Exception:
+ pass
+
+
+def touch_avatar_cache_entry(account: str, cache_key: str, *, ttl_seconds: int = AVATAR_CACHE_TTL_SECONDS) -> bool:
+ if (not is_avatar_cache_enabled()) or (not cache_key):
+ return False
+ now_ts = int(time.time())
+ try:
+ conn = _connect(account)
+ except Exception:
+ return False
+ try:
+ conn.execute(
+ "UPDATE avatar_cache_entries SET checked_at = ?, expires_at = ? WHERE account = ? AND cache_key = ?",
+ (now_ts, now_ts + max(60, int(ttl_seconds or AVATAR_CACHE_TTL_SECONDS)), str(account or ""), str(cache_key or "")),
+ )
+ conn.commit()
+ return True
+ except Exception:
+ return False
+ finally:
+ try:
+ conn.close()
+ except Exception:
+ pass
+
+
+def write_avatar_cache_payload(
+ account: str,
+ *,
+ source_kind: str,
+ username: str = "",
+ source_url: str = "",
+ payload: bytes,
+ media_type: str,
+ source_md5: str = "",
+ source_update_time: int = 0,
+ etag: str = "",
+ last_modified: str = "",
+ ttl_seconds: int = AVATAR_CACHE_TTL_SECONDS,
+) -> tuple[Optional[dict[str, Any]], Optional[Path]]:
+ if (not is_avatar_cache_enabled()) or (not payload):
+ return None, None
+
+ acct = str(account or "").strip()
+ sk = str(source_kind or "").strip().lower()
+ if not acct or sk not in {"user", "url"}:
+ return None, None
+
+ source_url_norm = normalize_avatar_source_url(source_url) if source_url else ""
+ if sk == "user":
+ cache_key = cache_key_for_avatar_user(username)
+ else:
+ cache_key = cache_key_for_avatar_url(source_url_norm)
+
+ digest = hashlib.sha1(bytes(payload)).hexdigest()
+ ext = _guess_ext(media_type)
+ rel_path = f"files/{digest[:2]}/{digest}.{ext}"
+
+ try:
+ account_dir, _, tmp_dir, _ = _ensure_account_layout(acct)
+ except Exception as e:
+ logger.warning(f"[avatar_cache_error] ensure dirs failed account={acct} err={e}")
+ return None, None
+
+ abs_path = account_dir / rel_path
+ try:
+ abs_path.parent.mkdir(parents=True, exist_ok=True)
+ if (not abs_path.exists()) or (int(abs_path.stat().st_size) != len(payload)):
+ tmp_path = tmp_dir / f"{digest}.{time.time_ns()}.tmp"
+ tmp_path.write_bytes(payload)
+ os.replace(str(tmp_path), str(abs_path))
+ except Exception as e:
+ logger.warning(f"[avatar_cache_error] write file failed account={acct} path={abs_path} err={e}")
+ return None, None
+
+ if (not etag) and digest:
+ etag = f'"{digest}"'
+ if (not last_modified) and source_update_time:
+ last_modified = _http_date_from_ts(source_update_time)
+ if not last_modified:
+ last_modified = _http_date_from_ts(int(time.time()))
+
+ entry = upsert_avatar_cache_entry(
+ acct,
+ cache_key=cache_key,
+ source_kind=sk,
+ username=username,
+ source_url=source_url_norm,
+ source_md5=source_md5,
+ source_update_time=int(source_update_time or 0),
+ rel_path=rel_path,
+ media_type=media_type,
+ size_bytes=len(payload),
+ etag=etag,
+ last_modified=last_modified,
+ fetched_at=int(time.time()),
+ checked_at=int(time.time()),
+ expires_at=int(time.time()) + max(60, int(ttl_seconds or AVATAR_CACHE_TTL_SECONDS)),
+ )
+ if not entry:
+ return None, None
+ return entry, abs_path
+
+
+def build_avatar_cache_response_headers(
+ entry: Optional[dict[str, Any]], *, max_age: int = AVATAR_CACHE_TTL_SECONDS
+) -> dict[str, str]:
+ headers: dict[str, str] = {
+ "Cache-Control": f"public, max-age={int(max_age)}",
+ }
+ if not entry:
+ return headers
+ etag = str(entry.get("etag") or "").strip()
+ last_modified = str(entry.get("last_modified") or "").strip()
+ if etag:
+ headers["ETag"] = etag
+ if last_modified:
+ headers["Last-Modified"] = last_modified
+ return headers
+
diff --git a/src/wechat_decrypt_tool/backend_entry.py b/src/wechat_decrypt_tool/backend_entry.py
index bbfbf3b..4f0d5e3 100644
--- a/src/wechat_decrypt_tool/backend_entry.py
+++ b/src/wechat_decrypt_tool/backend_entry.py
@@ -9,11 +9,12 @@
import uvicorn
from wechat_decrypt_tool.api import app
+from wechat_decrypt_tool.runtime_settings import read_effective_backend_port
def main() -> None:
host = os.environ.get("WECHAT_TOOL_HOST", "127.0.0.1")
- port = int(os.environ.get("WECHAT_TOOL_PORT", "8000"))
+ port, _ = read_effective_backend_port(default=10392)
uvicorn.run(app, host=host, port=port, log_level="info")
diff --git a/src/wechat_decrypt_tool/chat_edit_store.py b/src/wechat_decrypt_tool/chat_edit_store.py
new file mode 100644
index 0000000..9149dd9
--- /dev/null
+++ b/src/wechat_decrypt_tool/chat_edit_store.py
@@ -0,0 +1,514 @@
+from __future__ import annotations
+
+import json
+import re
+import sqlite3
+import time
+from pathlib import Path
+from typing import Any, Optional
+
+from .app_paths import get_output_dir
+
+_HEX_RE = re.compile(r"^[0-9a-fA-F]+$")
+
+
+def _db_path() -> Path:
+ return get_output_dir() / "message_edits.db"
+
+
+def _connect() -> sqlite3.Connection:
+ db_path = _db_path()
+ db_path.parent.mkdir(parents=True, exist_ok=True)
+ conn = sqlite3.connect(str(db_path), timeout=5)
+ conn.row_factory = sqlite3.Row
+ _ensure_schema(conn)
+ return conn
+
+
+def ensure_schema() -> None:
+ conn: Optional[sqlite3.Connection] = None
+ try:
+ conn = _connect()
+ finally:
+ try:
+ if conn is not None:
+ conn.close()
+ except Exception:
+ pass
+
+
+def _ensure_schema(conn: sqlite3.Connection) -> None:
+ conn.execute(
+ """
+ CREATE TABLE IF NOT EXISTS message_edits (
+ account TEXT NOT NULL,
+ session_id TEXT NOT NULL,
+ db TEXT NOT NULL,
+ table_name TEXT NOT NULL,
+ local_id INTEGER NOT NULL,
+ first_edited_at INTEGER NOT NULL,
+ last_edited_at INTEGER NOT NULL,
+ edit_count INTEGER NOT NULL,
+ original_msg_json TEXT NOT NULL,
+ original_resource_json TEXT,
+ edited_cols_json TEXT,
+ PRIMARY KEY (account, session_id, db, table_name, local_id)
+ )
+ """
+ )
+ conn.execute(
+ "CREATE INDEX IF NOT EXISTS idx_message_edits_account_session ON message_edits(account, session_id)"
+ )
+ conn.execute("CREATE INDEX IF NOT EXISTS idx_message_edits_account_last ON message_edits(account, last_edited_at)")
+
+ # Backwards-compatible migrations for existing DBs.
+ try:
+ cols = {
+ str(r[1] or "").strip().lower()
+ for r in conn.execute("PRAGMA table_info(message_edits)").fetchall()
+ if r and len(r) > 1 and r[1]
+ }
+ if "edited_cols_json" not in cols:
+ conn.execute("ALTER TABLE message_edits ADD COLUMN edited_cols_json TEXT")
+ except Exception:
+ pass
+ conn.commit()
+
+
+def _now_ms() -> int:
+ return int(time.time() * 1000)
+
+
+def format_message_id(db: str, table_name: str, local_id: int) -> str:
+ return f"{str(db or '').strip()}:{str(table_name or '').strip()}:{int(local_id or 0)}"
+
+
+def parse_message_id(message_id: str) -> tuple[str, str, int]:
+ parts = str(message_id or "").split(":", 2)
+ if len(parts) != 3:
+ raise ValueError("Invalid message_id format.")
+ db = str(parts[0] or "").strip()
+ table_name = str(parts[1] or "").strip()
+ try:
+ local_id = int(parts[2] or 0)
+ except Exception:
+ raise ValueError("Invalid message_id format.")
+ if not db or not table_name or local_id <= 0:
+ raise ValueError("Invalid message_id format.")
+ return db, table_name, local_id
+
+
+def _bytes_to_hex(value: bytes) -> str:
+ return "0x" + value.hex()
+
+
+def _hex_to_bytes(value: str) -> Optional[bytes]:
+ s = str(value or "").strip()
+ if not s.startswith("0x"):
+ return None
+ hex_part = s[2:]
+ if (not hex_part) or (len(hex_part) % 2 != 0) or (_HEX_RE.match(hex_part) is None):
+ return None
+ try:
+ return bytes.fromhex(hex_part)
+ except Exception:
+ return None
+
+
+def _jsonify_blobs(obj: Any) -> Any:
+ if obj is None:
+ return None
+ if isinstance(obj, (bytes, bytearray, memoryview)):
+ return _bytes_to_hex(bytes(obj))
+ if isinstance(obj, dict):
+ return {str(k): _jsonify_blobs(v) for k, v in obj.items()}
+ if isinstance(obj, (list, tuple)):
+ return [_jsonify_blobs(v) for v in obj]
+ return obj
+
+
+def _dejsonify_blobs(obj: Any) -> Any:
+ if obj is None:
+ return None
+ if isinstance(obj, str):
+ b = _hex_to_bytes(obj)
+ return b if b is not None else obj
+ if isinstance(obj, dict):
+ return {str(k): _dejsonify_blobs(v) for k, v in obj.items()}
+ if isinstance(obj, list):
+ return [_dejsonify_blobs(v) for v in obj]
+ return obj
+
+
+def dumps_json_with_blobs(obj: Any) -> str:
+ return json.dumps(_jsonify_blobs(obj), ensure_ascii=False, separators=(",", ":"))
+
+
+def loads_json_with_blobs(payload: str) -> Any:
+ return _dejsonify_blobs(json.loads(str(payload or "") or "null"))
+
+
+def upsert_original_once(
+ *,
+ account: str,
+ session_id: str,
+ db: str,
+ table_name: str,
+ local_id: int,
+ original_msg: dict[str, Any],
+ original_resource: Optional[dict[str, Any]],
+ now_ms: Optional[int] = None,
+) -> None:
+ """Insert the original snapshot for a message only once, then bump counters on subsequent edits."""
+ a = str(account or "").strip()
+ sid = str(session_id or "").strip()
+ db_norm = str(db or "").strip()
+ t = str(table_name or "").strip()
+ lid = int(local_id or 0)
+ if not a or not sid or not db_norm or not t or lid <= 0:
+ raise ValueError("Missing required keys for message edit store.")
+
+ ts = int(now_ms if now_ms is not None else _now_ms())
+ msg_json = dumps_json_with_blobs(original_msg or {})
+ res_json = dumps_json_with_blobs(original_resource) if original_resource is not None else None
+
+ conn: Optional[sqlite3.Connection] = None
+ try:
+ conn = _connect()
+ existing = conn.execute(
+ """
+ SELECT 1
+ FROM message_edits
+ WHERE account = ? AND session_id = ? AND db = ? AND table_name = ? AND local_id = ?
+ LIMIT 1
+ """,
+ (a, sid, db_norm, t, lid),
+ ).fetchone()
+ if existing is None:
+ conn.execute(
+ """
+ INSERT INTO message_edits(
+ account, session_id, db, table_name, local_id,
+ first_edited_at, last_edited_at, edit_count,
+ original_msg_json, original_resource_json, edited_cols_json
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ """,
+ (a, sid, db_norm, t, lid, ts, ts, 1, msg_json, res_json, None),
+ )
+ else:
+ conn.execute(
+ """
+ UPDATE message_edits
+ SET last_edited_at = ?, edit_count = edit_count + 1
+ WHERE account = ? AND session_id = ? AND db = ? AND table_name = ? AND local_id = ?
+ """,
+ (ts, a, sid, db_norm, t, lid),
+ )
+ conn.commit()
+ finally:
+ try:
+ if conn is not None:
+ conn.close()
+ except Exception:
+ pass
+
+
+def _parse_json_str_list(payload: Any) -> list[str]:
+ if payload is None:
+ return []
+ if isinstance(payload, (list, tuple)):
+ return [str(x or "").strip() for x in payload if str(x or "").strip()]
+ s = str(payload or "").strip()
+ if not s:
+ return []
+ try:
+ v = json.loads(s)
+ except Exception:
+ return []
+ if not isinstance(v, list):
+ return []
+ return [str(x or "").strip() for x in v if str(x or "").strip()]
+
+
+def merge_edited_columns(
+ *,
+ account: str,
+ session_id: str,
+ db: str,
+ table_name: str,
+ local_id: int,
+ columns: list[str],
+) -> bool:
+ """Merge edited message column names into the per-message edit record.
+
+ This allows reset to restore only the fields actually modified by the tool.
+ """
+ a = str(account or "").strip()
+ sid = str(session_id or "").strip()
+ db_norm = str(db or "").strip()
+ t = str(table_name or "").strip()
+ lid = int(local_id or 0)
+ if not a or not sid or not db_norm or not t or lid <= 0:
+ return False
+
+ cols_in = [str(x or "").strip() for x in (columns or []) if str(x or "").strip()]
+ if not cols_in:
+ return True
+
+ conn: Optional[sqlite3.Connection] = None
+ try:
+ conn = _connect()
+ row = conn.execute(
+ """
+ SELECT edited_cols_json
+ FROM message_edits
+ WHERE account = ? AND session_id = ? AND db = ? AND table_name = ? AND local_id = ?
+ LIMIT 1
+ """,
+ (a, sid, db_norm, t, lid),
+ ).fetchone()
+ if row is None:
+ return False
+
+ existing = _parse_json_str_list(row[0] if row and len(row) else None)
+ merged = {c.lower() for c in existing if c} | {c.lower() for c in cols_in if c}
+ merged_list = sorted(merged)
+ payload = json.dumps(merged_list, ensure_ascii=False, separators=(",", ":"))
+ conn.execute(
+ """
+ UPDATE message_edits
+ SET edited_cols_json = ?
+ WHERE account = ? AND session_id = ? AND db = ? AND table_name = ? AND local_id = ?
+ """,
+ (payload, a, sid, db_norm, t, lid),
+ )
+ conn.commit()
+ return True
+ finally:
+ try:
+ if conn is not None:
+ conn.close()
+ except Exception:
+ pass
+
+
+def _row_to_dict(row: Optional[sqlite3.Row]) -> Optional[dict[str, Any]]:
+ if row is None:
+ return None
+ out: dict[str, Any] = {}
+ for k in row.keys():
+ out[str(k)] = row[k]
+ return out
+
+
+def list_sessions(account: str) -> list[dict[str, Any]]:
+ a = str(account or "").strip()
+ if not a:
+ return []
+
+ conn: Optional[sqlite3.Connection] = None
+ try:
+ conn = _connect()
+ rows = conn.execute(
+ """
+ SELECT session_id, COUNT(*) AS msg_count, MAX(last_edited_at) AS last_edited_at
+ FROM message_edits
+ WHERE account = ?
+ GROUP BY session_id
+ ORDER BY last_edited_at DESC
+ """,
+ (a,),
+ ).fetchall()
+ out: list[dict[str, Any]] = []
+ for r in rows:
+ try:
+ sid = str(r["session_id"] or "").strip()
+ except Exception:
+ sid = ""
+ if not sid:
+ continue
+ out.append(
+ {
+ "session_id": sid,
+ "msg_count": int(r["msg_count"] or 0),
+ "last_edited_at": int(r["last_edited_at"] or 0),
+ }
+ )
+ return out
+ finally:
+ try:
+ if conn is not None:
+ conn.close()
+ except Exception:
+ pass
+
+
+def list_messages(account: str, session_id: str) -> list[dict[str, Any]]:
+ a = str(account or "").strip()
+ sid = str(session_id or "").strip()
+ if not a or not sid:
+ return []
+
+ conn: Optional[sqlite3.Connection] = None
+ try:
+ conn = _connect()
+ rows = conn.execute(
+ """
+ SELECT *
+ FROM message_edits
+ WHERE account = ? AND session_id = ?
+ ORDER BY last_edited_at ASC, local_id ASC
+ """,
+ (a, sid),
+ ).fetchall()
+ out: list[dict[str, Any]] = []
+ for r in rows:
+ item = _row_to_dict(r) or {}
+ try:
+ item["message_id"] = format_message_id(item.get("db") or "", item.get("table_name") or "", item.get("local_id") or 0)
+ except Exception:
+ item["message_id"] = ""
+ out.append(item)
+ return out
+ finally:
+ try:
+ if conn is not None:
+ conn.close()
+ except Exception:
+ pass
+
+
+def get_message_edit(account: str, session_id: str, message_id: str) -> Optional[dict[str, Any]]:
+ a = str(account or "").strip()
+ sid = str(session_id or "").strip()
+ if not a or not sid or not message_id:
+ return None
+ try:
+ db, table_name, local_id = parse_message_id(message_id)
+ except Exception:
+ return None
+
+ conn: Optional[sqlite3.Connection] = None
+ try:
+ conn = _connect()
+ row = conn.execute(
+ """
+ SELECT *
+ FROM message_edits
+ WHERE account = ? AND session_id = ? AND db = ? AND table_name = ? AND local_id = ?
+ LIMIT 1
+ """,
+ (a, sid, db, table_name, int(local_id)),
+ ).fetchone()
+ item = _row_to_dict(row)
+ if not item:
+ return None
+ item["message_id"] = format_message_id(db, table_name, local_id)
+ return item
+ finally:
+ try:
+ if conn is not None:
+ conn.close()
+ except Exception:
+ pass
+
+
+def delete_message_edit(account: str, session_id: str, message_id: str) -> bool:
+ a = str(account or "").strip()
+ sid = str(session_id or "").strip()
+ if not a or not sid or not message_id:
+ return False
+ try:
+ db, table_name, local_id = parse_message_id(message_id)
+ except Exception:
+ return False
+
+ conn: Optional[sqlite3.Connection] = None
+ try:
+ conn = _connect()
+ cur = conn.execute(
+ """
+ DELETE FROM message_edits
+ WHERE account = ? AND session_id = ? AND db = ? AND table_name = ? AND local_id = ?
+ """,
+ (a, sid, db, table_name, int(local_id)),
+ )
+ conn.commit()
+ return int(getattr(cur, "rowcount", 0) or 0) > 0
+ finally:
+ try:
+ if conn is not None:
+ conn.close()
+ except Exception:
+ pass
+
+
+def update_message_edit_local_id(
+ *,
+ account: str,
+ session_id: str,
+ db: str,
+ table_name: str,
+ old_local_id: int,
+ new_local_id: int,
+) -> bool:
+ """Update the primary key local_id for an existing edit record (unsafe operations may change Msg.local_id)."""
+ a = str(account or "").strip()
+ sid = str(session_id or "").strip()
+ db_norm = str(db or "").strip()
+ t = str(table_name or "").strip()
+ old_lid = int(old_local_id or 0)
+ new_lid = int(new_local_id or 0)
+ if not a or not sid or not db_norm or not t or old_lid <= 0 or new_lid <= 0:
+ return False
+ if old_lid == new_lid:
+ return True
+
+ conn: Optional[sqlite3.Connection] = None
+ try:
+ conn = _connect()
+ cur = conn.execute(
+ """
+ UPDATE message_edits
+ SET local_id = ?
+ WHERE account = ? AND session_id = ? AND db = ? AND table_name = ? AND local_id = ?
+ """,
+ (new_lid, a, sid, db_norm, t, old_lid),
+ )
+ conn.commit()
+ return int(getattr(cur, "rowcount", 0) or 0) > 0
+ except Exception:
+ return False
+ finally:
+ try:
+ if conn is not None:
+ conn.close()
+ except Exception:
+ pass
+
+
+def delete_account_edits(account: str) -> int:
+ a = str(account or "").strip()
+ if not a:
+ return 0
+
+ conn: Optional[sqlite3.Connection] = None
+ try:
+ conn = _connect()
+ cur = conn.execute(
+ """
+ DELETE FROM message_edits
+ WHERE account = ?
+ """,
+ (a,),
+ )
+ conn.commit()
+ return int(getattr(cur, "rowcount", 0) or 0)
+ except Exception:
+ return 0
+ finally:
+ try:
+ if conn is not None:
+ conn.close()
+ except Exception:
+ pass
diff --git a/src/wechat_decrypt_tool/chat_export_service.py b/src/wechat_decrypt_tool/chat_export_service.py
index ed5d345..8ef9ec6 100644
--- a/src/wechat_decrypt_tool/chat_export_service.py
+++ b/src/wechat_decrypt_tool/chat_export_service.py
@@ -1,9 +1,16 @@
from __future__ import annotations
+import functools
+import base64
+import hashlib
import heapq
+import html
+import ipaddress
import json
+import os
import re
import sqlite3
+import socket
import tempfile
import threading
import time
@@ -12,7 +19,10 @@
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
-from typing import Any, Iterable, Literal, Optional
+from typing import Any, Callable, Iterable, Literal, Optional
+from urllib.parse import urljoin, urlparse
+
+import requests
from .chat_helpers import (
_decode_message_content,
@@ -21,13 +31,17 @@
_extract_xml_attr,
_extract_xml_tag_or_attr,
_extract_xml_tag_text,
+ _format_session_time,
_infer_message_brief_by_local_type,
_infer_transfer_status_text,
_iter_message_db_paths,
_list_decrypted_accounts,
_load_contact_rows,
+ _load_latest_message_previews,
_lookup_resource_md5,
_parse_app_message,
+ _parse_location_message,
+ _parse_system_message_content,
_parse_pat_message,
_pick_display_name,
_quote_ident,
@@ -39,9 +53,10 @@
)
from .logging_config import get_logger
from .media_helpers import (
- _convert_silk_to_wav,
+ _convert_silk_to_browser_audio,
_detect_image_media_type,
_fallback_search_media_by_file_id,
+ _read_and_maybe_decrypt_media,
_resolve_account_db_storage_dir,
_resolve_account_wxid_dir,
_resolve_media_path_for_kind,
@@ -50,7 +65,7 @@
logger = get_logger(__name__)
-ExportFormat = Literal["json", "txt"]
+ExportFormat = Literal["json", "txt", "html"]
ExportScope = Literal["selected", "all", "groups", "singles"]
ExportStatus = Literal["queued", "running", "done", "error", "cancelled"]
MediaKind = Literal["image", "emoji", "video", "video_thumb", "voice", "file"]
@@ -74,6 +89,2238 @@ def _safe_name(s: str, max_len: int = 80) -> str:
return t
+def _resolve_export_output_dir(account_dir: Path, output_dir_raw: Any) -> Path:
+ text = str(output_dir_raw or "").strip()
+ if not text:
+ default_dir = account_dir.parents[1] / "exports" / account_dir.name
+ default_dir.mkdir(parents=True, exist_ok=True)
+ return default_dir
+
+ out_dir = Path(text).expanduser()
+ if not out_dir.is_absolute():
+ raise ValueError("output_dir must be an absolute path.")
+
+ try:
+ out_dir.mkdir(parents=True, exist_ok=True)
+ except Exception as e:
+ raise ValueError(f"Failed to prepare output_dir: {e}") from e
+
+ return out_dir.resolve()
+
+
+def _resolve_ui_public_dir() -> Optional[Path]:
+ """Best-effort resolve Nuxt generated public directory for exporting UI CSS.
+
+ Priority:
+ 1) `WECHAT_TOOL_UI_DIR` env
+ 2) repo default `frontend/.output/public`
+ """
+
+ ui_dir_env = os.environ.get("WECHAT_TOOL_UI_DIR", "").strip()
+ candidates: list[Path] = []
+ if ui_dir_env:
+ candidates.append(Path(ui_dir_env))
+
+ # Repo defaults: generated Nuxt output or checked-in desktop UI assets.
+ repo_root = Path(__file__).resolve().parents[2]
+ candidates.append(repo_root / "frontend" / ".output" / "public")
+ candidates.append(repo_root / "desktop" / "resources" / "ui")
+
+ for p in candidates:
+ try:
+ nuxt_dir = p / "_nuxt"
+ if nuxt_dir.is_dir() and any(nuxt_dir.glob("entry.*.css")):
+ return p
+ except Exception:
+ continue
+ return None
+
+
+def _load_ui_entry_css(ui_public_dir: Path) -> str:
+ """Load Nuxt `entry.*.css` content (choose largest file if multiple)."""
+
+ nuxt_dir = Path(ui_public_dir) / "_nuxt"
+ try:
+ css_files = list(nuxt_dir.glob("entry.*.css"))
+ except Exception:
+ css_files = []
+
+ if not css_files:
+ return ""
+
+ def sort_key(p: Path) -> int:
+ try:
+ return int(p.stat().st_size)
+ except Exception:
+ return 0
+
+ css_files.sort(key=sort_key, reverse=True)
+ best = css_files[0]
+ try:
+ return best.read_text(encoding="utf-8")
+ except Exception:
+ try:
+ return best.read_text(encoding="utf-8", errors="ignore")
+ except Exception:
+ return ""
+
+
+_VUE_SCOPED_ATTR_RE = re.compile(r"\[data-v-[0-9a-f]{8}\]", flags=re.IGNORECASE)
+_CHAT_HISTORY_MD5_TAG_RE = re.compile(
+ r"(?i)<(?:fullmd5|thumbfullmd5|md5|emoticonmd5|emojimd5|cdnthumbmd5)>([0-9a-f]{32})<"
+)
+_CHAT_HISTORY_URL_TAG_RE = re.compile(r"(?i)<(?:sourceheadurl|cdnurlstring|encrypturlstring|externurl)>(https?://[^<\s]+)<")
+_CHAT_HISTORY_SERVER_ID_TAG_RE = re.compile(r"(?i)\s*(\d+)\s*<")
+
+
+def _strip_vue_scoped_attrs(css: str) -> str:
+ """Strip Vue SFC scoped attribute selectors like `[data-v-xxxxxxxx]`."""
+
+ if not css:
+ return ""
+ try:
+ return _VUE_SCOPED_ATTR_RE.sub("", css)
+ except Exception:
+ return css
+
+
+def _load_ui_css_bundle(*, ui_public_dir: Optional[Path], report: dict[str, Any]) -> str:
+ """Load Nuxt CSS bundle for offline HTML export.
+
+ Includes:
+ - `_nuxt/entry.*.css` (base + tailwind utilities)
+ - Chat page chunks `_nuxt/*_username_*.css` (scoped selectors stripped)
+ - `_HTML_EXPORT_CSS_PATCH` appended last
+
+ Falls back to `_HTML_EXPORT_CSS_FALLBACK` when entry css is missing.
+
+ Note: We only bundle chat-related chunks because stripping Vue SFC scoped selectors (`[data-v-...]`) can
+ otherwise leak scoped utility overrides (e.g. `.text-sm[data-v-...]`) into global rules in the export.
+ """
+
+ if ui_public_dir is None:
+ try:
+ report["errors"].append("WARN: Nuxt UI dir not found; export HTML will use fallback styles.")
+ except Exception:
+ pass
+ return _HTML_EXPORT_CSS_FALLBACK + "\n\n" + _HTML_EXPORT_CSS_PATCH
+
+ entry_css = _load_ui_entry_css(ui_public_dir)
+ if not entry_css:
+ try:
+ report["errors"].append("WARN: Nuxt UI CSS not found; export HTML will use fallback styles.")
+ except Exception:
+ pass
+ return _HTML_EXPORT_CSS_FALLBACK + "\n\n" + _HTML_EXPORT_CSS_PATCH
+
+ entry_css = _strip_vue_scoped_attrs(entry_css)
+
+ nuxt_dir = Path(ui_public_dir) / "_nuxt"
+ chat_css_paths: list[Path] = []
+ try:
+ chat_css_paths = [p for p in nuxt_dir.glob("*_username_*.css") if p.is_file()]
+ except Exception:
+ chat_css_paths = []
+
+ chat_css_paths.sort(key=lambda p: p.name)
+
+ if not chat_css_paths:
+ try:
+ report["errors"].append(
+ "WARN: Nuxt chat CSS chunk not found (*_username_*.css); some message styles may be missing."
+ )
+ except Exception:
+ pass
+
+ extra_chunks: list[str] = []
+ for p in chat_css_paths:
+ try:
+ extra_chunks.append(_strip_vue_scoped_attrs(p.read_text(encoding="utf-8")))
+ except Exception:
+ try:
+ extra_chunks.append(_strip_vue_scoped_attrs(p.read_text(encoding="utf-8", errors="ignore")))
+ except Exception:
+ continue
+
+ parts = [entry_css]
+ if extra_chunks:
+ parts.append("\n\n".join(extra_chunks))
+ parts.append(_HTML_EXPORT_CSS_PATCH)
+ return "\n\n".join(parts)
+
+
+_TS_WECHAT_EMOJI_ENTRY_RE = re.compile(r'^\s*"(?P[^"]+)"\s*:\s*"(?P[^"]+)"\s*,?\s*$')
+
+
+@functools.lru_cache(maxsize=1)
+def _load_wechat_emoji_table() -> dict[str, str]:
+ repo_root = Path(__file__).resolve().parents[2]
+ path = repo_root / "frontend" / "utils" / "wechat-emojis.ts"
+ try:
+ text = path.read_text(encoding="utf-8")
+ except Exception:
+ return {}
+
+ table: dict[str, str] = {}
+ for line in text.splitlines():
+ stripped = line.strip()
+ if not stripped or stripped.startswith("//"):
+ continue
+ match = _TS_WECHAT_EMOJI_ENTRY_RE.match(line)
+ if match:
+ key = str(match.group("key") or "")
+ value = str(match.group("value") or "")
+ if key and value:
+ table[key] = value
+ return table
+
+
+@functools.lru_cache(maxsize=1)
+def _load_wechat_emoji_regex() -> Optional[re.Pattern[str]]:
+ table = _load_wechat_emoji_table()
+ if not table:
+ return None
+
+ keys = sorted(table.keys(), key=len, reverse=True)
+ escaped = [re.escape(k) for k in keys if k]
+ if not escaped:
+ return None
+
+ try:
+ return re.compile(f"({'|'.join(escaped)})")
+ except Exception:
+ return None
+
+
+def _zip_write_tree(
+ *,
+ zf: zipfile.ZipFile,
+ src_dir: Path,
+ dest_prefix: str,
+ written: set[str],
+) -> int:
+ """Recursively add a directory tree to the zip under `dest_prefix`.
+
+ Skips any file whose `arcname` already exists in `written`.
+ Returns number of files written.
+ """
+
+ try:
+ if not src_dir.exists() or (not src_dir.is_dir()):
+ return 0
+ except Exception:
+ return 0
+
+ prefix = str(dest_prefix or "").strip().strip("/").replace("\\", "/")
+ count = 0
+ try:
+ for p in src_dir.rglob("*"):
+ try:
+ if not p.is_file():
+ continue
+ except Exception:
+ continue
+ try:
+ rel = p.relative_to(src_dir).as_posix()
+ except Exception:
+ rel = p.name
+ arc = f"{prefix}/{rel}" if prefix else rel
+ arc = arc.lstrip("/").replace("\\", "/")
+ if not arc or arc in written:
+ continue
+ try:
+ zf.write(str(p), arcname=arc)
+ except Exception:
+ continue
+ written.add(arc)
+ count += 1
+ except Exception:
+ return count
+ return count
+
+
+_REMOTE_IMAGE_MAX_BYTES = 5 * 1024 * 1024
+_REMOTE_IMAGE_TIMEOUT = (5, 10)
+_REMOTE_IMAGE_ALLOWED_CT: dict[str, str] = {
+ "image/jpeg": "jpg",
+ "image/png": "png",
+ "image/gif": "gif",
+ "image/webp": "webp",
+}
+
+
+def _is_public_ip(ip_text: str) -> bool:
+ try:
+ ip = ipaddress.ip_address(str(ip_text or "").strip())
+ except Exception:
+ return False
+ return bool(getattr(ip, "is_global", False))
+
+
+def _is_safe_remote_host(hostname: str, port: Optional[int]) -> bool:
+ host = str(hostname or "").strip().lower().rstrip(".")
+ if not host:
+ return False
+ if host == "localhost" or host.endswith(".localhost"):
+ return False
+ try:
+ if _is_public_ip(host):
+ return True
+ if re.fullmatch(r"[0-9a-f:]+", host) and ":" in host and (not _is_public_ip(host)):
+ return False
+ except Exception:
+ pass
+
+ try:
+ infos = socket.getaddrinfo(host, int(port or 443), type=socket.SOCK_STREAM)
+ except Exception:
+ return False
+
+ for info in infos:
+ try:
+ sockaddr = info[4]
+ ip_text = str(sockaddr[0] or "")
+ except Exception:
+ ip_text = ""
+ if not _is_public_ip(ip_text):
+ return False
+ return True
+
+
+def _download_remote_image_to_zip(
+ *,
+ zf: zipfile.ZipFile,
+ url: str,
+ remote_written: dict[str, str],
+ report: dict[str, Any],
+) -> str:
+ raw = str(url or "").strip()
+ if not raw:
+ return ""
+
+ cached = remote_written.get(raw)
+ if cached is not None:
+ return cached
+
+ current = raw
+ last_error = ""
+
+ for _ in range(4): # 0..3 redirects
+ parsed = urlparse(current)
+ if parsed.scheme not in {"http", "https"}:
+ last_error = f"unsupported scheme: {parsed.scheme}"
+ break
+ host = parsed.hostname or ""
+ if not host:
+ last_error = "missing hostname"
+ break
+ if not _is_safe_remote_host(host, parsed.port):
+ last_error = f"blocked host: {host}"
+ break
+
+ resp = None
+ try:
+ resp = requests.get(
+ current,
+ stream=True,
+ timeout=_REMOTE_IMAGE_TIMEOUT,
+ allow_redirects=False,
+ headers={
+ "User-Agent": "wechat-chat-export/1.0",
+ "Accept": "image/*",
+ },
+ )
+
+ if int(resp.status_code) in {301, 302, 303, 307, 308}:
+ loc = str(resp.headers.get("Location") or "").strip()
+ if not loc:
+ last_error = f"redirect without Location ({resp.status_code})"
+ break
+ current = urljoin(current, loc)
+ continue
+
+ if int(resp.status_code) != 200:
+ last_error = f"http {resp.status_code}"
+ break
+
+ ct = str(resp.headers.get("Content-Type") or "").split(";", 1)[0].strip().lower()
+ ext = _REMOTE_IMAGE_ALLOWED_CT.get(ct, "")
+
+ cl = str(resp.headers.get("Content-Length") or "").strip()
+ if cl:
+ try:
+ if int(cl) > _REMOTE_IMAGE_MAX_BYTES:
+ last_error = f"remote image too large: {cl} bytes"
+ break
+ except Exception:
+ pass
+
+ buf = bytearray()
+ too_large = False
+ for chunk in resp.iter_content(chunk_size=65536):
+ if not chunk:
+ continue
+ buf.extend(chunk)
+ if len(buf) > _REMOTE_IMAGE_MAX_BYTES:
+ too_large = True
+ break
+
+ if too_large:
+ last_error = f"remote image too large: >{_REMOTE_IMAGE_MAX_BYTES} bytes"
+ break
+
+ if not ext:
+ # Some WeChat CDN endpoints return `application/octet-stream` even for images.
+ # Detect by magic bytes to improve offline exports for merged-forward emojis/avatars.
+ try:
+ mt2 = _detect_image_media_type(bytes(buf[:32]))
+ except Exception:
+ mt2 = ""
+ ext = _REMOTE_IMAGE_ALLOWED_CT.get(str(mt2 or "").strip().lower(), "")
+ if not ext:
+ last_error = f"unsupported content-type: {ct or 'unknown'}"
+ break
+
+ h = hashlib.sha256(raw.encode("utf-8", errors="ignore")).hexdigest()
+ arc = f"media/remote/{h[:32]}.{ext}"
+ zf.writestr(arc, bytes(buf))
+ remote_written[raw] = arc
+ return arc
+ except Exception as e:
+ last_error = f"request failed: {e}"
+ break
+ finally:
+ try:
+ if resp is not None:
+ resp.close()
+ except Exception:
+ pass
+
+ try:
+ clipped = raw if len(raw) <= 260 else (raw[:257] + "...")
+ report["errors"].append(f"WARN: Remote image download skipped/failed: {clipped} ({last_error})")
+ except Exception:
+ pass
+ remote_written[raw] = ""
+ return ""
+
+
+_HTML_EXPORT_CSS_FALLBACK = """
+/* Fallback styles for chat export HTML (Nuxt build CSS not found). */
+html, body { height: 100%; }
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
+ "Helvetica Neue", Helvetica, Arial, sans-serif;
+ background: #EDEDED;
+ color: #111827;
+}
+a { color: inherit; }
+"""
+
+
+_HTML_EXPORT_CSS_PATCH = """
+/* Offline HTML viewer patch */
+:root {
+ /* Keep aligned with frontend defaults (see `frontend/app.vue`). */
+ --dpr: 1;
+ --message-radius: 4px;
+ --sidebar-rail-step: 48px;
+ --sidebar-rail-btn: 32px;
+ --sidebar-rail-icon: 24px;
+}
+html, body { height: 100%; }
+body { background: #EDEDED; }
+
+/* Layout helpers (used by exported HTML). */
+.wce-root { height: 100vh; display: flex; overflow: hidden; background: #EDEDED; }
+.wce-rail { width: 60px; min-width: 60px; max-width: 60px; background: #e8e7e7; border-right: 1px solid #e5e7eb; display: flex; flex-direction: column; }
+.wce-session-panel { width: calc(var(--session-list-width, 295px) / var(--dpr)); min-width: calc(var(--session-list-width, 295px) / var(--dpr)); max-width: calc(var(--session-list-width, 295px) / var(--dpr)); background: #F7F7F7; border-right: 1px solid #e5e7eb; display: flex; flex-direction: column; min-height: 0; }
+.wce-chat-area { flex: 1; display: flex; flex-direction: column; min-height: 0; background: #EDEDED; }
+.wce-chat-main { flex: 1; display: flex; min-height: 0; }
+.wce-chat-col { flex: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; position: relative; }
+.wce-chat-header { height: calc(56px / var(--dpr)); padding: 0 calc(20px / var(--dpr)); display: flex; align-items: center; border-bottom: 1px solid #e5e7eb; background: #EDEDED; }
+.wce-chat-title { font-size: 1rem; font-weight: 500; color: #111827; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.wce-filter-select { font-size: 0.75rem; padding: calc(6px / var(--dpr)) calc(8px / var(--dpr)); border: 0; border-radius: calc(8px / var(--dpr)); background: transparent; color: #374151; }
+.wce-message-container { flex: 1; overflow: auto; padding: 16px; min-height: 0; }
+.wce-pager { display: flex; align-items: center; justify-content: center; gap: calc(12px / var(--dpr)); padding: calc(6px / var(--dpr)) 0 calc(12px / var(--dpr)); }
+.wce-pager-btn { font-size: 0.75rem; padding: calc(6px / var(--dpr)) calc(10px / var(--dpr)); border-radius: calc(8px / var(--dpr)); border: 1px solid #e5e7eb; background: #fff; color: #374151; cursor: pointer; }
+.wce-pager-btn:hover { background: #f9fafb; }
+.wce-pager-btn:disabled { opacity: 0.6; cursor: not-allowed; }
+.wce-pager-status { font-size: 0.75rem; color: #6b7280; }
+
+/* Single session item (middle column). */
+.wce-session-item { display: flex; align-items: center; gap: 12px; padding: 0 12px; height: 80px; border-bottom: 1px solid #f3f4f6; background: #DEDEDE; text-decoration: none; color: inherit; }
+.wce-session-avatar { width: 45px; height: 45px; border-radius: 6px; overflow: hidden; background: #d1d5db; flex-shrink: 0; }
+.wce-session-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
+.wce-session-meta { min-width: 0; flex: 1; }
+.wce-session-name { font-size: 0.875rem; font-weight: 600; color: #111827; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.wce-session-sub { font-size: 0.75rem; color: #6b7280; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: calc(2px / var(--dpr)); }
+
+/* Message rows (right column). */
+.wce-msg-row { display: flex; align-items: flex-start; margin-bottom: 24px; }
+.wce-msg-row-sent { justify-content: flex-end; }
+.wce-msg-row-received { justify-content: flex-start; }
+.wce-msg { display: flex; align-items: flex-start; max-width: 640px; }
+.wce-msg-sent { flex-direction: row-reverse; }
+.wce-avatar { width: calc(42px / var(--dpr)); height: calc(42px / var(--dpr)); border-radius: 6px; overflow: hidden; background: #d1d5db; flex-shrink: 0; }
+.wce-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
+.wce-avatar-sent { margin-left: 12px; }
+.wce-avatar-received { margin-right: 12px; }
+.wce-sender-name { font-size: 0.75rem; color: #6b7280; margin-bottom: calc(4px / var(--dpr)); max-width: calc(320px / var(--dpr)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+
+/* Bubble basics (tailwind classes may override when Nuxt CSS is present). */
+.wce-bubble { padding: calc(8px / var(--dpr)) calc(12px / var(--dpr)); border-radius: var(--message-radius); font-size: 0.875rem; line-height: 1.6; white-space: pre-wrap; word-break: break-word; max-width: calc(320px / var(--dpr)); position: relative; }
+.wce-bubble-sent { background: #95EC69; color: #000; }
+.wce-bubble-received { background: #fff; color: #1f2937; }
+
+/* WeChat-like bubble tail (fallback). */
+.bubble-tail-l, .bubble-tail-r { position: relative; }
+.bubble-tail-l::after {
+ content: '';
+ position: absolute;
+ left: -4px;
+ top: 12px;
+ width: 12px;
+ height: 12px;
+ background: #FFFFFF;
+ transform: rotate(45deg);
+ border-radius: 2px;
+}
+.bubble-tail-r::after {
+ content: '';
+ position: absolute;
+ right: -4px;
+ top: 12px;
+ width: 12px;
+ height: 12px;
+ background: #95EC69;
+ transform: rotate(45deg);
+ border-radius: 2px;
+}
+
+/* System messages. */
+.wce-system { display: flex; justify-content: center; margin: 16px 0; }
+.wce-system > div { font-size: 0.75rem; color: #9e9e9e; padding: calc(4px / var(--dpr)) 0; }
+
+/* Media blocks. */
+.wce-media-img { max-width: 240px; max-height: 240px; border-radius: var(--message-radius); display: block; object-fit: cover; }
+.wce-emoji-img { width: 96px; height: 96px; object-fit: contain; display: block; }
+.wce-video-wrap { position: relative; display: inline-block; border-radius: var(--message-radius); overflow: hidden; background: rgba(0,0,0,0.05); }
+.wce-video-thumb { display: block; width: 220px; max-width: 260px; height: auto; max-height: 260px; object-fit: cover; }
+.wce-video-play { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
+.wce-video-play > div { width: 48px; height: 48px; border-radius: 9999px; background: rgba(0,0,0,0.45); display: flex; align-items: center; justify-content: center; }
+
+.wce-file { border: 1px solid #e5e7eb; border-radius: 10px; padding: 10px 12px; background: #fff; max-width: 320px; }
+.wce-file-name { font-size: 0.8125rem; color: #111827; word-break: break-all; }
+.wce-file-meta { font-size: 0.75rem; color: #6b7280; margin-top: calc(4px / var(--dpr)); }
+.wce-file-actions { margin-top: 8px; }
+.wce-file-actions a { font-size: 0.75rem; color: #07c160; text-decoration: none; }
+.wce-file-actions a:hover { text-decoration: underline; }
+
+.wce-audio { width: 260px; max-width: 92vw; }
+.wce-audio-actions { margin-top: 6px; }
+.wce-audio-actions a { font-size: 0.75rem; color: #07c160; text-decoration: none; }
+.wce-audio-actions a:hover { text-decoration: underline; }
+
+/* Voice message fallback styles (keep close to `frontend/pages/chat/[[username]].vue`). */
+.wechat-voice-wrapper { display: flex; width: 100%; position: relative; }
+.wechat-voice-bubble {
+ border-radius: var(--message-radius);
+ position: relative;
+ transition: opacity 0.15s ease;
+ min-width: 80px;
+ max-width: 200px;
+ cursor: pointer;
+}
+.wechat-voice-bubble:hover { opacity: 0.85; }
+.wechat-voice-bubble:active { opacity: 0.7; }
+.wechat-voice-sent { background: #95EC69; }
+.wechat-voice-sent::after {
+ content: '';
+ position: absolute;
+ top: 50%;
+ right: -4px;
+ transform: translateY(-50%) rotate(45deg);
+ width: 10px;
+ height: 10px;
+ background: #95EC69;
+ border-radius: 2px;
+}
+.wechat-voice-received { background: #fff; }
+.wechat-voice-received::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: -4px;
+ transform: translateY(-50%) rotate(45deg);
+ width: 10px;
+ height: 10px;
+ background: #fff;
+ border-radius: 2px;
+}
+.wechat-voice-content { display: flex; align-items: center; padding: 8px 12px; gap: 8px; }
+.wechat-voice-icon { width: 18px; height: 18px; flex-shrink: 0; color: #1a1a1a; }
+.wechat-quote-voice-icon { width: 14px; height: 14px; color: inherit; }
+.voice-icon-sent { transform: scaleX(-1); }
+.wechat-voice-icon.voice-playing .voice-wave-2 { animation: voice-wave-2 1s infinite; }
+.wechat-voice-icon.voice-playing .voice-wave-3 { animation: voice-wave-3 1s infinite; }
+@keyframes voice-wave-2 {
+ 0%, 33% { opacity: 0; }
+ 34%, 100% { opacity: 1; }
+}
+@keyframes voice-wave-3 {
+ 0%, 66% { opacity: 0; }
+ 67%, 100% { opacity: 1; }
+}
+.wechat-voice-duration { font-size: 14px; color: #1a1a1a; }
+.wechat-voice-unread {
+ position: absolute;
+ top: 50%;
+ right: -20px;
+ transform: translateY(-50%);
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: #e75e58;
+}
+
+/* Index page helpers. */
+.wce-index { min-height: 100vh; background: #EDEDED; }
+.wce-index-container { max-width: 880px; margin: 0 auto; padding: 24px; }
+.wce-index-card { background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; overflow: hidden; }
+.wce-index-item { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-bottom: 1px solid #f3f4f6; text-decoration: none; color: inherit; }
+.wce-index-item:last-child { border-bottom: 0; }
+.wce-index-item:hover { background: #f9fafb; }
+.wce-index-title { font-size: 1.125rem; font-weight: 700; color: #111827; margin: 0 0 calc(6px / var(--dpr)) 0; }
+.wce-index-sub { font-size: 0.75rem; color: #6b7280; margin: 0 0 calc(16px / var(--dpr)) 0; }
+"""
+
+
+_HTML_EXPORT_JS = r"""
+(() => {
+ const updateDprVar = () => {
+ try {
+ document.documentElement.style.setProperty('--dpr', '1')
+ } catch {}
+ }
+
+ const hideJsMissingBanner = () => {
+ try {
+ const el = document.getElementById('wceJsMissing')
+ if (el) el.style.display = 'none'
+ } catch {}
+ }
+
+ const initSessionSearch = () => {
+ const input = document.getElementById('sessionSearchInput')
+ if (!input) return
+
+ const clearBtn = document.getElementById('sessionSearchClear')
+ const items = Array.from(document.querySelectorAll('[data-wce-session-item=\"1\"]'))
+
+ const apply = () => {
+ const q = String(input.value || '').trim().toLowerCase()
+ try { if (clearBtn) clearBtn.style.display = q ? '' : 'none' } catch {}
+
+ items.forEach((el) => {
+ if (!el) return
+ const isActive = String(el.getAttribute('aria-current') || '') === 'page'
+ const name = String(el.getAttribute('data-wce-session-name') || '').toLowerCase()
+ const username = String(el.getAttribute('data-wce-session-username') || '').toLowerCase()
+ const show = !q || isActive || name.includes(q) || username.includes(q)
+ try { el.style.display = show ? '' : 'none' } catch {}
+ })
+ }
+
+ input.addEventListener('input', apply)
+ if (clearBtn) {
+ clearBtn.addEventListener('click', () => {
+ try { input.value = '' } catch {}
+ try { input.focus() } catch {}
+ apply()
+ })
+ }
+ apply()
+ }
+
+ const initVoicePlayback = () => {
+ let activeAudio = null
+ let activeIcon = null
+
+ const stopAudio = (audio, icon) => {
+ if (!audio) return
+ try { audio.pause() } catch {}
+ try { audio.currentTime = 0 } catch {}
+ try { if (icon) icon.classList.remove('voice-playing') } catch {}
+ }
+
+ const bindAudioEnd = (audio) => {
+ if (!audio) return
+ try {
+ if (audio.dataset && audio.dataset.wceVoiceBound === '1') return
+ if (audio.dataset) audio.dataset.wceVoiceBound = '1'
+ } catch {}
+
+ try {
+ audio.addEventListener('ended', () => {
+ try {
+ const wrapper = audio.closest('.wechat-voice-wrapper') || audio.parentElement
+ const icon = wrapper ? wrapper.querySelector('.wechat-voice-icon') : null
+ if (icon) icon.classList.remove('voice-playing')
+ } catch {}
+
+ if (activeAudio === audio) {
+ activeAudio = null
+ activeIcon = null
+ }
+ })
+ } catch {}
+ }
+
+ document.addEventListener('click', (ev) => {
+ const target = ev && ev.target
+
+ const quoteBtn = target && target.closest ? target.closest('[data-wce-quote-voice-btn=\"1\"]') : null
+ if (quoteBtn) {
+ if (quoteBtn.hasAttribute && quoteBtn.hasAttribute('disabled')) return
+
+ const wrapper = quoteBtn.closest ? (quoteBtn.closest('[data-wce-quote-voice-wrapper=\"1\"]') || quoteBtn.parentElement) : quoteBtn.parentElement
+ if (!wrapper) return
+
+ const audio = wrapper.querySelector ? (wrapper.querySelector('audio[data-wce-quote-voice-audio=\"1\"]') || wrapper.querySelector('audio')) : null
+ if (!audio) return
+
+ bindAudioEnd(audio)
+
+ const icon = (quoteBtn.querySelector && quoteBtn.querySelector('.wechat-voice-icon')) || (wrapper.querySelector && wrapper.querySelector('.wechat-voice-icon'))
+
+ if (activeAudio && activeAudio !== audio) stopAudio(activeAudio, activeIcon)
+
+ const isPlaying = !audio.paused && !audio.ended
+ if (activeAudio === audio && isPlaying) {
+ stopAudio(audio, icon)
+ activeAudio = null
+ activeIcon = null
+ return
+ }
+
+ activeAudio = audio
+ activeIcon = icon
+ try { if (icon) icon.classList.add('voice-playing') } catch {}
+ try {
+ const p = audio.play()
+ if (p && typeof p.catch === 'function') {
+ p.catch(() => {
+ stopAudio(audio, icon)
+ if (activeAudio === audio) {
+ activeAudio = null
+ activeIcon = null
+ }
+ })
+ }
+ } catch {
+ stopAudio(audio, icon)
+ if (activeAudio === audio) {
+ activeAudio = null
+ activeIcon = null
+ }
+ }
+ return
+ }
+
+ const bubble = target && target.closest ? target.closest('.wechat-voice-bubble') : null
+ if (!bubble) return
+
+ const wrapper = bubble.closest('.wechat-voice-wrapper') || bubble.parentElement
+ if (!wrapper) return
+
+ const audio = wrapper.querySelector('audio')
+ if (!audio) return
+
+ bindAudioEnd(audio)
+
+ const icon = bubble.querySelector('.wechat-voice-icon') || wrapper.querySelector('.wechat-voice-icon')
+
+ if (activeAudio && activeAudio !== audio) stopAudio(activeAudio, activeIcon)
+
+ const isPlaying = !audio.paused && !audio.ended
+ if (activeAudio === audio && isPlaying) {
+ stopAudio(audio, icon)
+ activeAudio = null
+ activeIcon = null
+ return
+ }
+
+ activeAudio = audio
+ activeIcon = icon
+ try { if (icon) icon.classList.add('voice-playing') } catch {}
+ try {
+ const p = audio.play()
+ if (p && typeof p.catch === 'function') {
+ p.catch(() => {
+ stopAudio(audio, icon)
+ if (activeAudio === audio) {
+ activeAudio = null
+ activeIcon = null
+ }
+ })
+ }
+ } catch {
+ stopAudio(audio, icon)
+ if (activeAudio === audio) {
+ activeAudio = null
+ activeIcon = null
+ }
+ }
+ })
+ }
+
+ const applyMessageTypeFilter = () => {
+ const select = document.getElementById('messageTypeFilter')
+ if (!select) return
+ const selected = String(select.value || 'all')
+ const nodes = document.querySelectorAll('[data-render-type]')
+ nodes.forEach((el) => {
+ const rt = String(el.getAttribute('data-render-type') || 'text')
+ const show = selected === 'all' ? true : rt === selected
+ el.style.display = show ? '' : 'none'
+ })
+ }
+
+ const scrollToBottom = () => {
+ const container = document.getElementById('messageContainer')
+ if (!container) return
+ container.scrollTop = container.scrollHeight
+ }
+
+ const updateSessionMessageCount = () => {
+ const el = document.getElementById('sessionMessageCount')
+ const container = document.getElementById('messageContainer')
+ if (!el || !container) return
+ const items = container.querySelectorAll('[data-render-type]')
+ el.textContent = String(items.length)
+ }
+
+ const safeJsonParse = (text) => {
+ try { return JSON.parse(String(text || '')) } catch { return null }
+ }
+
+ const readMediaIndex = () => {
+ const el = document.getElementById('wceMediaIndex')
+ const obj = safeJsonParse(el ? el.textContent : '')
+ if (!obj || typeof obj !== 'object') return {}
+ return obj
+ }
+
+ const readPageMeta = () => {
+ const el = document.getElementById('wcePageMeta')
+ const obj = safeJsonParse(el ? el.textContent : '')
+ if (!obj || typeof obj !== 'object') return null
+ return obj
+ }
+
+ const initPagedMessageLoading = () => {
+ const meta = readPageMeta()
+ if (!meta) return
+
+ const totalPages = Number(meta.totalPages || 0)
+ if (!Number.isFinite(totalPages) || totalPages <= 1) return
+
+ const initialPage = Number(meta.initialPage || totalPages || 1)
+ const padWidth = Number(meta.padWidth || 0) || 0
+ const prefix = String(meta.pageFilePrefix || 'pages/page-')
+ const suffix = String(meta.pageFileSuffix || '.js')
+
+ const container = document.getElementById('messageContainer')
+ const list = document.getElementById('wceMessageList') || container
+ const pager = document.getElementById('wcePager')
+ const btn = document.getElementById('wceLoadPrevBtn')
+ const status = document.getElementById('wceLoadPrevStatus')
+ if (!container || !list || !pager || !btn) return
+
+ try { pager.style.display = '' } catch {}
+
+ const loaded = new Set()
+ loaded.add(initialPage)
+ let nextPage = initialPage - 1
+ let loading = false
+
+ const setStatus = (text) => {
+ try { if (status) status.textContent = String(text || '') } catch {}
+ }
+
+ const updateUi = (overrideText) => {
+ if (overrideText != null) {
+ setStatus(overrideText)
+ try { btn.disabled = false } catch {}
+ return
+ }
+ if (nextPage < 1) {
+ setStatus('已到底')
+ try { btn.disabled = true } catch {}
+ return
+ }
+ if (loading) {
+ setStatus('加载中...')
+ try { btn.disabled = true } catch {}
+ return
+ }
+ setStatus('点击加载更早消息')
+ try { btn.disabled = false } catch {}
+ }
+
+ const pageSrc = (n) => {
+ const num = padWidth > 0 ? String(n).padStart(padWidth, '0') : String(n)
+ return prefix + num + suffix
+ }
+
+ window.__WCE_PAGE_QUEUE__ = window.__WCE_PAGE_QUEUE__ || []
+ window.__WCE_PAGE_LOADED__ = (pageNo, html) => {
+ const n = Number(pageNo)
+ if (!Number.isFinite(n) || n < 1) return
+ if (loaded.has(n)) return
+ loaded.add(n)
+
+ try {
+ const prevH = container.scrollHeight
+ const prevTop = container.scrollTop
+ list.insertAdjacentHTML('afterbegin', String(html || ''))
+ const newH = container.scrollHeight
+ container.scrollTop = prevTop + (newH - prevH)
+ } catch {
+ try { list.insertAdjacentHTML('afterbegin', String(html || '')) } catch {}
+ }
+
+ loading = false
+ nextPage = n - 1
+ try { applyMessageTypeFilter() } catch {}
+ try { updateSessionMessageCount() } catch {}
+ updateUi()
+ }
+
+ // Flush any queued pages (should be rare, but keeps behavior robust).
+ try {
+ const q = window.__WCE_PAGE_QUEUE__
+ if (Array.isArray(q) && q.length) {
+ const items = q.slice(0)
+ q.length = 0
+ items.forEach((it) => {
+ try {
+ if (it && it.length >= 2) window.__WCE_PAGE_LOADED__(it[0], it[1])
+ } catch {}
+ })
+ }
+ } catch {}
+
+ const requestLoad = () => {
+ if (loading) return
+ if (nextPage < 1) return
+ const n = nextPage
+
+ loading = true
+ updateUi()
+
+ const s = document.createElement('script')
+ s.async = true
+ s.src = pageSrc(n)
+ s.onerror = () => {
+ loading = false
+ updateUi('加载失败,可重试')
+ }
+ try { document.body.appendChild(s) } catch {
+ loading = false
+ updateUi('加载失败,可重试')
+ }
+ }
+
+ btn.addEventListener('click', () => requestLoad())
+
+ let lastScrollAt = 0
+ container.addEventListener('scroll', () => {
+ const now = Date.now()
+ if (now - lastScrollAt < 200) return
+ lastScrollAt = now
+ if (container.scrollTop < 120) requestLoad()
+ })
+
+ updateUi()
+ }
+
+ const isMaybeMd5 = (value) => /^[0-9a-f]{32}$/i.test(String(value || '').trim())
+ const pickFirstMd5 = (...values) => {
+ for (const v of values) {
+ const s = String(v || '').trim()
+ if (isMaybeMd5(s)) return s.toLowerCase()
+ }
+ return ''
+ }
+
+ const normalizeChatHistoryUrl = (value) => String(value || '').trim().replace(/\s+/g, '')
+
+ const decodeBase64Utf8 = (b64) => {
+ try {
+ const bin = atob(String(b64 || ''))
+ const bytes = new Uint8Array(bin.length)
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)
+ if (typeof TextDecoder !== 'undefined') {
+ return new TextDecoder('utf-8', { fatal: false }).decode(bytes)
+ }
+ let out = ''
+ for (let i = 0; i < bytes.length; i++) out += String.fromCharCode(bytes[i])
+ return out
+ } catch {
+ return ''
+ }
+ }
+
+ const resolveMd5Any = (index, md5) => {
+ const key = String(md5 || '').trim().toLowerCase()
+ if (!key) return ''
+ const maps = [
+ index && index.images,
+ index && index.emojis,
+ index && index.videos,
+ index && index.videoThumbs,
+ ]
+ for (const m of maps) {
+ try {
+ if (m && m[key]) return String(m[key] || '')
+ } catch {}
+ }
+ return ''
+ }
+
+ const resolveServerMd5 = (index, serverId) => {
+ const key = String(serverId || '').trim()
+ if (!key) return ''
+ try {
+ const v = index && index.serverMd5 && index.serverMd5[key]
+ return isMaybeMd5(v) ? String(v || '').trim().toLowerCase() : ''
+ } catch {}
+ return ''
+ }
+
+ const resolveRemoteAny = (index, ...urls) => {
+ for (const u0 of urls) {
+ const u = normalizeChatHistoryUrl(u0)
+ if (!u) continue
+ try {
+ const local = index && index.remote && index.remote[u]
+ if (local) return String(local || '')
+ } catch {}
+ const ul = String(u || '').trim().toLowerCase()
+ if (ul.startsWith('http://') || ul.startsWith('https://')) return u
+ }
+ return ''
+ }
+
+ const parseChatHistoryRecord = (recordItemXml) => {
+ const xml = String(recordItemXml || '').trim()
+ if (!xml) return { info: null, items: [] }
+
+ const normalized = xml
+ .replace(/ /g, ' ')
+ .replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g, '')
+ .replace(/&(?!amp;|lt;|gt;|quot;|apos;|#\d+;|#x[\da-fA-F]+;)/g, '&')
+
+ let doc
+ try {
+ doc = new DOMParser().parseFromString(normalized, 'text/xml')
+ } catch {
+ return { info: null, items: [] }
+ }
+
+ const parserErrors = doc.getElementsByTagName('parsererror')
+ if (parserErrors && parserErrors.length) return { info: null, items: [] }
+
+ const getText = (node, tag) => {
+ try {
+ if (!node) return ''
+ const els = Array.from(node.getElementsByTagName(tag) || [])
+ const direct = els.find((el) => el && el.parentNode === node)
+ const el = direct || els[0]
+ return String(el?.textContent || '').trim()
+ } catch {
+ return ''
+ }
+ }
+
+ const getDirectChildXml = (node, tag) => {
+ try {
+ if (!node) return ''
+ const children = Array.from(node.children || [])
+ const el = children.find((c) => String(c?.tagName || '').toLowerCase() === String(tag || '').toLowerCase())
+ if (!el) return ''
+
+ const raw = String(el.textContent || '').trim()
+ if (raw && raw.startsWith('<') && raw.endsWith('>')) return raw
+
+ if (typeof XMLSerializer !== 'undefined') {
+ return new XMLSerializer().serializeToString(el)
+ }
+ } catch {}
+ return ''
+ }
+
+ const getAnyXml = (node, tag) => {
+ try {
+ if (!node) return ''
+ const els = Array.from(node.getElementsByTagName(tag) || [])
+ const direct = els.find((el) => el && el.parentNode === node)
+ const el = direct || els[0]
+ if (!el) return ''
+
+ const raw = String(el.textContent || '').trim()
+ if (raw && raw.startsWith('<') && raw.endsWith('>')) return raw
+ if (typeof XMLSerializer !== 'undefined') return new XMLSerializer().serializeToString(el)
+ } catch {}
+ return ''
+ }
+
+ const sameTag = (el, tag) => String(el?.tagName || '').toLowerCase() === String(tag || '').toLowerCase()
+
+ const closestAncestorByTag = (node, tag) => {
+ const lower = String(tag || '').toLowerCase()
+ let cur = node
+ while (cur) {
+ if (cur.nodeType === 1 && String(cur.tagName || '').toLowerCase() === lower) return cur
+ cur = cur.parentNode
+ }
+ return null
+ }
+
+ const root = doc?.documentElement
+ const isChatRoom = String(getText(root, 'isChatRoom') || '').trim() === '1'
+ const title = getText(root, 'title')
+ const desc = getText(root, 'desc') || getText(root, 'info')
+
+ const datalist = (() => {
+ try {
+ const all = Array.from(doc.getElementsByTagName('datalist') || [])
+ const top = root ? all.find((el) => closestAncestorByTag(el, 'recorditem') === root) : null
+ return top || all[0] || null
+ } catch {
+ return null
+ }
+ })()
+
+ const itemNodes = (() => {
+ if (datalist) return Array.from(datalist.children || []).filter((el) => sameTag(el, 'dataitem'))
+ return Array.from(root?.children || []).filter((el) => sameTag(el, 'dataitem'))
+ })()
+
+ const parsed = itemNodes.map((node, idx) => {
+ const datatype = String(node.getAttribute('datatype') || getText(node, 'datatype') || '').trim()
+ const dataid = String(node.getAttribute('dataid') || getText(node, 'dataid') || '').trim() || String(idx)
+
+ const sourcename = getText(node, 'sourcename')
+ const sourcetime = getText(node, 'sourcetime')
+ const sourceheadurl = normalizeChatHistoryUrl(getText(node, 'sourceheadurl'))
+ const datatitle = getText(node, 'datatitle')
+ const datadesc = getText(node, 'datadesc')
+ const link = normalizeChatHistoryUrl(getText(node, 'link') || getText(node, 'dataurl') || getText(node, 'url'))
+ const datafmt = getText(node, 'datafmt')
+ const duration = getText(node, 'duration')
+
+ const fullmd5 = getText(node, 'fullmd5')
+ const thumbfullmd5 = getText(node, 'thumbfullmd5')
+ const md5 = getText(node, 'md5') || getText(node, 'emoticonmd5') || getText(node, 'emojimd5') || getText(node, 'emojiMd5')
+ const cdnthumbmd5 = getText(node, 'cdnthumbmd5')
+ const cdnurlstring = normalizeChatHistoryUrl(getText(node, 'cdnurlstring'))
+ const encrypturlstring = normalizeChatHistoryUrl(getText(node, 'encrypturlstring'))
+ const externurl = normalizeChatHistoryUrl(getText(node, 'externurl'))
+ const aeskey = getText(node, 'aeskey')
+ const fromnewmsgid = getText(node, 'fromnewmsgid')
+ const srcMsgLocalid = getText(node, 'srcMsgLocalid')
+ const srcMsgCreateTime = getText(node, 'srcMsgCreateTime')
+ const nestedRecordItem = (
+ getAnyXml(node, 'recorditem')
+ || getDirectChildXml(node, 'recorditem')
+ || getText(node, 'recorditem')
+ || getAnyXml(node, 'recordxml')
+ || getDirectChildXml(node, 'recordxml')
+ || getText(node, 'recordxml')
+ )
+
+ let content = datatitle || datadesc
+ if (!content) {
+ if (datatype === '4') content = '[视频]'
+ else if (datatype === '2' || datatype === '3') content = '[图片]'
+ else if (datatype === '47' || datatype === '37') content = '[表情]'
+ else if (datatype) content = `[消息 ${datatype}]`
+ else content = '[消息]'
+ }
+
+ const fmt = String(datafmt || '').trim().toLowerCase().replace(/^\./, '')
+ const imageFormats = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'heic', 'heif'])
+
+ let renderType = 'text'
+ if (datatype === '17') {
+ renderType = 'chatHistory'
+ } else if (datatype === '5' || link) {
+ renderType = 'link'
+ } else if (datatype === '4' || String(duration || '').trim() || fmt === 'mp4') {
+ renderType = 'video'
+ } else if (datatype === '47' || datatype === '37') {
+ renderType = 'emoji'
+ } else if (
+ datatype === '2'
+ || datatype === '3'
+ || imageFormats.has(fmt)
+ || (datatype !== '1' && isMaybeMd5(fullmd5))
+ ) {
+ renderType = 'image'
+ } else if (isMaybeMd5(md5) && /表情/.test(String(content || ''))) {
+ renderType = 'emoji'
+ }
+
+ let outTitle = ''
+ let outUrl = ''
+ let recordItem = ''
+ if (renderType === 'chatHistory') {
+ outTitle = datatitle || content || '聊天记录'
+ content = datadesc || ''
+ recordItem = nestedRecordItem
+ } else if (renderType === 'link') {
+ outTitle = datatitle || content || ''
+ outUrl = link || externurl || ''
+ // datadesc can be an invisible filler; only keep as description when meaningful.
+ const cleanDesc = String(datadesc || '').replace(/[\\u3164\\u2800]/g, '').trim()
+ const cleanTitle = String(outTitle || '').replace(/[\\u3164\\u2800]/g, '').trim()
+ if (!cleanDesc || (cleanTitle && cleanDesc === cleanTitle)) content = ''
+ else content = String(datadesc || '').trim()
+ }
+
+ return {
+ id: dataid,
+ datatype,
+ sourcename,
+ sourcetime,
+ sourceheadurl,
+ datafmt,
+ duration,
+ fullmd5,
+ thumbfullmd5,
+ md5,
+ cdnthumbmd5,
+ cdnurlstring,
+ encrypturlstring,
+ externurl,
+ aeskey,
+ fromnewmsgid,
+ srcMsgLocalid,
+ srcMsgCreateTime,
+ renderType,
+ title: outTitle,
+ recordItem,
+ url: outUrl,
+ content
+ }
+ })
+
+ return {
+ info: { isChatRoom, title, desc },
+ items: parsed
+ }
+ }
+
+ const initChatHistoryModal = () => {
+ const modal = document.getElementById('chatHistoryModal')
+ const titleEl = document.getElementById('chatHistoryModalTitle')
+ const closeBtn = document.getElementById('chatHistoryModalClose')
+ const emptyEl = document.getElementById('chatHistoryModalEmpty')
+ const listEl = document.getElementById('chatHistoryModalList')
+ if (!modal || !titleEl || !closeBtn || !emptyEl || !listEl) return
+
+ const mediaIndex = readMediaIndex()
+ let historyStack = []
+ let currentState = null
+ let backBtn = null
+
+ const updateBackVisibility = () => {
+ if (!backBtn) return
+ const show = Array.isArray(historyStack) && historyStack.length > 0
+ try { backBtn.classList.toggle('hidden', !show) } catch {}
+ }
+
+ // Add a back button next to the title (created at runtime to avoid changing the HTML template).
+ try {
+ const header = titleEl.parentElement
+ if (header) {
+ const wrap = document.createElement('div')
+ wrap.className = 'flex items-center gap-2 min-w-0'
+
+ backBtn = document.createElement('button')
+ backBtn.type = 'button'
+ backBtn.className = 'p-2 rounded hover:bg-black/5 flex-shrink-0 hidden'
+ try { backBtn.setAttribute('aria-label', '返回') } catch {}
+ try { backBtn.setAttribute('title', '返回') } catch {}
+ backBtn.innerHTML = ' '
+
+ header.insertBefore(wrap, titleEl)
+ wrap.appendChild(backBtn)
+ wrap.appendChild(titleEl)
+ }
+ } catch {}
+
+ const close = () => {
+ try { modal.classList.add('hidden') } catch {}
+ try { modal.style.display = 'none' } catch {}
+ try { modal.setAttribute('aria-hidden', 'true') } catch {}
+ try { document.body.style.overflow = '' } catch {}
+ try { titleEl.textContent = '聊天记录' } catch {}
+ try { listEl.textContent = '' } catch {}
+ try { emptyEl.style.display = '' } catch {}
+ historyStack = []
+ currentState = null
+ updateBackVisibility()
+ }
+
+ const buildChatHistoryState = (payload) => {
+ const title = String(payload?.title || '聊天记录').trim() || '聊天记录'
+ const xml = String(payload?.recordItem || '').trim()
+ const parsed = parseChatHistoryRecord(xml)
+ const info = (parsed && parsed.info) ? parsed.info : { isChatRoom: false }
+ let records = (parsed && Array.isArray(parsed.items)) ? parsed.items : []
+
+ if (!records.length) {
+ const lines = Array.isArray(payload?.fallbackLines)
+ ? payload.fallbackLines
+ : String(payload?.content || '').trim().split(/\r?\n/).map((x) => String(x || '').trim()).filter(Boolean)
+ records = lines.map((line, idx) => ({ id: String(idx), renderType: 'text', content: line, sourcename: '', sourcetime: '' }))
+ }
+
+ return { title, info, records }
+ }
+
+ const renderRecordRow = (rec, info) => {
+ const row = document.createElement('div')
+ row.className = 'px-4 py-3 flex gap-3 border-b border-gray-100'
+
+ const avatarWrap = document.createElement('div')
+ avatarWrap.className = 'w-9 h-9 rounded-md overflow-hidden bg-gray-200 flex-shrink-0'
+ const name0 = String(rec?.sourcename || '').trim() || '?'
+ const avatarUrlRaw = normalizeChatHistoryUrl(rec?.sourceheadurl)
+ const avatarLocal = (mediaIndex && mediaIndex.remote && mediaIndex.remote[avatarUrlRaw]) ? String(mediaIndex.remote[avatarUrlRaw] || '') : ''
+ const avatarUrlLower = String(avatarUrlRaw || '').trim().toLowerCase()
+ const avatarUrl = avatarLocal || ((avatarUrlLower.startsWith('http://') || avatarUrlLower.startsWith('https://')) ? avatarUrlRaw : '')
+ if (avatarUrl) {
+ const img = document.createElement('img')
+ img.src = avatarUrl
+ img.alt = '头像'
+ img.className = 'w-full h-full object-cover'
+ try { img.referrerPolicy = 'no-referrer' } catch {}
+ img.onerror = () => {
+ try { avatarWrap.textContent = '' } catch {}
+ const fb = document.createElement('div')
+ fb.className = 'w-full h-full flex items-center justify-center text-xs font-bold text-gray-600'
+ fb.textContent = String(name0.charAt(0) || '?')
+ avatarWrap.appendChild(fb)
+ }
+ avatarWrap.appendChild(img)
+ } else {
+ const fb = document.createElement('div')
+ fb.className = 'w-full h-full flex items-center justify-center text-xs font-bold text-gray-600'
+ fb.textContent = String(name0.charAt(0) || '?')
+ avatarWrap.appendChild(fb)
+ }
+
+ const main = document.createElement('div')
+ main.className = 'min-w-0 flex-1'
+
+ const header = document.createElement('div')
+ header.className = 'flex items-start gap-2'
+
+ const headerLeft = document.createElement('div')
+ headerLeft.className = 'min-w-0 flex-1'
+ const senderName = String(rec?.sourcename || '').trim()
+ if (info && info.isChatRoom && senderName) {
+ const sn = document.createElement('div')
+ sn.className = 'text-xs text-gray-500 leading-none truncate mb-1'
+ sn.textContent = senderName
+ headerLeft.appendChild(sn)
+ }
+
+ const headerRight = document.createElement('div')
+ headerRight.className = 'text-xs text-gray-400 flex-shrink-0 leading-none'
+ const timeText = String(rec?.sourcetime || '').trim()
+ headerRight.textContent = timeText
+
+ header.appendChild(headerLeft)
+ if (timeText) header.appendChild(headerRight)
+
+ const body = document.createElement('div')
+ body.className = 'mt-1'
+
+ const rt = String(rec?.renderType || 'text')
+ const content = String(rec?.content || '').trim()
+ const serverId = String(rec?.fromnewmsgid || '').trim()
+ const serverMd5 = resolveServerMd5(mediaIndex, serverId)
+
+ if (rt === 'chatHistory') {
+ const card = document.createElement('div')
+ card.className = 'wechat-chat-history-card wechat-special-card msg-radius'
+
+ const chBody = document.createElement('div')
+ chBody.className = 'wechat-chat-history-body'
+
+ const chTitle = document.createElement('div')
+ chTitle.className = 'wechat-chat-history-title'
+ chTitle.textContent = String(rec?.title || '聊天记录')
+ chBody.appendChild(chTitle)
+
+ const raw = String(rec?.content || '').trim()
+ const lines = raw ? raw.split(/\r?\n/).map((x) => String(x || '').trim()).filter(Boolean).slice(0, 4) : []
+ if (lines.length) {
+ const preview = document.createElement('div')
+ preview.className = 'wechat-chat-history-preview'
+ for (const line of lines) {
+ const el = document.createElement('div')
+ el.className = 'wechat-chat-history-line'
+ el.textContent = line
+ preview.appendChild(el)
+ }
+ chBody.appendChild(preview)
+ }
+
+ card.appendChild(chBody)
+
+ const bottom = document.createElement('div')
+ bottom.className = 'wechat-chat-history-bottom'
+ const label = document.createElement('span')
+ label.textContent = '聊天记录'
+ bottom.appendChild(label)
+ card.appendChild(bottom)
+
+ const nestedXml = String(rec?.recordItem || '').trim()
+ if (nestedXml) {
+ card.classList.add('cursor-pointer')
+ card.addEventListener('click', (ev) => {
+ try { ev.preventDefault() } catch {}
+ try { ev.stopPropagation() } catch {}
+ openNestedChatHistory(rec)
+ })
+ }
+
+ body.appendChild(card)
+ } else if (rt === 'link') {
+ const href = normalizeChatHistoryUrl(rec?.url) || normalizeChatHistoryUrl(rec?.externurl)
+ const heading = String(rec?.title || '').trim() || content || href || '链接'
+ const desc = String(rec?.content || '').trim()
+
+ const thumbMd5 = pickFirstMd5(rec?.fullmd5, rec?.thumbfullmd5, rec?.cdnthumbmd5, rec?.md5, rec?.id)
+ let previewUrl = resolveMd5Any(mediaIndex, thumbMd5)
+ if (!previewUrl && serverMd5) previewUrl = resolveMd5Any(mediaIndex, serverMd5)
+ if (!previewUrl) previewUrl = resolveRemoteAny(mediaIndex, rec?.externurl, rec?.cdnurlstring, rec?.encrypturlstring)
+
+ const card = document.createElement(href ? 'a' : 'div')
+ card.className = 'wechat-link-card wechat-special-card msg-radius cursor-pointer'
+ if (href) {
+ card.href = href
+ card.target = '_blank'
+ card.rel = 'noreferrer noopener'
+ }
+ try { card.style.textDecoration = 'none' } catch {}
+ try { card.style.outline = 'none' } catch {}
+
+ const linkContent = document.createElement('div')
+ linkContent.className = 'wechat-link-content'
+
+ const linkInfo = document.createElement('div')
+ linkInfo.className = 'wechat-link-info'
+ const titleEl = document.createElement('div')
+ titleEl.className = 'wechat-link-title'
+ titleEl.textContent = heading
+ linkInfo.appendChild(titleEl)
+ if (desc) {
+ const descEl = document.createElement('div')
+ descEl.className = 'wechat-link-desc'
+ descEl.textContent = desc
+ linkInfo.appendChild(descEl)
+ }
+ linkContent.appendChild(linkInfo)
+
+ if (previewUrl) {
+ const thumb = document.createElement('div')
+ thumb.className = 'wechat-link-thumb'
+ const img = document.createElement('img')
+ img.src = previewUrl
+ img.alt = heading || '链接预览'
+ img.className = 'wechat-link-thumb-img'
+ try { img.referrerPolicy = 'no-referrer' } catch {}
+ thumb.appendChild(img)
+ linkContent.appendChild(thumb)
+ }
+
+ card.appendChild(linkContent)
+
+ const fromRow = document.createElement('div')
+ fromRow.className = 'wechat-link-from'
+ const fromText = (() => {
+ const f0 = String(rec?.from || '').trim()
+ if (f0) return f0
+ try { return href ? (new URL(href).hostname || '') : '' } catch { return '' }
+ })()
+ const fromAvatarText = fromText ? (Array.from(fromText)[0] || '') : ''
+ const fromAvatar = document.createElement('div')
+ fromAvatar.className = 'wechat-link-from-avatar'
+ fromAvatar.textContent = fromAvatarText || '\u200B'
+ const fromName = document.createElement('div')
+ fromName.className = 'wechat-link-from-name'
+ fromName.textContent = fromText || '\u200B'
+ fromRow.appendChild(fromAvatar)
+ fromRow.appendChild(fromName)
+ card.appendChild(fromRow)
+
+ body.appendChild(card)
+ } else if (rt === 'video') {
+ const videoMd5 = pickFirstMd5(rec?.fullmd5, rec?.md5, rec?.id)
+ const thumbMd5 = pickFirstMd5(rec?.thumbfullmd5, rec?.cdnthumbmd5) || videoMd5
+ let videoUrl = resolveMd5Any(mediaIndex, videoMd5)
+ if (!videoUrl && serverMd5) videoUrl = resolveMd5Any(mediaIndex, serverMd5)
+ if (!videoUrl) videoUrl = resolveRemoteAny(mediaIndex, rec?.externurl, rec?.cdnurlstring, rec?.encrypturlstring)
+
+ let thumbUrl = resolveMd5Any(mediaIndex, thumbMd5)
+ if (!thumbUrl && serverMd5) thumbUrl = resolveMd5Any(mediaIndex, serverMd5)
+ if (!thumbUrl) thumbUrl = resolveRemoteAny(mediaIndex, rec?.externurl, rec?.cdnurlstring, rec?.encrypturlstring)
+
+ const wrap = document.createElement('div')
+ wrap.className = 'msg-radius overflow-hidden relative bg-black/5 inline-block'
+
+ if (thumbUrl) {
+ const img = document.createElement('img')
+ img.src = thumbUrl
+ img.alt = '视频'
+ img.className = 'block w-[220px] max-w-[260px] h-auto max-h-[260px] object-cover'
+ wrap.appendChild(img)
+ } else {
+ const t = document.createElement('div')
+ t.className = 'px-3 py-2 text-sm text-gray-700'
+ t.textContent = content || '[视频]'
+ wrap.appendChild(t)
+ }
+
+ if (thumbUrl) {
+ const overlay = document.createElement(videoUrl ? 'a' : 'div')
+ if (videoUrl) {
+ overlay.href = videoUrl
+ overlay.target = '_blank'
+ overlay.rel = 'noreferrer noopener'
+ }
+ overlay.className = 'absolute inset-0 flex items-center justify-center'
+ const btn = document.createElement('div')
+ btn.className = 'w-12 h-12 rounded-full bg-black/45 flex items-center justify-center'
+ btn.innerHTML = ' '
+ overlay.appendChild(btn)
+ wrap.appendChild(overlay)
+ }
+
+ body.appendChild(wrap)
+ } else if (rt === 'image') {
+ const imageMd5 = pickFirstMd5(rec?.fullmd5, rec?.thumbfullmd5, rec?.cdnthumbmd5, rec?.md5, rec?.id)
+ let imgUrl = resolveMd5Any(mediaIndex, imageMd5)
+ if (!imgUrl && serverMd5) imgUrl = resolveMd5Any(mediaIndex, serverMd5)
+ if (!imgUrl) imgUrl = resolveRemoteAny(mediaIndex, rec?.externurl, rec?.cdnurlstring, rec?.encrypturlstring)
+ if (imgUrl) {
+ const outer = document.createElement('div')
+ outer.className = 'msg-radius overflow-hidden cursor-pointer inline-block'
+ const a = document.createElement('a')
+ a.href = imgUrl
+ a.target = '_blank'
+ a.rel = 'noreferrer noopener'
+ const img = document.createElement('img')
+ img.src = imgUrl
+ img.alt = '图片'
+ img.className = 'max-w-[240px] max-h-[240px] object-cover'
+ a.appendChild(img)
+ outer.appendChild(a)
+ body.appendChild(outer)
+ } else {
+ const t = document.createElement('div')
+ t.className = 'px-3 py-2 text-sm text-gray-700 whitespace-pre-wrap break-words'
+ t.textContent = content || '[图片]'
+ body.appendChild(t)
+ }
+ } else if (rt === 'emoji') {
+ const emojiMd5 = pickFirstMd5(rec?.md5, rec?.fullmd5, rec?.thumbfullmd5, rec?.cdnthumbmd5, rec?.id)
+ let emojiUrl = resolveMd5Any(mediaIndex, emojiMd5)
+ if (!emojiUrl && serverMd5) emojiUrl = resolveMd5Any(mediaIndex, serverMd5)
+ if (!emojiUrl) emojiUrl = resolveRemoteAny(mediaIndex, rec?.externurl, rec?.cdnurlstring, rec?.encrypturlstring)
+ if (emojiUrl) {
+ const img = document.createElement('img')
+ img.src = emojiUrl
+ img.alt = '表情'
+ img.className = 'w-24 h-24 object-contain'
+ body.appendChild(img)
+ } else {
+ const t = document.createElement('div')
+ t.className = 'px-3 py-2 text-sm text-gray-700 whitespace-pre-wrap break-words'
+ t.textContent = content || '[表情]'
+ body.appendChild(t)
+ }
+ } else {
+ const t = document.createElement('div')
+ t.className = 'px-3 py-2 text-sm text-gray-700 whitespace-pre-wrap break-words'
+ t.textContent = content || ''
+ body.appendChild(t)
+ }
+
+ main.appendChild(header)
+ main.appendChild(body)
+
+ row.appendChild(avatarWrap)
+ row.appendChild(main)
+ return row
+ }
+
+ const applyChatHistoryState = (state) => {
+ currentState = state
+ const title = String(state?.title || '聊天记录').trim() || '聊天记录'
+ const info = state?.info || { isChatRoom: false }
+ const records = Array.isArray(state?.records) ? state.records : []
+
+ try { titleEl.textContent = title } catch {}
+ try { listEl.textContent = '' } catch {}
+
+ if (!records.length) {
+ try { emptyEl.style.display = '' } catch {}
+ } else {
+ try { emptyEl.style.display = 'none' } catch {}
+ for (const rec of records) {
+ try {
+ listEl.appendChild(renderRecordRow(rec, info))
+ } catch {}
+ }
+ }
+
+ updateBackVisibility()
+ }
+
+ const openNestedChatHistory = (rec) => {
+ const xml = String(rec?.recordItem || '').trim()
+ if (!xml) return
+ if (currentState) {
+ historyStack = [...historyStack, currentState]
+ }
+ const state = buildChatHistoryState({
+ title: String(rec?.title || '聊天记录'),
+ recordItem: xml,
+ content: String(rec?.content || ''),
+ })
+ applyChatHistoryState(state)
+ }
+
+ if (backBtn) {
+ backBtn.addEventListener('click', (ev) => {
+ try { ev.preventDefault() } catch {}
+ if (!Array.isArray(historyStack) || !historyStack.length) return
+ const prev = historyStack[historyStack.length - 1]
+ historyStack = historyStack.slice(0, -1)
+ applyChatHistoryState(prev)
+ })
+ }
+
+ const openFromCard = (card) => {
+ const title = String(card?.getAttribute('data-title') || '聊天记录').trim() || '聊天记录'
+ const b64 = String(card?.getAttribute('data-record-item-b64') || '').trim()
+ const xml = decodeBase64Utf8(b64)
+ const lines = Array.from(card.querySelectorAll('.wechat-chat-history-line') || [])
+ .map((el) => String(el?.textContent || '').trim())
+ .filter(Boolean)
+
+ historyStack = []
+ const state = buildChatHistoryState({ title, recordItem: xml, fallbackLines: lines })
+ applyChatHistoryState(state)
+
+ try { modal.classList.remove('hidden') } catch {}
+ try { modal.style.display = 'flex' } catch {}
+ try { modal.setAttribute('aria-hidden', 'false') } catch {}
+ try { document.body.style.overflow = 'hidden' } catch {}
+ }
+
+ closeBtn.addEventListener('click', (ev) => {
+ try { ev.preventDefault() } catch {}
+ close()
+ })
+ modal.addEventListener('click', (ev) => {
+ const t = ev && ev.target
+ if (t === modal) close()
+ })
+
+ document.addEventListener('keydown', (ev) => {
+ const key = String(ev?.key || '')
+ if (key === 'Escape' && !modal.classList.contains('hidden')) close()
+
+ if ((key === 'Enter' || key === ' ') && modal.classList.contains('hidden')) {
+ const target = ev && ev.target
+ const card = target && target.closest ? target.closest('[data-wce-chat-history=\"1\"]') : null
+ if (!card) return
+ try { ev.preventDefault() } catch {}
+ openFromCard(card)
+ }
+ }, true)
+
+ document.addEventListener('click', (ev) => {
+ const target = ev && ev.target
+ const card = target && target.closest ? target.closest('[data-wce-chat-history=\"1\"]') : null
+ if (!card) return
+ try { ev.preventDefault() } catch {}
+ openFromCard(card)
+ }, true)
+ }
+
+ const initChatHistoryFloatingWindows = () => {
+ const mediaIndex = readMediaIndex()
+ let zIndex = 1000
+ let cascade = 0
+ let idSeed = 0
+
+ const clampNumber = (value, min, max) => {
+ const n = Number(value)
+ if (!Number.isFinite(n)) return min
+ return Math.min(max, Math.max(min, n))
+ }
+
+ const getViewport = () => {
+ const w = Math.max(320, window.innerWidth || 0)
+ const h = Math.max(240, window.innerHeight || 0)
+ return { w, h }
+ }
+
+ const getPoint = (ev) => {
+ try {
+ return (ev && ev.touches && ev.touches[0]) ? ev.touches[0] : ev
+ } catch {
+ return ev
+ }
+ }
+
+ const buildChatHistoryState = (payload) => {
+ const title = String(payload?.title || '聊天记录').trim() || '聊天记录'
+ const xml = String(payload?.recordItem || '').trim()
+ const parsed = parseChatHistoryRecord(xml)
+ const info = (parsed && parsed.info) ? parsed.info : { isChatRoom: false }
+ let records = (parsed && Array.isArray(parsed.items)) ? parsed.items : []
+
+ if (!records.length) {
+ const lines = Array.isArray(payload?.fallbackLines)
+ ? payload.fallbackLines
+ : String(payload?.content || '').trim().split(/\r?\n/).map((x) => String(x || '').trim()).filter(Boolean)
+ records = lines.map((line, idx) => ({ id: String(idx), renderType: 'text', content: line, sourcename: '', sourcetime: '' }))
+ }
+
+ return { title, info, records }
+ }
+
+ const renderRecordRow = (rec, info, onOpenNested) => {
+ const row = document.createElement('div')
+ row.className = 'px-4 py-3 flex gap-3 border-b border-gray-100 bg-[#f7f7f7]'
+
+ const avatarWrap = document.createElement('div')
+ avatarWrap.className = 'w-9 h-9 rounded-md overflow-hidden bg-gray-200 flex-shrink-0'
+ const name0 = String(rec?.sourcename || '').trim() || '?'
+ const avatarUrlRaw = normalizeChatHistoryUrl(rec?.sourceheadurl)
+ const avatarLocal = (mediaIndex && mediaIndex.remote && mediaIndex.remote[avatarUrlRaw]) ? String(mediaIndex.remote[avatarUrlRaw] || '') : ''
+ const avatarUrlLower = String(avatarUrlRaw || '').trim().toLowerCase()
+ const avatarUrl = avatarLocal || ((avatarUrlLower.startsWith('http://') || avatarUrlLower.startsWith('https://')) ? avatarUrlRaw : '')
+ if (avatarUrl) {
+ const img = document.createElement('img')
+ img.src = avatarUrl
+ img.alt = '头像'
+ img.className = 'w-full h-full object-cover'
+ try { img.referrerPolicy = 'no-referrer' } catch {}
+ img.onerror = () => {
+ try { avatarWrap.textContent = '' } catch {}
+ const fb = document.createElement('div')
+ fb.className = 'w-full h-full flex items-center justify-center text-xs font-bold text-gray-600'
+ fb.textContent = String(name0.charAt(0) || '?')
+ avatarWrap.appendChild(fb)
+ }
+ avatarWrap.appendChild(img)
+ } else {
+ const fb = document.createElement('div')
+ fb.className = 'w-full h-full flex items-center justify-center text-xs font-bold text-gray-600'
+ fb.textContent = String(name0.charAt(0) || '?')
+ avatarWrap.appendChild(fb)
+ }
+
+ const main = document.createElement('div')
+ main.className = 'min-w-0 flex-1'
+
+ const header = document.createElement('div')
+ header.className = 'flex items-start gap-2'
+
+ const headerLeft = document.createElement('div')
+ headerLeft.className = 'min-w-0 flex-1'
+ const senderName = String(rec?.sourcename || '').trim()
+ if (info && info.isChatRoom && senderName) {
+ const sn = document.createElement('div')
+ sn.className = 'text-xs text-gray-500 leading-none truncate mb-1'
+ sn.textContent = senderName
+ headerLeft.appendChild(sn)
+ }
+
+ const headerRight = document.createElement('div')
+ headerRight.className = 'text-xs text-gray-400 flex-shrink-0 leading-none'
+ const timeText = String(rec?.sourcetime || '').trim()
+ headerRight.textContent = timeText
+
+ header.appendChild(headerLeft)
+ if (timeText) header.appendChild(headerRight)
+
+ const body = document.createElement('div')
+ body.className = 'mt-1'
+
+ const rt = String(rec?.renderType || 'text')
+ const content = String(rec?.content || '').trim()
+ const serverId = String(rec?.fromnewmsgid || '').trim()
+ const serverMd5 = resolveServerMd5(mediaIndex, serverId)
+
+ if (rt === 'chatHistory') {
+ const card = document.createElement('div')
+ card.className = 'wechat-chat-history-card wechat-special-card msg-radius'
+
+ const chBody = document.createElement('div')
+ chBody.className = 'wechat-chat-history-body'
+
+ const chTitle = document.createElement('div')
+ chTitle.className = 'wechat-chat-history-title'
+ chTitle.textContent = String(rec?.title || '聊天记录')
+ chBody.appendChild(chTitle)
+
+ const raw = String(rec?.content || '').trim()
+ const lines = raw ? raw.split(/\r?\n/).map((x) => String(x || '').trim()).filter(Boolean).slice(0, 4) : []
+ if (lines.length) {
+ const preview = document.createElement('div')
+ preview.className = 'wechat-chat-history-preview'
+ for (const line of lines) {
+ const el = document.createElement('div')
+ el.className = 'wechat-chat-history-line'
+ el.textContent = line
+ preview.appendChild(el)
+ }
+ chBody.appendChild(preview)
+ }
+
+ card.appendChild(chBody)
+
+ const bottom = document.createElement('div')
+ bottom.className = 'wechat-chat-history-bottom'
+ const label = document.createElement('span')
+ label.textContent = '聊天记录'
+ bottom.appendChild(label)
+ card.appendChild(bottom)
+
+ const nestedXml = String(rec?.recordItem || '').trim()
+ if (nestedXml) {
+ card.classList.add('cursor-pointer')
+ card.addEventListener('click', (ev) => {
+ try { ev.preventDefault() } catch {}
+ try { ev.stopPropagation() } catch {}
+ if (typeof onOpenNested === 'function') onOpenNested(rec)
+ })
+ }
+
+ body.appendChild(card)
+ } else if (rt === 'link') {
+ const href = normalizeChatHistoryUrl(rec?.url) || normalizeChatHistoryUrl(rec?.externurl)
+ const heading = String(rec?.title || '').trim() || content || href || '链接'
+ const desc = String(rec?.content || '').trim()
+
+ const thumbMd5 = pickFirstMd5(rec?.fullmd5, rec?.thumbfullmd5, rec?.cdnthumbmd5, rec?.md5, rec?.id)
+ let previewUrl = resolveMd5Any(mediaIndex, thumbMd5)
+ if (!previewUrl && serverMd5) previewUrl = resolveMd5Any(mediaIndex, serverMd5)
+ if (!previewUrl) previewUrl = resolveRemoteAny(mediaIndex, rec?.externurl, rec?.cdnurlstring, rec?.encrypturlstring)
+
+ const card = document.createElement(href ? 'a' : 'div')
+ card.className = 'wechat-link-card wechat-special-card msg-radius cursor-pointer'
+ if (href) {
+ card.href = href
+ card.target = '_blank'
+ card.rel = 'noreferrer noopener'
+ }
+ try { card.style.textDecoration = 'none' } catch {}
+ try { card.style.outline = 'none' } catch {}
+
+ const linkContent = document.createElement('div')
+ linkContent.className = 'wechat-link-content'
+
+ const linkInfo = document.createElement('div')
+ linkInfo.className = 'wechat-link-info'
+ const titleEl = document.createElement('div')
+ titleEl.className = 'wechat-link-title'
+ titleEl.textContent = heading
+ linkInfo.appendChild(titleEl)
+ if (desc) {
+ const descEl = document.createElement('div')
+ descEl.className = 'wechat-link-desc'
+ descEl.textContent = desc
+ linkInfo.appendChild(descEl)
+ }
+ linkContent.appendChild(linkInfo)
+
+ if (previewUrl) {
+ const thumb = document.createElement('div')
+ thumb.className = 'wechat-link-thumb'
+ const img = document.createElement('img')
+ img.src = previewUrl
+ img.alt = heading || '链接预览'
+ img.className = 'wechat-link-thumb-img'
+ try { img.referrerPolicy = 'no-referrer' } catch {}
+ thumb.appendChild(img)
+ linkContent.appendChild(thumb)
+ }
+
+ card.appendChild(linkContent)
+
+ const fromRow = document.createElement('div')
+ fromRow.className = 'wechat-link-from'
+ const fromAvatar = document.createElement('div')
+ fromAvatar.className = 'wechat-link-from-avatar'
+
+ const fromUrlRaw = normalizeChatHistoryUrl(rec?.sourceheadurl)
+ const fromLocal = (mediaIndex && mediaIndex.remote && mediaIndex.remote[fromUrlRaw]) ? String(mediaIndex.remote[fromUrlRaw] || '') : ''
+ const fromLower = String(fromUrlRaw || '').trim().toLowerCase()
+ const fromUrl = fromLocal || ((fromLower.startsWith('http://') || fromLower.startsWith('https://')) ? fromUrlRaw : '')
+ const fromText = String(rec?.sourcename || '').trim()
+ if (fromUrl) {
+ const img = document.createElement('img')
+ img.src = fromUrl
+ img.alt = ''
+ img.className = 'wechat-link-from-avatar-img'
+ try { img.referrerPolicy = 'no-referrer' } catch {}
+ img.onerror = () => {
+ try { fromAvatar.textContent = '' } catch {}
+ const span = document.createElement('span')
+ span.textContent = String(fromText ? fromText.charAt(0) : '\u200B')
+ fromAvatar.appendChild(span)
+ }
+ fromAvatar.appendChild(img)
+ } else {
+ const span = document.createElement('span')
+ span.textContent = String(fromText ? fromText.charAt(0) : '\u200B')
+ fromAvatar.appendChild(span)
+ }
+ const fromName = document.createElement('div')
+ fromName.className = 'wechat-link-from-name'
+ fromName.textContent = fromText || '\u200B'
+ fromRow.appendChild(fromAvatar)
+ fromRow.appendChild(fromName)
+ card.appendChild(fromRow)
+
+ body.appendChild(card)
+ } else if (rt === 'video') {
+ const videoMd5 = pickFirstMd5(rec?.fullmd5, rec?.md5, rec?.id)
+ const thumbMd5 = pickFirstMd5(rec?.thumbfullmd5, rec?.cdnthumbmd5) || videoMd5
+ let videoUrl = resolveMd5Any(mediaIndex, videoMd5)
+ if (!videoUrl && serverMd5) videoUrl = resolveMd5Any(mediaIndex, serverMd5)
+ if (!videoUrl) videoUrl = resolveRemoteAny(mediaIndex, rec?.externurl, rec?.cdnurlstring, rec?.encrypturlstring)
+
+ let thumbUrl = resolveMd5Any(mediaIndex, thumbMd5)
+ if (!thumbUrl && serverMd5) thumbUrl = resolveMd5Any(mediaIndex, serverMd5)
+ if (!thumbUrl) thumbUrl = resolveRemoteAny(mediaIndex, rec?.externurl, rec?.cdnurlstring, rec?.encrypturlstring)
+
+ const wrap = document.createElement('div')
+ wrap.className = 'msg-radius overflow-hidden relative bg-black/5 inline-block'
+
+ if (thumbUrl) {
+ const img = document.createElement('img')
+ img.src = thumbUrl
+ img.alt = '视频'
+ img.className = 'block w-[220px] max-w-[260px] h-auto max-h-[260px] object-cover'
+ wrap.appendChild(img)
+ } else {
+ const t = document.createElement('div')
+ t.className = 'px-3 py-2 text-sm text-gray-700'
+ t.textContent = content || '[视频]'
+ wrap.appendChild(t)
+ }
+
+ if (thumbUrl) {
+ const overlay = document.createElement(videoUrl ? 'a' : 'div')
+ if (videoUrl) {
+ overlay.href = videoUrl
+ overlay.target = '_blank'
+ overlay.rel = 'noreferrer noopener'
+ }
+ overlay.className = 'absolute inset-0 flex items-center justify-center'
+ const btn = document.createElement('div')
+ btn.className = 'w-12 h-12 rounded-full bg-black/45 flex items-center justify-center'
+ btn.innerHTML = ' '
+ overlay.appendChild(btn)
+ wrap.appendChild(overlay)
+ }
+
+ body.appendChild(wrap)
+ } else if (rt === 'image') {
+ const imageMd5 = pickFirstMd5(rec?.fullmd5, rec?.thumbfullmd5, rec?.cdnthumbmd5, rec?.md5, rec?.id)
+ let imgUrl = resolveMd5Any(mediaIndex, imageMd5)
+ if (!imgUrl && serverMd5) imgUrl = resolveMd5Any(mediaIndex, serverMd5)
+ if (!imgUrl) imgUrl = resolveRemoteAny(mediaIndex, rec?.externurl, rec?.cdnurlstring, rec?.encrypturlstring)
+ if (imgUrl) {
+ const outer = document.createElement('div')
+ outer.className = 'msg-radius overflow-hidden cursor-pointer inline-block'
+ const a = document.createElement('a')
+ a.href = imgUrl
+ a.target = '_blank'
+ a.rel = 'noreferrer noopener'
+ const img = document.createElement('img')
+ img.src = imgUrl
+ img.alt = '图片'
+ img.className = 'max-w-[240px] max-h-[240px] object-cover'
+ a.appendChild(img)
+ outer.appendChild(a)
+ body.appendChild(outer)
+ } else {
+ const t = document.createElement('div')
+ t.className = 'px-3 py-2 text-sm text-gray-700 whitespace-pre-wrap break-words'
+ t.textContent = content || '[图片]'
+ body.appendChild(t)
+ }
+ } else if (rt === 'emoji') {
+ const emojiMd5 = pickFirstMd5(rec?.md5, rec?.fullmd5, rec?.thumbfullmd5, rec?.cdnthumbmd5, rec?.id)
+ let emojiUrl = resolveMd5Any(mediaIndex, emojiMd5)
+ if (!emojiUrl && serverMd5) emojiUrl = resolveMd5Any(mediaIndex, serverMd5)
+ if (!emojiUrl) emojiUrl = resolveRemoteAny(mediaIndex, rec?.externurl, rec?.cdnurlstring, rec?.encrypturlstring)
+ if (emojiUrl) {
+ const img = document.createElement('img')
+ img.src = emojiUrl
+ img.alt = '表情'
+ img.className = 'w-24 h-24 object-contain'
+ body.appendChild(img)
+ } else {
+ const t = document.createElement('div')
+ t.className = 'px-3 py-2 text-sm text-gray-700 whitespace-pre-wrap break-words'
+ t.textContent = content || '[表情]'
+ body.appendChild(t)
+ }
+ } else {
+ const t = document.createElement('div')
+ t.className = 'px-3 py-2 text-sm text-gray-700 whitespace-pre-wrap break-words'
+ t.textContent = content || ''
+ body.appendChild(t)
+ }
+
+ main.appendChild(header)
+ main.appendChild(body)
+
+ row.appendChild(avatarWrap)
+ row.appendChild(main)
+ return row
+ }
+
+ const focusWindow = (wrap) => {
+ zIndex += 1
+ try { wrap.style.zIndex = String(zIndex) } catch {}
+ }
+
+ const openChatHistoryWindow = (payload, opts) => {
+ const state = buildChatHistoryState(payload || {})
+ const info = state.info || { isChatRoom: false }
+ const records = Array.isArray(state.records) ? state.records : []
+
+ const vp = getViewport()
+ const width = Math.min(560, Math.max(320, Math.floor(vp.w * 0.92)))
+ const height = Math.min(560, Math.max(240, Math.floor(vp.h * 0.8)))
+
+ let x = Math.max(8, Math.floor((vp.w - width) / 2))
+ let y = Math.max(8, Math.floor((vp.h - height) / 2))
+
+ const spawnFrom = opts && opts.spawnFrom
+ if (spawnFrom) {
+ x = Number(spawnFrom.x || x) + 24
+ y = Number(spawnFrom.y || y) + 24
+ } else {
+ x += cascade
+ y += cascade
+ cascade = (cascade + 24) % 120
+ }
+
+ x = clampNumber(x, 8, Math.max(8, vp.w - width - 8))
+ y = clampNumber(y, 8, Math.max(8, vp.h - height - 8))
+
+ const win = { id: String(++idSeed), x, y, width, height }
+
+ const wrap = document.createElement('div')
+ wrap.className = 'fixed'
+ wrap.style.left = `${win.x}px`
+ wrap.style.top = `${win.y}px`
+ wrap.style.zIndex = String(++zIndex)
+
+ const box = document.createElement('div')
+ box.className = 'bg-[#f7f7f7] rounded-xl shadow-xl overflow-hidden border border-gray-200 flex flex-col'
+ box.style.width = `${win.width}px`
+ box.style.height = `${win.height}px`
+ wrap.appendChild(box)
+
+ const header = document.createElement('div')
+ header.className = 'px-3 py-2 bg-[#f7f7f7] border-b border-gray-200 flex items-center justify-between select-none cursor-move'
+ box.appendChild(header)
+
+ const titleEl = document.createElement('div')
+ titleEl.className = 'text-sm text-[#161616] truncate min-w-0'
+ titleEl.textContent = String(state.title || '聊天记录')
+ header.appendChild(titleEl)
+
+ const closeBtn = document.createElement('button')
+ closeBtn.type = 'button'
+ closeBtn.className = 'p-2 rounded hover:bg-black/5 flex-shrink-0'
+ try { closeBtn.setAttribute('aria-label', '关闭') } catch {}
+ try { closeBtn.setAttribute('title', '关闭') } catch {}
+ closeBtn.innerHTML = ' '
+ header.appendChild(closeBtn)
+
+ const body = document.createElement('div')
+ body.className = 'flex-1 overflow-auto bg-[#f7f7f7]'
+ box.appendChild(body)
+
+ if (!records.length) {
+ const empty = document.createElement('div')
+ empty.className = 'text-sm text-gray-500 text-center py-10'
+ empty.textContent = '没有可显示的聊天记录'
+ body.appendChild(empty)
+ } else {
+ const onOpenNested = (rec) => {
+ const xml = String(rec?.recordItem || '').trim()
+ if (!xml) return
+ openChatHistoryWindow({
+ title: String(rec?.title || '聊天记录'),
+ recordItem: xml,
+ content: String(rec?.content || ''),
+ }, { spawnFrom: win })
+ }
+ for (const rec of records) {
+ try {
+ body.appendChild(renderRecordRow(rec, info, onOpenNested))
+ } catch {}
+ }
+ }
+
+ const updatePos = () => {
+ try { wrap.style.left = `${win.x}px` } catch {}
+ try { wrap.style.top = `${win.y}px` } catch {}
+ }
+
+ closeBtn.addEventListener('click', (ev) => {
+ try { ev.preventDefault() } catch {}
+ try { ev.stopPropagation() } catch {}
+ try { wrap.remove() } catch {
+ try { if (wrap.parentElement) wrap.parentElement.removeChild(wrap) } catch {}
+ }
+ })
+
+ const startDrag = (ev) => {
+ const t = ev && ev.target
+ if (t && t.closest && t.closest('button')) return
+
+ focusWindow(wrap)
+ const p0 = getPoint(ev)
+ const ox = Number(p0?.clientX || 0) - win.x
+ const oy = Number(p0?.clientY || 0) - win.y
+
+ const onMove = (e2) => {
+ const p = getPoint(e2)
+ if (!p) return
+ try { if (e2 && typeof e2.preventDefault === 'function') e2.preventDefault() } catch {}
+
+ const vp2 = getViewport()
+ const nx = Number(p.clientX || 0) - ox
+ const ny = Number(p.clientY || 0) - oy
+ win.x = clampNumber(nx, 8, Math.max(8, vp2.w - win.width - 8))
+ win.y = clampNumber(ny, 8, Math.max(8, vp2.h - win.height - 8))
+ updatePos()
+ }
+
+ const stop = () => {
+ try { document.removeEventListener('mousemove', onMove) } catch {}
+ try { document.removeEventListener('touchmove', onMove) } catch {}
+ }
+
+ try { document.addEventListener('mousemove', onMove) } catch {}
+ try { document.addEventListener('mouseup', () => stop(), { once: true }) } catch {}
+ try { document.addEventListener('touchmove', onMove, { passive: false }) } catch {}
+ try { document.addEventListener('touchend', () => stop(), { once: true }) } catch {}
+
+ try { ev.preventDefault() } catch {}
+ }
+
+ header.addEventListener('mousedown', startDrag)
+ header.addEventListener('touchstart', startDrag, { passive: false })
+
+ wrap.addEventListener('mousedown', () => focusWindow(wrap))
+ wrap.addEventListener('touchstart', () => focusWindow(wrap), { passive: true })
+
+ try { document.body.appendChild(wrap) } catch {}
+ return win
+ }
+
+ document.addEventListener('keydown', (ev) => {
+ const key = String(ev?.key || '')
+ if (key !== 'Enter' && key !== ' ') return
+ const target = ev && ev.target
+ const card = target && target.closest ? target.closest('[data-wce-chat-history=\"1\"]') : null
+ if (!card) return
+ try { ev.preventDefault() } catch {}
+ const title = String(card?.getAttribute('data-title') || '聊天记录').trim() || '聊天记录'
+ const b64 = String(card?.getAttribute('data-record-item-b64') || '').trim()
+ const xml = decodeBase64Utf8(b64)
+ const lines = Array.from(card.querySelectorAll('.wechat-chat-history-line') || [])
+ .map((el) => String(el?.textContent || '').trim())
+ .filter(Boolean)
+ openChatHistoryWindow({ title, recordItem: xml, fallbackLines: lines })
+ }, true)
+
+ document.addEventListener('click', (ev) => {
+ const target = ev && ev.target
+ const card = target && target.closest ? target.closest('[data-wce-chat-history=\"1\"]') : null
+ if (!card) return
+ try { ev.preventDefault() } catch {}
+ const title = String(card?.getAttribute('data-title') || '聊天记录').trim() || '聊天记录'
+ const b64 = String(card?.getAttribute('data-record-item-b64') || '').trim()
+ const xml = decodeBase64Utf8(b64)
+ const lines = Array.from(card.querySelectorAll('.wechat-chat-history-line') || [])
+ .map((el) => String(el?.textContent || '').trim())
+ .filter(Boolean)
+ openChatHistoryWindow({ title, recordItem: xml, fallbackLines: lines })
+ }, true)
+ }
+
+ document.addEventListener('DOMContentLoaded', () => {
+ hideJsMissingBanner()
+ updateDprVar()
+ try {
+ window.addEventListener('resize', updateDprVar)
+ } catch {}
+
+ initSessionSearch()
+ initVoicePlayback()
+ initChatHistoryFloatingWindows()
+ initPagedMessageLoading()
+
+ const select = document.getElementById('messageTypeFilter')
+ if (select) {
+ select.addEventListener('change', applyMessageTypeFilter)
+ applyMessageTypeFilter()
+ }
+
+ updateSessionMessageCount()
+ scrollToBottom()
+ try {
+ window.addEventListener('load', () => {
+ updateSessionMessageCount()
+ scrollToBottom()
+ setTimeout(scrollToBottom, 60)
+ })
+ } catch {}
+ })
+
+ // Best-effort: defer scripts execute after the DOM is parsed, so we can hide the banner immediately.
+ hideJsMissingBanner()
+})()
+"""
+
+
def _format_ts(ts: int) -> str:
if not ts:
return ""
@@ -99,43 +2346,58 @@ def _normalize_render_type_key(value: Any) -> str:
return lower
-def _render_types_to_local_types(render_types: set[str]) -> Optional[set[int]]:
- rt = {str(x or "").strip() for x in (render_types or set())}
- rt = {x for x in rt if x}
- if not rt:
+def _is_render_type_selected(render_type: Any, selected_render_types: Optional[set[str]]) -> bool:
+ if selected_render_types is None:
+ return True
+ rt = _normalize_render_type_key(render_type) or "text"
+ return rt in selected_render_types
+
+
+def _media_kinds_from_selected_types(selected_render_types: Optional[set[str]]) -> Optional[set[MediaKind]]:
+ if selected_render_types is None:
return None
- out: set[int] = set()
- for k in rt:
- if k == "text":
- out.add(1)
- elif k == "image":
- out.add(3)
- elif k == "voice":
- out.add(34)
- elif k == "video":
- out.update({43, 62})
- elif k == "emoji":
- out.add(47)
- elif k == "voip":
- out.add(50)
- elif k == "system":
- out.update({10000, 266287972401})
- elif k == "quote":
- out.add(244813135921)
- out.add(49) # Some quote messages are embedded as appmsg (local_type=49).
- elif k in {"link", "file", "transfer", "redpacket"}:
- out.add(49)
- else:
- # Unknown type: cannot safely prefilter by local_type.
- return None
+ out: set[MediaKind] = set()
+ # Merged-forward chat history items can contain arbitrary media types; enable packing those
+ # even when users only select `chatHistory` in the renderType filter.
+ if "chathistory" in selected_render_types:
+ out.update({"image", "emoji", "video", "video_thumb", "voice", "file"})
+ if "image" in selected_render_types:
+ out.add("image")
+ if "emoji" in selected_render_types:
+ out.add("emoji")
+ if "video" in selected_render_types:
+ out.add("video")
+ out.add("video_thumb")
+ if "voice" in selected_render_types:
+ out.add("voice")
+ if "file" in selected_render_types:
+ out.add("file")
return out
-def _should_estimate_by_local_type(render_types: set[str]) -> bool:
- # Only estimate counts when every requested type maps 1:1 to local_type.
- # App messages (local_type=49) are heterogeneous and cannot be counted accurately without parsing.
- return not bool(render_types & {"link", "file", "transfer", "redpacket", "quote"})
+def _resolve_effective_media_kinds(
+ *,
+ include_media: bool,
+ media_kinds: list[MediaKind],
+ selected_render_types: Optional[set[str]],
+ privacy_mode: bool,
+) -> tuple[bool, list[MediaKind]]:
+ if privacy_mode or (not include_media):
+ return False, []
+
+ kinds = [k for k in media_kinds if k in {"image", "emoji", "video", "video_thumb", "voice", "file"}]
+ if not kinds:
+ return False, []
+
+ selected_media_kinds = _media_kinds_from_selected_types(selected_render_types)
+ if selected_media_kinds is not None:
+ kinds = [k for k in kinds if k in selected_media_kinds]
+
+ kinds = list(dict.fromkeys(kinds))
+ if not kinds:
+ return False, []
+ return True, kinds
@dataclass
@@ -235,7 +2497,10 @@ def create_job(
include_media: bool,
media_kinds: list[MediaKind],
message_types: list[str],
+ output_dir: Optional[str],
allow_process_key_extract: bool,
+ download_remote_media: bool,
+ html_page_size: int = 1000,
privacy_mode: bool,
file_name: Optional[str],
) -> ExportJob:
@@ -257,7 +2522,10 @@ def create_job(
"includeMedia": bool(include_media),
"mediaKinds": media_kinds,
"messageTypes": list(dict.fromkeys([str(t or "").strip() for t in (message_types or []) if str(t or "").strip()])),
+ "outputDir": str(output_dir or "").strip(),
"allowProcessKeyExtract": bool(allow_process_key_extract),
+ "downloadRemoteMedia": bool(download_remote_media),
+ "htmlPageSize": int(html_page_size) if int(html_page_size or 0) > 0 else int(html_page_size or 0),
"privacyMode": bool(privacy_mode),
"fileName": str(file_name or "").strip(),
},
@@ -299,12 +2567,22 @@ def _run_job(self, job: ExportJob, account_dir: Path) -> None:
opts = dict(job.options or {})
scope: ExportScope = str(opts.get("scope") or "selected") # type: ignore[assignment]
- export_format: ExportFormat = str(opts.get("format") or "json") # type: ignore[assignment]
+ export_format_raw = str(opts.get("format") or "json").strip() or "json"
+ if export_format_raw not in {"json", "txt", "html"}:
+ raise ValueError(f"Unsupported export format: {export_format_raw}")
+ export_format: ExportFormat = export_format_raw # type: ignore[assignment]
include_hidden = bool(opts.get("includeHidden"))
include_official = bool(opts.get("includeOfficial"))
include_media = bool(opts.get("includeMedia"))
allow_process_key_extract = bool(opts.get("allowProcessKeyExtract"))
+ download_remote_media = bool(opts.get("downloadRemoteMedia"))
privacy_mode = bool(opts.get("privacyMode"))
+ try:
+ html_page_size = int(opts.get("htmlPageSize") or 1000)
+ except Exception:
+ html_page_size = 1000
+ if html_page_size < 0:
+ html_page_size = 0
media_kinds_raw = opts.get("mediaKinds") or []
media_kinds: list[MediaKind] = []
@@ -313,10 +2591,6 @@ def _run_job(self, job: ExportJob, account_dir: Path) -> None:
if ks in {"image", "emoji", "video", "video_thumb", "voice", "file"}:
media_kinds.append(ks) # type: ignore[arg-type]
- if privacy_mode:
- include_media = False
- media_kinds = []
-
st = int(opts.get("startTime") or 0) or None
et = int(opts.get("endTime") or 0) or None
@@ -328,9 +2602,15 @@ def _run_job(self, job: ExportJob, account_dir: Path) -> None:
if want:
want_types = want
- local_types = _render_types_to_local_types(want_types) if want_types else None
- can_estimate = (want_types is None) or _should_estimate_by_local_type(want_types)
- estimate_local_types = local_types if (want_types and can_estimate) else None
+ include_media, media_kinds = _resolve_effective_media_kinds(
+ include_media=include_media,
+ media_kinds=media_kinds,
+ selected_render_types=want_types,
+ privacy_mode=privacy_mode,
+ )
+
+ local_types = None
+ estimate_local_types = None
target_usernames = _resolve_export_targets(
account_dir=account_dir,
@@ -342,8 +2622,7 @@ def _run_job(self, job: ExportJob, account_dir: Path) -> None:
if not target_usernames:
raise ValueError("No target conversations to export.")
- exports_root = account_dir.parents[1] / "exports" / account_dir.name
- exports_root.mkdir(parents=True, exist_ok=True)
+ exports_root = _resolve_export_output_dir(account_dir, opts.get("outputDir"))
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
base_name = str(opts.get("fileName") or "").strip()
@@ -438,6 +2717,138 @@ def resolve_display_name(u: str) -> str:
pass
with zipfile.ZipFile(tmp_zip, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=6) as zf:
+ html_index_items: list[dict[str, Any]] = []
+ self_avatar_path = ""
+ session_items: list[dict[str, Any]] = []
+ remote_written: dict[str, str] = {}
+ remote_download_enabled = bool(download_remote_media) and (export_format == "html") and include_media and (not privacy_mode)
+ if export_format == "html":
+ ui_public_dir = _resolve_ui_public_dir()
+ css_payload = _load_ui_css_bundle(ui_public_dir=ui_public_dir, report=report)
+ zf.writestr("assets/wechat-chat-export.css", css_payload)
+ zf.writestr("assets/wechat-chat-export.js", _HTML_EXPORT_JS)
+
+ # Bundle UI static assets so the HTML works offline.
+ repo_root = Path(__file__).resolve().parents[2]
+ static_written: set[str] = {
+ "assets/wechat-chat-export.css",
+ "assets/wechat-chat-export.js",
+ }
+
+ if ui_public_dir is not None:
+ _zip_write_tree(
+ zf=zf,
+ src_dir=Path(ui_public_dir) / "fonts",
+ dest_prefix="fonts",
+ written=static_written,
+ )
+ _zip_write_tree(
+ zf=zf,
+ src_dir=Path(ui_public_dir) / "wxemoji",
+ dest_prefix="wxemoji",
+ written=static_written,
+ )
+ _zip_write_tree(
+ zf=zf,
+ src_dir=Path(ui_public_dir) / "assets" / "images" / "wechat",
+ dest_prefix="assets/images/wechat",
+ written=static_written,
+ )
+
+ _zip_write_tree(
+ zf=zf,
+ src_dir=repo_root / "frontend" / "public" / "assets" / "images" / "wechat",
+ dest_prefix="assets/images/wechat",
+ written=static_written,
+ )
+ _zip_write_tree(
+ zf=zf,
+ src_dir=repo_root / "frontend" / "assets" / "images" / "wechat",
+ dest_prefix="assets/images/wechat",
+ written=static_written,
+ )
+
+ preview_by_username: dict[str, str] = {}
+ last_ts_by_username: dict[str, int] = {}
+
+ if not privacy_mode:
+ self_avatar_path = _materialize_avatar(
+ zf=zf,
+ head_image_conn=head_image_conn,
+ username=account_dir.name,
+ avatar_written=avatar_written,
+ )
+
+ try:
+ preview_by_username = _load_latest_message_previews(account_dir, target_usernames)
+ except Exception:
+ preview_by_username = {}
+
+ session_db_path = Path(account_dir) / "session.db"
+ if session_db_path.exists():
+ sconn = sqlite3.connect(str(session_db_path))
+ sconn.row_factory = sqlite3.Row
+ try:
+ uniq = list(dict.fromkeys([u for u in target_usernames if u]))
+ chunk_size = 900
+ for i in range(0, len(uniq), chunk_size):
+ chunk = uniq[i : i + chunk_size]
+ placeholders = ",".join(["?"] * len(chunk))
+ try:
+ rows = sconn.execute(
+ f"SELECT username, sort_timestamp, last_timestamp FROM SessionTable WHERE username IN ({placeholders})",
+ chunk,
+ ).fetchall()
+ for r in rows:
+ u = str(r["username"] or "").strip()
+ if not u:
+ continue
+ ts = int(r["sort_timestamp"] or 0)
+ if ts <= 0:
+ ts = int(r["last_timestamp"] or 0)
+ last_ts_by_username[u] = int(ts or 0)
+ except sqlite3.OperationalError:
+ rows = sconn.execute(
+ f"SELECT username, last_timestamp FROM SessionTable WHERE username IN ({placeholders})",
+ chunk,
+ ).fetchall()
+ for r in rows:
+ u = str(r["username"] or "").strip()
+ if not u:
+ continue
+ last_ts_by_username[u] = int(r["last_timestamp"] or 0)
+ except Exception:
+ last_ts_by_username = {}
+ finally:
+ sconn.close()
+
+ for idx, conv_username in enumerate(target_usernames, start=1):
+ conv_row = contact_row_cache.get(conv_username)
+ conv_name = _pick_display_name(conv_row, conv_username)
+ conv_is_group = bool(conv_username.endswith("@chatroom"))
+ conv_dir = f"conversations/{_conversation_dir_name(idx, conv_name, conv_username, conv_is_group, privacy_mode)}"
+
+ conv_avatar_path = ""
+ if not privacy_mode:
+ conv_avatar_path = _materialize_avatar(
+ zf=zf,
+ head_image_conn=head_image_conn,
+ username=conv_username,
+ avatar_written=avatar_written,
+ )
+
+ session_items.append(
+ {
+ "username": "" if privacy_mode else conv_username,
+ "displayName": (f"会话 {idx:04d}" if privacy_mode else conv_name),
+ "isGroup": bool(conv_is_group),
+ "convDir": conv_dir,
+ "avatarPath": "" if privacy_mode else conv_avatar_path,
+ "lastTimeText": ("" if privacy_mode else _format_session_time(last_ts_by_username.get(conv_username))),
+ "previewText": ("" if privacy_mode else str(preview_by_username.get(conv_username) or "")),
+ }
+ )
+
for idx, conv_username in enumerate(target_usernames, start=1):
if self._should_cancel(job):
raise _JobCancelled()
@@ -456,16 +2867,13 @@ def resolve_display_name(u: str) -> str:
job.progress.current_conversation_messages_total = 0
try:
- if not can_estimate:
- estimated_total = 0
- else:
- estimated_total = _estimate_conversation_message_count(
- account_dir=account_dir,
- conv_username=conv_username,
- start_time=st,
- end_time=et,
- local_types=estimate_local_types,
- )
+ estimated_total = _estimate_conversation_message_count(
+ account_dir=account_dir,
+ conv_username=conv_username,
+ start_time=st,
+ end_time=et,
+ local_types=estimate_local_types,
+ )
except Exception:
estimated_total = 0
@@ -516,6 +2924,39 @@ def resolve_display_name(u: str) -> str:
job=job,
lock=self._lock,
)
+ elif export_format == "html":
+ exported_count = _write_conversation_html(
+ zf=zf,
+ conv_dir=conv_dir,
+ account_dir=account_dir,
+ conv_username=conv_username,
+ conv_name=conv_name,
+ conv_avatar_path=conv_avatar_path,
+ conv_is_group=conv_is_group,
+ self_avatar_path=self_avatar_path,
+ session_items=session_items,
+ download_remote_media=remote_download_enabled,
+ remote_written=remote_written,
+ html_page_size=html_page_size,
+ start_time=st,
+ end_time=et,
+ want_types=want_types,
+ local_types=local_types,
+ resource_conn=resource_conn,
+ resource_chat_id=chat_id,
+ head_image_conn=head_image_conn,
+ resolve_display_name=resolve_display_name,
+ privacy_mode=privacy_mode,
+ include_media=include_media,
+ media_kinds=media_kinds,
+ media_written=media_written,
+ avatar_written=avatar_written,
+ report=report,
+ allow_process_key_extract=allow_process_key_extract,
+ media_db_path=media_db_path,
+ job=job,
+ lock=self._lock,
+ )
else:
exported_count = _write_conversation_json(
zf=zf,
@@ -555,10 +2996,80 @@ def resolve_display_name(u: str) -> str:
"messageCount": int(exported_count),
}
zf.writestr(f"{conv_dir}/meta.json", json.dumps(meta, ensure_ascii=False, indent=2))
+ if export_format == "html":
+ html_index_items.append({"convDir": conv_dir, "meta": meta})
with self._lock:
+ job.progress.current_conversation_messages_exported = int(exported_count)
+ job.progress.current_conversation_messages_total = int(exported_count)
job.progress.conversations_done += 1
+ if export_format == "html":
+ def esc_text(v: Any) -> str:
+ return html.escape(str(v or ""), quote=False)
+
+ def esc_attr(v: Any) -> str:
+ return html.escape(str(v or ""), quote=True)
+
+ parts: list[str] = []
+ parts.append("\n")
+ parts.append('\n')
+ parts.append("\n")
+ parts.append(' \n')
+ parts.append(' \n')
+ parts.append(" 聊天记录导出 \n")
+ parts.append(' \n')
+ parts.append(' \n')
+ parts.append("\n")
+ parts.append("\n")
+ parts.append(
+ ' '
+ "提示:此页面需要 JavaScript 才能使用“合并聊天记录”等交互功能。若该提示一直存在,请确认已完整解压导出目录,并检查 wechat-chat-export.js 是否能加载(位于 assets/)。
\n"
+ )
+ parts.append('\n')
+ parts.append('
\n')
+ parts.append('
聊天记录导出(HTML) \n')
+ parts.append(
+ f'
账号: {esc_text("hidden" if privacy_mode else account_dir.name)} · 会话数: {len(html_index_items)} · 导出时间: {esc_text(_now_iso())}
\n'
+ )
+ parts.append('
\n")
+ parts.append('
提示:解压后直接打开本文件;媒体文件位于 media/ 目录。
\n')
+ parts.append("
\n")
+ parts.append("
\n")
+ parts.append("\n")
+ parts.append("\n")
+ zf.writestr("index.html", "".join(parts))
+
manifest = {
"schemaVersion": 1,
"exportedAt": _now_iso(),
@@ -577,6 +3088,8 @@ def resolve_display_name(u: str) -> str:
"includeMedia": include_media,
"mediaKinds": media_kinds,
"allowProcessKeyExtract": allow_process_key_extract,
+ "downloadRemoteMedia": bool(download_remote_media),
+ "htmlPageSize": int(html_page_size) if export_format == "html" else None,
"privacyMode": privacy_mode,
},
"stats": {
@@ -873,6 +3386,7 @@ def _parse_message_for_export(
resource_conn: Optional[sqlite3.Connection],
resource_chat_id: Optional[int],
sender_alias: str = "",
+ resolve_display_name: Optional[Callable[[str], str]] = None,
) -> dict[str, Any]:
raw_text = row.raw_text or ""
sender_username = str(row.sender_username or "").strip()
@@ -895,9 +3409,14 @@ def _parse_message_for_export(
title = ""
url = ""
from_name = ""
+ from_username = ""
+ link_type = ""
+ link_style = ""
record_item = ""
image_md5 = ""
+ image_md5_candidates: list[str] = []
image_file_id = ""
+ image_file_id_candidates: list[str] = []
emoji_md5 = ""
emoji_url = ""
thumb_url = ""
@@ -909,6 +3428,11 @@ def _parse_message_for_export(
video_url = ""
video_thumb_url = ""
voice_length = ""
+ quote_username = ""
+ quote_server_id = ""
+ quote_type = ""
+ quote_thumb_url = ""
+ quote_voice_length = ""
quote_title = ""
quote_content = ""
amount = ""
@@ -919,16 +3443,25 @@ def _parse_message_for_export(
file_md5 = ""
transfer_id = ""
voip_type = ""
+ location_lat: Optional[float] = None
+ location_lng: Optional[float] = None
+ location_poiname = ""
+ location_label = ""
if local_type == 10000:
render_type = "system"
- if "revokemsg" in raw_text:
- content_text = "撤回了一条消息"
- else:
- import re as _re
-
- content_text = _re.sub(r"?[_a-zA-Z0-9]+[^>]*>", "", raw_text)
- content_text = _re.sub(r"\\s+", " ", content_text).strip() or "[系统消息]"
+ system_display_name_resolver = None
+ if resolve_display_name is not None:
+ def system_display_name_resolver(username: str, fallback_display_name: str) -> str:
+ resolved = str(resolve_display_name(username) or "").strip()
+ if resolved and resolved != username:
+ return resolved
+ fallback = str(fallback_display_name or "").strip()
+ return fallback or resolved or username
+ content_text = _parse_system_message_content(
+ raw_text,
+ resolve_display_name=system_display_name_resolver,
+ )
elif local_type == 49:
parsed = _parse_app_message(raw_text)
render_type = str(parsed.get("renderType") or "text")
@@ -936,7 +3469,15 @@ def _parse_message_for_export(
title = str(parsed.get("title") or "")
url = str(parsed.get("url") or "")
from_name = str(parsed.get("from") or "")
+ from_username = str(parsed.get("fromUsername") or "")
+ link_type = str(parsed.get("linkType") or "")
+ link_style = str(parsed.get("linkStyle") or "")
record_item = str(parsed.get("recordItem") or "")
+ quote_username = str(parsed.get("quoteUsername") or "")
+ quote_server_id = str(parsed.get("quoteServerId") or "")
+ quote_type = str(parsed.get("quoteType") or "")
+ quote_thumb_url = str(parsed.get("quoteThumbUrl") or "")
+ quote_voice_length = str(parsed.get("quoteVoiceLength") or "")
quote_title = str(parsed.get("quoteTitle") or "")
quote_content = str(parsed.get("quoteContent") or "")
amount = str(parsed.get("amount") or "")
@@ -969,51 +3510,98 @@ def _parse_message_for_export(
render_type = "quote"
parsed = _parse_app_message(raw_text)
content_text = str(parsed.get("content") or "[引用消息]")
+ quote_username = str(parsed.get("quoteUsername") or "")
+ quote_server_id = str(parsed.get("quoteServerId") or "")
+ quote_type = str(parsed.get("quoteType") or "")
+ quote_thumb_url = str(parsed.get("quoteThumbUrl") or "")
+ quote_voice_length = str(parsed.get("quoteVoiceLength") or "")
quote_title = str(parsed.get("quoteTitle") or "")
quote_content = str(parsed.get("quoteContent") or "")
+ elif local_type == 48:
+ parsed = _parse_location_message(raw_text)
+ render_type = str(parsed.get("renderType") or "location")
+ content_text = str(parsed.get("content") or "[Location]")
+ location_lat = parsed.get("locationLat")
+ location_lng = parsed.get("locationLng")
+ location_poiname = str(parsed.get("locationPoiname") or "")
+ location_label = str(parsed.get("locationLabel") or "")
elif local_type == 3:
render_type = "image"
- image_md5 = _extract_xml_attr(raw_text, "md5") or _extract_xml_tag_text(raw_text, "md5")
- if not image_md5:
- for k in [
- "cdnthumbmd5",
- "cdnthumd5",
- "cdnmidimgmd5",
- "cdnbigimgmd5",
- "hdmd5",
- "hevc_mid_md5",
- "hevc_md5",
- "imgmd5",
- "filemd5",
- ]:
- image_md5 = _extract_xml_attr(raw_text, k) or _extract_xml_tag_text(raw_text, k)
- if image_md5:
- break
+ def add_md5(v: Any) -> None:
+ s = str(v or "").strip().lower()
+ if _is_md5(s) and s not in image_md5_candidates:
+ image_md5_candidates.append(s)
+
+ for k in [
+ "md5",
+ "hdmd5",
+ "hevc_md5",
+ "hevc_mid_md5",
+ "cdnbigimgmd5",
+ "cdnmidimgmd5",
+ "cdnthumbmd5",
+ "cdnthumd5",
+ "imgmd5",
+ "filemd5",
+ ]:
+ add_md5(_extract_xml_attr(raw_text, k))
+ add_md5(_extract_xml_tag_text(raw_text, k))
+
+ # Prefer message_resource.db md5 for local files: XML md5 frequently differs from the on-disk *.dat basename
+ # (especially for *_t.dat thumbnails), causing offline media materialization to miss.
+ if resource_conn is not None:
+ try:
+ md5_hit = _lookup_resource_md5(
+ resource_conn,
+ resource_chat_id,
+ message_local_type=local_type,
+ server_id=int(row.server_id or 0),
+ local_id=int(row.local_id or 0),
+ create_time=int(row.create_time or 0),
+ )
+ except Exception:
+ md5_hit = ""
+
+ md5_hit = str(md5_hit or "").strip().lower()
+ if _is_md5(md5_hit):
+ try:
+ image_md5_candidates.remove(md5_hit)
+ except ValueError:
+ pass
+ image_md5_candidates.insert(0, md5_hit)
- _cdn_url_or_id = (
- _extract_xml_attr(raw_text, "cdnthumburl")
- or _extract_xml_attr(raw_text, "cdnthumurl")
- or _extract_xml_attr(raw_text, "cdnmidimgurl")
- or _extract_xml_attr(raw_text, "cdnbigimgurl")
- or _extract_xml_tag_text(raw_text, "cdnthumburl")
- or _extract_xml_tag_text(raw_text, "cdnthumurl")
- or _extract_xml_tag_text(raw_text, "cdnmidimgurl")
- or _extract_xml_tag_text(raw_text, "cdnbigimgurl")
- )
- _cdn_url_or_id = str(_cdn_url_or_id or "").strip()
- image_url = _cdn_url_or_id if _cdn_url_or_id.startswith(("http://", "https://")) else ""
- if (not image_url) and _cdn_url_or_id:
- image_file_id = _cdn_url_or_id
+ image_md5 = image_md5_candidates[0] if image_md5_candidates else ""
- if (not image_md5) and resource_conn is not None:
- image_md5 = _lookup_resource_md5(
- resource_conn,
- resource_chat_id,
- message_local_type=local_type,
- server_id=int(row.server_id or 0),
- local_id=int(row.local_id or 0),
- create_time=int(row.create_time or 0),
- )
+ url_or_id_candidates: list[str] = []
+
+ def add_url_or_id(v: Any) -> None:
+ s = str(v or "").strip()
+ if s:
+ try:
+ s = html.unescape(s).strip()
+ except Exception:
+ pass
+ if s and s not in url_or_id_candidates:
+ url_or_id_candidates.append(s)
+
+ for k in ["cdnthumburl", "cdnthumurl", "cdnmidimgurl", "cdnbigimgurl"]:
+ add_url_or_id(_extract_xml_attr(raw_text, k))
+ add_url_or_id(_extract_xml_tag_text(raw_text, k))
+
+ for v in url_or_id_candidates:
+ low = str(v or "").strip().lower()
+ if low.startswith(("http://", "https://")):
+ if not image_url:
+ image_url = str(v).strip()
+ continue
+ if str(v).startswith("//"):
+ if not image_url:
+ image_url = "https:" + str(v).strip()
+ continue
+ if v and v not in image_file_id_candidates:
+ image_file_id_candidates.append(v)
+
+ image_file_id = image_file_id_candidates[0] if image_file_id_candidates else ""
content_text = "[图片]"
elif local_type == 34:
render_type = "voice"
@@ -1107,7 +3695,16 @@ def _parse_message_for_export(
content_text = str(parsed.get("content") or content_text)
title = str(parsed.get("title") or title)
url = str(parsed.get("url") or url)
+ from_name = str(parsed.get("from") or from_name)
+ from_username = str(parsed.get("fromUsername") or from_username)
+ link_type = str(parsed.get("linkType") or link_type)
+ link_style = str(parsed.get("linkStyle") or link_style)
record_item = str(parsed.get("recordItem") or record_item)
+ quote_username = str(parsed.get("quoteUsername") or quote_username)
+ quote_server_id = str(parsed.get("quoteServerId") or quote_server_id)
+ quote_type = str(parsed.get("quoteType") or quote_type)
+ quote_thumb_url = str(parsed.get("quoteThumbUrl") or quote_thumb_url)
+ quote_voice_length = str(parsed.get("quoteVoiceLength") or quote_voice_length)
quote_title = str(parsed.get("quoteTitle") or quote_title)
quote_content = str(parsed.get("quoteContent") or quote_content)
amount = str(parsed.get("amount") or amount)
@@ -1165,10 +3762,15 @@ def _parse_message_for_export(
"title": title,
"url": url,
"from": from_name,
+ "fromUsername": from_username,
+ "linkType": link_type,
+ "linkStyle": link_style,
"recordItem": record_item,
"thumbUrl": thumb_url,
"imageMd5": image_md5,
"imageFileId": image_file_id,
+ "imageMd5Candidates": image_md5_candidates,
+ "imageFileIdCandidates": image_file_id_candidates,
"imageUrl": image_url,
"emojiMd5": emoji_md5,
"emojiUrl": emoji_url,
@@ -1179,6 +3781,11 @@ def _parse_message_for_export(
"videoUrl": video_url,
"videoThumbUrl": video_thumb_url,
"voiceLength": voice_length,
+ "quoteUsername": quote_username,
+ "quoteServerId": quote_server_id,
+ "quoteType": quote_type,
+ "quoteThumbUrl": quote_thumb_url,
+ "quoteVoiceLength": quote_voice_length,
"quoteTitle": quote_title,
"quoteContent": quote_content,
"amount": amount,
@@ -1189,6 +3796,10 @@ def _parse_message_for_export(
"transferStatus": transfer_status,
"transferId": transfer_id,
"voipType": voip_type,
+ "locationLat": location_lat,
+ "locationLng": location_lng,
+ "locationPoiname": location_poiname,
+ "locationLabel": location_label,
}
@@ -1324,13 +3935,10 @@ def lookup_alias(username: str) -> str:
resource_conn=resource_conn,
resource_chat_id=resource_chat_id,
sender_alias=sender_alias,
+ resolve_display_name=resolve_display_name,
)
- if want_types:
- rt_key = _normalize_render_type_key(msg.get("renderType"))
- if rt_key not in want_types:
- if scanned % 500 == 0 and job.cancel_requested:
- raise _JobCancelled()
- continue
+ if not _is_render_type_selected(msg.get("renderType"), want_types):
+ continue
su = str(msg.get("senderUsername") or "").strip()
if privacy_mode:
@@ -1438,43 +4046,816 @@ def lookup_alias(username: str) -> str:
if u in alias_cache:
return alias_cache[u]
- alias = ""
+ alias = ""
+ try:
+ r = contact_conn.execute("SELECT alias FROM contact WHERE username = ? LIMIT 1", (u,)).fetchone()
+ if r is not None and r[0] is not None:
+ alias = str(r[0] or "").strip()
+ if not alias:
+ r = contact_conn.execute("SELECT alias FROM stranger WHERE username = ? LIMIT 1", (u,)).fetchone()
+ if r is not None and r[0] is not None:
+ alias = str(r[0] or "").strip()
+ except Exception:
+ alias = ""
+
+ alias_cache[u] = alias
+ return alias
+
+ # Same as JSON: write to temp file first to avoid zip interleaving writes.
+ with tempfile.TemporaryDirectory(prefix="wechat_chat_export_") as tmp_dir:
+ tmp_path = Path(tmp_dir) / "messages.txt"
+ with open(tmp_path, "w", encoding="utf-8", newline="\n") as tw:
+ if privacy_mode:
+ tw.write("会话: 已隐藏\n")
+ tw.write("账号: hidden\n")
+ else:
+ tw.write(f"会话: {conv_name} ({conv_username})\n")
+ tw.write(f"账号: {account_dir.name}\n")
+ if conv_avatar_path:
+ tw.write(f"会话头像: {conv_avatar_path}\n")
+ if start_time or end_time:
+ st = _format_ts(int(start_time)) if start_time else "不限"
+ et = _format_ts(int(end_time)) if end_time else "不限"
+ tw.write(f"时间范围: {st} ~ {et}\n")
+ if want_types:
+ tw.write(f"消息类型: {', '.join(sorted(want_types))}\n")
+ tw.write(f"导出时间: {_now_iso()}\n")
+ tw.write("\n")
+
+ sender_alias_map: dict[str, int] = {}
+ scanned = 0
+ prev_ts = 0
+ for row in _iter_rows_for_conversation(
+ account_dir=account_dir,
+ conv_username=conv_username,
+ start_time=start_time,
+ end_time=end_time,
+ local_types=local_types,
+ ):
+ scanned += 1
+ sender_alias = ""
+ if conv_is_group and row.raw_text and (not row.raw_text.startswith("<")) and (not row.raw_text.startswith('"<')):
+ sep = row.raw_text.find(":\n")
+ if sep > 0:
+ prefix = row.raw_text[:sep].strip()
+ su = str(row.sender_username or "").strip()
+ if prefix and su and prefix != su:
+ strong_hint = prefix.startswith("wxid_") or prefix.endswith("@chatroom") or "@" in prefix
+ if not strong_hint:
+ body_probe = row.raw_text[sep + 2 :].lstrip("\n").lstrip()
+ body_is_xml = body_probe.startswith("<") or body_probe.startswith('"<')
+ if not body_is_xml:
+ sender_alias = lookup_alias(su)
+
+ msg = _parse_message_for_export(
+ row=row,
+ conv_username=conv_username,
+ is_group=conv_is_group,
+ resource_conn=resource_conn,
+ resource_chat_id=resource_chat_id,
+ sender_alias=sender_alias,
+ resolve_display_name=resolve_display_name,
+ )
+ if not _is_render_type_selected(msg.get("renderType"), want_types):
+ continue
+
+ su = str(msg.get("senderUsername") or "").strip()
+ if privacy_mode:
+ _privacy_scrub_message(msg, conv_is_group=conv_is_group, sender_alias_map=sender_alias_map)
+ else:
+ msg["senderDisplayName"] = resolve_display_name(su) if su else ""
+ msg["senderAvatarPath"] = (
+ _materialize_avatar(
+ zf=zf,
+ head_image_conn=head_image_conn,
+ username=su,
+ avatar_written=avatar_written,
+ )
+ if (su and head_image_conn is not None)
+ else ""
+ )
+
+ if include_media:
+ _attach_offline_media(
+ zf=zf,
+ account_dir=account_dir,
+ conv_username=conv_username,
+ msg=msg,
+ media_written=media_written,
+ report=report,
+ media_kinds=media_kinds,
+ allow_process_key_extract=allow_process_key_extract,
+ media_db_path=media_db_path,
+ lock=lock,
+ job=job,
+ )
+
+ tw.write(_format_message_line_txt(msg=msg) + "\n")
+
+ exported += 1
+ with lock:
+ job.progress.messages_exported += 1
+ job.progress.current_conversation_messages_exported = exported
+
+ if scanned % 500 == 0 and job.cancel_requested:
+ raise _JobCancelled()
+
+ tw.flush()
+
+ zf.write(str(tmp_path), arcname)
+ if contact_conn is not None:
+ try:
+ contact_conn.close()
+ except Exception:
+ pass
+
+ return exported
+
+
+def _write_conversation_html(
+ *,
+ zf: zipfile.ZipFile,
+ conv_dir: str,
+ account_dir: Path,
+ conv_username: str,
+ conv_name: str,
+ conv_avatar_path: str,
+ conv_is_group: bool,
+ self_avatar_path: str,
+ session_items: list[dict[str, Any]],
+ download_remote_media: bool,
+ remote_written: dict[str, str],
+ html_page_size: int = 1000,
+ start_time: Optional[int],
+ end_time: Optional[int],
+ want_types: Optional[set[str]],
+ local_types: Optional[set[int]],
+ resource_conn: Optional[sqlite3.Connection],
+ resource_chat_id: Optional[int],
+ head_image_conn: Optional[sqlite3.Connection],
+ resolve_display_name: Any,
+ privacy_mode: bool,
+ include_media: bool,
+ media_kinds: list[MediaKind],
+ media_written: dict[str, str],
+ avatar_written: dict[str, str],
+ report: dict[str, Any],
+ allow_process_key_extract: bool,
+ media_db_path: Path,
+ job: ExportJob,
+ lock: threading.Lock,
+) -> int:
+ arcname = f"{conv_dir}/messages.html"
+ exported = 0
+
+ rel_root = "../../"
+ css_href = rel_root + "assets/wechat-chat-export.css"
+ js_src = rel_root + "assets/wechat-chat-export.js"
+
+ def esc_text(v: Any) -> str:
+ return html.escape(str(v or ""), quote=False)
+
+ def esc_attr(v: Any) -> str:
+ return html.escape(str(v or ""), quote=True)
+
+ def is_http_url(u: str) -> bool:
+ s = str(u or "").strip().lower()
+ return s.startswith("http://") or s.startswith("https://")
+
+ def rel_path(p: Any) -> str:
+ s = str(p or "").strip().lstrip("/").replace("\\", "/")
+ if not s:
+ return ""
+ return rel_root + s
+
+ def offline_path(msg: dict[str, Any], kind: str) -> str:
+ media = msg.get("offlineMedia") or []
+ if not isinstance(media, list):
+ return ""
+ for item in media:
+ try:
+ k = str(item.get("kind") or "").strip()
+ except Exception:
+ k = ""
+ if k != kind:
+ continue
+ try:
+ p = str(item.get("path") or "").strip()
+ except Exception:
+ p = ""
+ if p:
+ return rel_path(p)
+ return ""
+
+ def maybe_download_remote_image(url: str) -> str:
+ if not download_remote_media:
+ return ""
+ u = str(url or "").strip()
+ if u:
+ try:
+ u = html.unescape(u).strip()
+ except Exception:
+ pass
+ try:
+ u = re.sub(r"\s+", "", u)
+ except Exception:
+ pass
+ if not is_http_url(u):
+ return ""
+ arc = _download_remote_image_to_zip(
+ zf=zf,
+ url=u,
+ remote_written=remote_written,
+ report=report,
+ )
+ if not arc:
+ return ""
+ local = rel_path(arc)
+ try:
+ page_media_index.setdefault("remote", {})[u] = local
+ except Exception:
+ pass
+ return local
+
+ emoji_table = _load_wechat_emoji_table()
+ emoji_regex = _load_wechat_emoji_regex()
+
+ def render_text_with_emojis(v: Any) -> str:
+ text = str(v or "")
+ if not text:
+ return ""
+ if not emoji_table or emoji_regex is None:
+ return esc_text(text)
+
+ parts: list[str] = []
+ last = 0
+ for match in emoji_regex.finditer(text):
+ start = match.start()
+ end = match.end()
+ if start > last:
+ parts.append(esc_text(text[last:start]))
+
+ key = match.group(0)
+ value = str(emoji_table.get(key) or "")
+ if value:
+ src = rel_path(f"wxemoji/{value}")
+ parts.append(
+ f' '
+ )
+ else:
+ parts.append(esc_text(key))
+ last = end
+
+ if last < len(text):
+ parts.append(esc_text(text[last:]))
+ return "".join(parts)
+
+ def build_avatar_html(*, src: str, fallback_text: str, extra_class: str) -> str:
+ safe_fallback = esc_text((fallback_text or "?")[:1] or "?")
+ if src:
+ return (
+ f'"
+ )
+ return (
+ f'"
+ )
+
+ def wechat_icon(name: str) -> str:
+ return rel_path(f"assets/images/wechat/{name}")
+
+ def format_file_size(size: Any) -> str:
+ if not size:
+ return ""
+ s = str(size).strip()
+ try:
+ num = float(s)
+ except Exception:
+ return s
+
+ if num < 0:
+ return s
+
+ def fmt_num(n: float) -> str:
+ if float(n).is_integer():
+ return str(int(n))
+ txt = f"{n:.2f}"
+ return txt.rstrip("0").rstrip(".")
+
+ if num < 1024:
+ return f"{fmt_num(num)} B"
+ if num < 1024 * 1024:
+ return f"{(num / 1024):.2f} KB"
+ return f"{(num / 1024 / 1024):.2f} MB"
+
+ def format_transfer_amount(amount: Any) -> str:
+ s = str(amount if amount is not None else "").strip()
+ if not s:
+ return ""
+ return re.sub(r"[¥¥]", "", s).strip()
+
+ def get_red_packet_text(message: dict[str, Any]) -> str:
+ text = str(message.get("content") if message is not None else "").strip()
+ if (not text) or text == "[Red Packet]":
+ return "恭喜发财,大吉大利"
+ return text
+
+ def is_transfer_returned(message: dict[str, Any]) -> bool:
+ pay_sub_type = str(message.get("paySubType") or "").strip()
+ if pay_sub_type in {"4", "9"}:
+ return True
+ st = str(message.get("transferStatus") or "").strip()
+ c = str(message.get("content") or "").strip()
+ text = f"{st} {c}".strip()
+ if not text:
+ return False
+ return ("退回" in text) or ("退还" in text)
+
+ def is_transfer_overdue(message: dict[str, Any]) -> bool:
+ pay_sub_type = str(message.get("paySubType") or "").strip()
+ if pay_sub_type == "10":
+ return True
+ st = str(message.get("transferStatus") or "").strip()
+ c = str(message.get("content") or "").strip()
+ text = f"{st} {c}".strip()
+ if not text:
+ return False
+ return "过期" in text
+
+ def is_transfer_received(message: dict[str, Any]) -> bool:
+ pay_sub_type = str(message.get("paySubType") or "").strip()
+ if pay_sub_type == "3":
+ return True
+ st = str(message.get("transferStatus") or "").strip()
+ if not st:
+ return False
+ return ("已收款" in st) or ("已被接收" in st)
+
+ def get_transfer_title(message: dict[str, Any], *, is_sent: bool) -> str:
+ pay_sub_type = str(message.get("paySubType") or "").strip()
+ transfer_status = str(message.get("transferStatus") or "").strip()
+ if transfer_status:
+ return transfer_status
+ if pay_sub_type == "1":
+ return "转账"
+ if pay_sub_type == "3":
+ return "已被接收" if is_sent else "已收款"
+ if pay_sub_type == "8":
+ return "发起转账"
+ if pay_sub_type == "4":
+ return "已退还"
+ if pay_sub_type == "9":
+ return "已被退还"
+ if pay_sub_type == "10":
+ return "已过期"
+ content = str(message.get("content") or "").strip()
+ if content and content not in {"转账", "[转账]"}:
+ return content
+ return "转账"
+
+ def get_voice_duration_in_seconds(duration_ms: Any) -> int:
+ try:
+ ms = int(str(duration_ms or "0").strip() or "0")
+ except Exception:
+ ms = 0
+ return int(round(ms / 1000.0))
+
+ def get_voice_width(duration_ms: Any) -> str:
+ seconds = get_voice_duration_in_seconds(duration_ms)
+ min_width = 80
+ max_width = 200
+ width = min(max_width, min_width + seconds * 4)
+ return f"{width}px"
+
+ def get_chat_history_preview_lines(message: dict[str, Any]) -> list[str]:
+ raw = str(message.get("content") or "").strip()
+ if not raw:
+ return []
+ lines = [ln.strip() for ln in raw.splitlines()]
+ lines = [ln for ln in lines if ln]
+ return lines[:4]
+
+ def get_file_icon_url(file_name: str) -> str:
+ ext = ""
+ try:
+ ext = (str(file_name or "").rsplit(".", 1)[-1] or "").lower().strip()
+ except Exception:
+ ext = ""
+
+ if ext == "pdf":
+ return wechat_icon("pdf.png")
+ if ext in {"zip", "rar", "7z", "tar", "gz"}:
+ return wechat_icon("zip.png")
+ if ext in {"doc", "docx"}:
+ return wechat_icon("word.png")
+ if ext in {"xls", "xlsx", "csv"}:
+ return wechat_icon("excel.png")
+ return wechat_icon("zip.png")
+
+ def get_link_from_text(message: dict[str, Any], *, url: str) -> str:
+ raw = str(message.get("from") or "").strip()
+ if raw:
+ return raw
+ try:
+ from urllib.parse import urlparse
+
+ host = urlparse(str(url or "")).hostname
+ return str(host or "").strip()
+ except Exception:
+ return ""
+
+ def first_glyph(text: str) -> str:
+ t = str(text or "").strip()
+ if not t:
+ return ""
+ try:
+ return next(iter(t)) or ""
+ except Exception:
+ return t[:1]
+
+ page_media_index: dict[str, Any] = {
+ "images": {},
+ "emojis": {},
+ "videos": {},
+ "videoThumbs": {},
+ "serverMd5": {},
+ "remote": {},
+ }
+ chat_history_md5_done: set[str] = set()
+
+ def _remember_offline_media(message: dict[str, Any]) -> None:
+ media = message.get("offlineMedia") or []
+ if not isinstance(media, list):
+ return
+ for item in media:
+ try:
+ kind = str(item.get("kind") or "").strip()
+ except Exception:
+ kind = ""
+ try:
+ md5 = str(item.get("md5") or "").strip().lower()
+ except Exception:
+ md5 = ""
+ try:
+ path0 = str(item.get("path") or "").strip()
+ except Exception:
+ path0 = ""
+ if (not md5) or (not path0):
+ continue
+ url0 = rel_path(path0)
+ if kind == "image":
+ page_media_index["images"][md5] = url0
+ elif kind == "emoji":
+ page_media_index["emojis"][md5] = url0
+ elif kind == "video":
+ page_media_index["videos"][md5] = url0
+ elif kind == "video_thumb":
+ page_media_index["videoThumbs"][md5] = url0
+
+ def _ensure_chat_history_md5(md5: str) -> str:
+ m = str(md5 or "").strip().lower()
+ if (not m) or (not _is_md5(m)):
+ return ""
+ if m in chat_history_md5_done:
+ for k in ("images", "emojis", "videos", "videoThumbs"):
+ try:
+ hit = str((page_media_index.get(k) or {}).get(m) or "").strip()
+ except Exception:
+ hit = ""
+ if hit:
+ return hit
+ return ""
+ chat_history_md5_done.add(m)
+
+ arc = ""
+ is_new = False
+
+ for try_kind in ("image", "emoji", "video_thumb", "video"):
+ arc, is_new = _materialize_media(
+ zf=zf,
+ account_dir=account_dir,
+ conv_username=conv_username,
+ kind=try_kind, # type: ignore[arg-type]
+ md5=m,
+ file_id="",
+ media_written=media_written,
+ suggested_name="",
+ )
+ if arc:
+ break
+
+ if not arc:
+ return ""
+
+ url0 = rel_path(arc)
try:
- r = contact_conn.execute("SELECT alias FROM contact WHERE username = ? LIMIT 1", (u,)).fetchone()
- if r is not None and r[0] is not None:
- alias = str(r[0] or "").strip()
- if not alias:
- r = contact_conn.execute("SELECT alias FROM stranger WHERE username = ? LIMIT 1", (u,)).fetchone()
- if r is not None and r[0] is not None:
- alias = str(r[0] or "").strip()
+ page_media_index["images"].setdefault(m, url0)
+ page_media_index["emojis"].setdefault(m, url0)
+ page_media_index["videoThumbs"].setdefault(m, url0)
+ if arc.lower().endswith(".mp4"):
+ page_media_index["videos"][m] = url0
except Exception:
- alias = ""
+ pass
- alias_cache[u] = alias
- return alias
+ if is_new:
+ with lock:
+ job.progress.media_copied += 1
+ return url0
+
+ chat_title = "已隐藏" if privacy_mode else (conv_name or conv_username or "会话")
+ page_title = chat_title
+
+ options = [
+ ("all", "全部"),
+ ("text", "文本"),
+ ("image", "图片"),
+ ("emoji", "表情"),
+ ("video", "视频"),
+ ("voice", "语音"),
+ ("chatHistory", "聊天记录"),
+ ("transfer", "转账"),
+ ("redPacket", "红包"),
+ ("file", "文件"),
+ ("link", "链接"),
+ ("quote", "引用"),
+ ("system", "系统"),
+ ("voip", "通话"),
+ ]
+
+ page_size = 0
+ try:
+ page_size = int(html_page_size or 0)
+ except Exception:
+ page_size = 0
+ if page_size < 0:
+ page_size = 0
- # Same as JSON: write to temp file first to avoid zip interleaving writes.
+ # NOTE: write to a temp file first to avoid zip interleaving writes.
with tempfile.TemporaryDirectory(prefix="wechat_chat_export_") as tmp_dir:
- tmp_path = Path(tmp_dir) / "messages.txt"
- with open(tmp_path, "w", encoding="utf-8", newline="\n") as tw:
- if privacy_mode:
- tw.write("会话: 已隐藏\n")
- tw.write("账号: hidden\n")
+ tmp_path = Path(tmp_dir) / "messages.html"
+ pages_frag_dir = Path(tmp_dir) / "pages_fragments"
+ page_frag_paths: list[Path] = []
+ paged_old_page_paths: list[Path] = []
+ paged_total_pages = 1
+ paged_pad_width = 4
+ with open(tmp_path, "w", encoding="utf-8", newline="\n") as hw:
+ class _WriteProxy:
+ def __init__(self, default_target):
+ self._default = default_target
+ self._target = default_target
+
+ def set_target(self, target) -> None:
+ self._target = target or self._default
+
+ def write(self, s: str) -> Any:
+ return self._target.write(s)
+
+ def flush(self) -> None:
+ try:
+ if self._target is not self._default:
+ self._target.flush()
+ except Exception:
+ pass
+ try:
+ self._default.flush()
+ except Exception:
+ pass
+
+ tw = _WriteProxy(hw)
+ tw.write("\n")
+ tw.write('\n')
+ tw.write("\n")
+ tw.write(' \n')
+ tw.write(' \n')
+ tw.write(f" {esc_text(page_title)} \n")
+ tw.write(f' \n')
+ tw.write(f' \n')
+ tw.write("\n")
+ tw.write("\n")
+ tw.write(
+ ' '
+ "提示:此页面需要 JavaScript 才能使用“合并聊天记录”等交互功能。若该提示一直存在,请确认已完整解压导出目录,并检查 wechat-chat-export.js 是否能加载(位于 assets/)。
\n"
+ )
+
+ # Root
+ tw.write('\n')
+
+ # Left rail (avatar + chat icon)
+ tw.write(
+ '
\n'
+ )
+
+ self_avatar_src = "" if privacy_mode else rel_path(self_avatar_path)
+ tw.write('
\n')
+ tw.write('
\n')
+ if self_avatar_src:
+ tw.write(
+ f'
\n'
+ )
else:
- tw.write(f"会话: {conv_name} ({conv_username})\n")
- tw.write(f"账号: {account_dir.name}\n")
- if conv_avatar_path:
- tw.write(f"会话头像: {conv_avatar_path}\n")
- if start_time or end_time:
- st = _format_ts(int(start_time)) if start_time else "不限"
- et = _format_ts(int(end_time)) if end_time else "不限"
- tw.write(f"时间范围: {st} ~ {et}\n")
- if want_types:
- tw.write(f"消息类型: {', '.join(sorted(want_types))}\n")
- tw.write(f"导出时间: {_now_iso()}\n")
- tw.write("\n")
+ tw.write(
+ '
我
\n'
+ )
+ tw.write("
\n")
+ tw.write("
\n")
+
+ tw.write(
+ f' \n")
+ tw.write("
\n")
+
+ # Middle session list (all exported conversations)
+ tw.write(
+ '
\n'
+ )
+ tw.write('
\n')
+ tw.write(
+ '
\n'
+ )
+ tw.write('
\n")
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write('
\n")
+ tw.write("
\n")
+
+ # Right chat area
+ tw.write('
\n')
+ tw.write('
\n')
+ tw.write('
\n')
+ tw.write('
\n')
+
+ tw.write(' \n")
+
+ tw.write('
\n')
+ tw.write(' \n")
+ tw.write('
\n')
+
+ page_fp = None
+ page_fp_path: Optional[Path] = None
+ page_no = 1
+ page_msg_count = 0
+
+ def _open_page_fp() -> Any:
+ nonlocal page_fp, page_fp_path
+ pages_frag_dir.mkdir(parents=True, exist_ok=True)
+ page_fp_path = pages_frag_dir / f"page_{page_no}.htmlfrag"
+ page_fp = open(page_fp_path, "w", encoding="utf-8", newline="\n")
+ return page_fp
+
+ def _close_page_fp() -> None:
+ nonlocal page_fp, page_fp_path
+ if page_fp is None:
+ page_fp_path = None
+ return
+ try:
+ page_fp.flush()
+ except Exception:
+ pass
+ try:
+ page_fp.close()
+ except Exception:
+ pass
+ if page_fp_path is not None:
+ page_frag_paths.append(page_fp_path)
+ page_fp = None
+ page_fp_path = None
+ tw.set_target(hw)
+
+ def _mark_exported() -> None:
+ nonlocal exported, page_no, page_msg_count
+ exported += 1
+ with lock:
+ job.progress.messages_exported += 1
+ job.progress.current_conversation_messages_exported = exported
+ if page_size > 0:
+ page_msg_count += 1
+ if page_msg_count >= page_size:
+ _close_page_fp()
+ page_no += 1
+ page_msg_count = 0
sender_alias_map: dict[str, int] = {}
+ prev_ts = 0
scanned = 0
for row in _iter_rows_for_conversation(
account_dir=account_dir,
@@ -1484,19 +4865,6 @@ def lookup_alias(username: str) -> str:
local_types=local_types,
):
scanned += 1
- sender_alias = ""
- if conv_is_group and row.raw_text and (not row.raw_text.startswith("<")) and (not row.raw_text.startswith('"<')):
- sep = row.raw_text.find(":\n")
- if sep > 0:
- prefix = row.raw_text[:sep].strip()
- su = str(row.sender_username or "").strip()
- if prefix and su and prefix != su:
- strong_hint = prefix.startswith("wxid_") or prefix.endswith("@chatroom") or "@" in prefix
- if not strong_hint:
- body_probe = row.raw_text[sep + 2 :].lstrip("\n").lstrip()
- body_is_xml = body_probe.startswith("<") or body_probe.startswith('"<')
- if not body_is_xml:
- sender_alias = lookup_alias(su)
msg = _parse_message_for_export(
row=row,
@@ -1504,28 +4872,25 @@ def lookup_alias(username: str) -> str:
is_group=conv_is_group,
resource_conn=resource_conn,
resource_chat_id=resource_chat_id,
- sender_alias=sender_alias,
+ sender_alias="",
+ resolve_display_name=resolve_display_name,
)
- if want_types:
- rt_key = _normalize_render_type_key(msg.get("renderType"))
- if rt_key not in want_types:
- if scanned % 500 == 0 and job.cancel_requested:
- raise _JobCancelled()
- continue
+ if not _is_render_type_selected(msg.get("renderType"), want_types):
+ continue
- su = str(msg.get("senderUsername") or "").strip()
+ sender_username = str(msg.get("senderUsername") or "").strip()
if privacy_mode:
_privacy_scrub_message(msg, conv_is_group=conv_is_group, sender_alias_map=sender_alias_map)
else:
- msg["senderDisplayName"] = resolve_display_name(su) if su else ""
+ msg["senderDisplayName"] = resolve_display_name(sender_username) if sender_username else ""
msg["senderAvatarPath"] = (
_materialize_avatar(
zf=zf,
head_image_conn=head_image_conn,
- username=su,
+ username=sender_username,
avatar_written=avatar_written,
)
- if (su and head_image_conn is not None)
+ if (sender_username and head_image_conn is not None)
else ""
)
@@ -1543,25 +4908,692 @@ def lookup_alias(username: str) -> str:
lock=lock,
job=job,
)
+ _remember_offline_media(msg)
- tw.write(_format_message_line_txt(msg=msg) + "\n")
+ rt = str(msg.get("renderType") or "text").strip() or "text"
+ create_time_text = str(msg.get("createTimeText") or "").strip()
+ try:
+ ts = int(msg.get("createTime") or 0)
+ except Exception:
+ ts = 0
+
+ show_divider = False
+ if ts and ((prev_ts == 0) or (abs(ts - prev_ts) >= 300)):
+ show_divider = True
+
+ if page_size > 0:
+ if page_fp is None:
+ _open_page_fp()
+ tw.set_target(page_fp)
+
+ if show_divider:
+ divider_text = _format_session_time(ts)
+ if divider_text:
+ tw.write('
\n')
+ tw.write(f'
{esc_text(divider_text)}
\n')
+ tw.write("
\n")
+
+ # Wrapper (for filter)
+ tw.write(f'
\n')
+
+ if rt == "system":
+ tw.write('
\n')
+ tw.write(f'
{esc_text(msg.get("content") or "")}
\n')
+ tw.write("
\n")
+ tw.write("
\n")
+ _mark_exported()
+ if ts:
+ prev_ts = ts
+ continue
- exported += 1
- with lock:
- job.progress.messages_exported += 1
- job.progress.current_conversation_messages_exported = exported
+ is_sent = bool(msg.get("isSent"))
+ row_cls = "wce-msg-row wce-msg-row-sent flex items-center justify-end" if is_sent else "wce-msg-row wce-msg-row-received flex items-center justify-start"
+ msg_cls = "wce-msg wce-msg-sent flex items-start max-w-md flex-row-reverse" if is_sent else "wce-msg flex items-start max-w-md"
+ avatar_extra = "wce-avatar-sent ml-3" if is_sent else "wce-avatar-received mr-3"
+
+ tw.write(f'
\n')
+ tw.write(f'
\n')
+
+ avatar_src = rel_path(str(msg.get("senderAvatarPath") or "").strip())
+ display_name = str(msg.get("senderDisplayName") or "").strip()
+ fallback_char = (display_name or sender_username or "?")[:1]
+ tw.write(" " + build_avatar_html(src=avatar_src, fallback_text=fallback_char, extra_class=avatar_extra) + "\n")
+
+ align_cls = "items-end" if is_sent else "items-start"
+ tw.write(f'
\n')
+ if conv_is_group and (not is_sent) and display_name:
+ tw.write(f'
{esc_text(display_name)}
\n')
+
+ pos_cls = "right-0" if is_sent else "left-0"
+ tw.write(
+ '
{esc_text(create_time_text)}
\n'
+ )
+
+ # Message body
+ bubble_dir_cls = "bg-[#95EC69] text-black bubble-tail-r" if is_sent else "bg-white text-gray-800 bubble-tail-l"
+ bubble_base_cls = "px-3 py-2 text-sm max-w-sm relative msg-bubble whitespace-pre-wrap break-words leading-relaxed"
+ bubble_unknown_cls = (
+ "px-3 py-2 text-xs max-w-sm relative msg-bubble whitespace-pre-wrap break-words leading-relaxed text-gray-700"
+ )
+
+ if rt == "image":
+ src = offline_path(msg, "image")
+ if not src:
+ url = str(msg.get("imageUrl") or "").strip()
+ src = url if is_http_url(url) else ""
+ if src:
+ tw.write('
\n')
+ tw.write('
\n")
+ tw.write("
\n")
+ else:
+ tw.write(f'
{render_text_with_emojis(msg.get("content") or "")}
\n')
+ elif rt == "emoji":
+ src = offline_path(msg, "emoji")
+ if not src:
+ url = str(msg.get("emojiUrl") or "").strip()
+ src = url if is_http_url(url) else ""
+ if src:
+ emoji_dir = " flex-row-reverse" if is_sent else ""
+ tw.write(f'
\n')
+ tw.write(f'
\n')
+ tw.write("
\n")
+ else:
+ tw.write(f'
{render_text_with_emojis(msg.get("content") or "")}
\n')
+ elif rt == "video":
+ thumb = offline_path(msg, "video_thumb")
+ if not thumb:
+ url = str(msg.get("videoThumbUrl") or "").strip()
+ thumb = url if is_http_url(url) else ""
+ video = offline_path(msg, "video")
+ if not video:
+ url = str(msg.get("videoUrl") or "").strip()
+ video = url if is_http_url(url) else ""
+ if thumb:
+ tw.write('
\n')
+ tw.write('
\n")
+ tw.write("
\n")
+ else:
+ tw.write(f'
{render_text_with_emojis(msg.get("content") or "")}
\n')
+ elif rt == "voice":
+ voice = offline_path(msg, "voice")
+ duration_ms = msg.get("voiceLength")
+ width = get_voice_width(duration_ms)
+ seconds = get_voice_duration_in_seconds(duration_ms)
+ voice_dir_cls = "wechat-voice-sent" if is_sent else "wechat-voice-received"
+ content_dir_cls = " flex-row-reverse" if is_sent else ""
+ icon_dir_cls = "voice-icon-sent" if is_sent else "voice-icon-received"
+ voice_id = str(msg.get("id") or "").strip()
+
+ tw.write('
\n')
+ tw.write(
+ f'
\n'
+ )
+ tw.write(f'
\n')
+ tw.write(
+ f'
\n'
+ )
+ tw.write(
+ ' \n'
+ )
+ tw.write(
+ ' \n'
+ )
+ tw.write(
+ ' \n'
+ )
+ tw.write(" \n")
+ tw.write(f'
{esc_text(seconds)}" \n')
+ tw.write("
\n")
+ tw.write("
\n")
+ if voice:
+ tw.write(f'
\n')
+ tw.write("
\n")
+ elif rt == "file":
+ fsrc = offline_path(msg, "file")
+ title = str(msg.get("title") or msg.get("content") or "文件").strip()
+ size = str(msg.get("fileSize") or "").strip()
+ size_text = format_file_size(size)
+ sent_side_cls = " wechat-special-sent-side" if is_sent else ""
+ cls = f"wechat-redpacket-card wechat-special-card wechat-file-card msg-radius{sent_side_cls}"
+ tag = "a" if fsrc else "div"
+ attrs = f' href="{esc_attr(fsrc)}" download' if fsrc else ""
+ tw.write(f' <{tag}{attrs} class="{esc_attr(cls)}">\n')
+ tw.write('
\n')
+ tw.write('
\n')
+ tw.write(f' {esc_text(title or "文件")} \n')
+ if size_text:
+ tw.write(f' {esc_text(size_text)} \n')
+ tw.write("
\n")
+ tw.write(f'
\n')
+ tw.write("
\n")
+ tw.write('
\n')
+ tw.write(f'
\n')
+ tw.write("
微信电脑版 \n")
+ tw.write("
\n")
+ tw.write(f" {tag}>\n")
+ elif rt == "link":
+ url = str(msg.get("url") or "").strip()
+ safe_url = url if is_http_url(url) else ""
+ if safe_url:
+ heading = str(msg.get("title") or msg.get("content") or safe_url).strip()
+ abstract = str(msg.get("content") or "").strip()
+ preview = str(msg.get("thumbUrl") or "").strip()
+ preview_url = ""
+ if is_http_url(preview):
+ local = maybe_download_remote_image(preview)
+ preview_url = local or preview
+ variant = str(msg.get("linkStyle") or "").strip().lower()
+
+ from_text = get_link_from_text(msg, url=safe_url)
+ from_avatar_text = first_glyph(from_text) or "\u200B"
+ from_text = from_text or "\u200B"
+ sent_side_cls = " wechat-special-sent-side" if is_sent else ""
+
+ if variant == "cover":
+ cls = f"wechat-link-card-cover wechat-special-card msg-radius{sent_side_cls}"
+ tw.write(
+ f'
\n'
+ )
+ if preview_url:
+ tw.write(' \n')
+ tw.write(
+ f'
\n'
+ )
+ tw.write('
\n')
+ tw.write(
+ f'
{esc_text(from_avatar_text)}
\n'
+ )
+ tw.write(f'
{esc_text(from_text)}
\n')
+ tw.write("
\n")
+ tw.write("
\n")
+ else:
+ tw.write(' \n')
+ tw.write(
+ f'
{esc_text(from_avatar_text)}
\n'
+ )
+ tw.write(f'
{esc_text(from_text)}
\n')
+ tw.write("
\n")
+ tw.write(f' {esc_text(heading or safe_url)}
\n')
+ tw.write(" \n")
+ else:
+ cls = f"wechat-link-card wechat-special-card msg-radius{sent_side_cls}"
+ tw.write(
+ f'
\n'
+ )
+ tw.write(' \n')
+ tw.write('
\n')
+ tw.write(f'
{esc_text(heading or safe_url)}
\n')
+ if abstract:
+ tw.write(f'
{esc_text(abstract)}
\n')
+ tw.write("
\n")
+ if preview_url:
+ tw.write('
\n')
+ tw.write(
+ f'
\n'
+ )
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write(' \n')
+ tw.write(
+ f'
{esc_text(from_avatar_text)}
\n'
+ )
+ tw.write(f'
{esc_text(from_text)}
\n')
+ tw.write("
\n")
+ tw.write(" \n")
+ else:
+ tw.write(f'
{render_text_with_emojis(msg.get("content") or "")}
\n')
+ elif rt == "voip":
+ voip_dir_cls = "wechat-voip-sent" if is_sent else "wechat-voip-received"
+ content_dir_cls = " flex-row-reverse" if is_sent else ""
+ voip_type = str(msg.get("voipType") or "").strip().lower()
+ icon = "wechat-video-light.png" if voip_type == "video" else "wechat-audio-light.png"
+ tw.write(f'
\n')
+ tw.write(f'
\n')
+ tw.write(f'
\n')
+ tw.write(f'
{esc_text(msg.get("content") or "通话")} \n')
+ tw.write("
\n")
+ tw.write("
\n")
+ elif rt == "quote":
+ tw.write(
+ f'
{render_text_with_emojis(msg.get("content") or "")}
\n'
+ )
+
+ qt = str(msg.get("quoteTitle") or "").strip()
+ qc = str(msg.get("quoteContent") or "").strip()
+ qthumb = str(msg.get("quoteThumbUrl") or "").strip()
+ qtype = str(msg.get("quoteType") or "").strip()
+ qsid_raw = str(msg.get("quoteServerId") or "").strip()
+ qsid = int(qsid_raw) if qsid_raw.isdigit() else 0
+
+ def is_quoted_voice() -> bool:
+ if qtype == "34":
+ return True
+ return (qc == "[语音]") and bool(qsid_raw)
+
+ def is_quoted_image() -> bool:
+ if qtype == "3":
+ return True
+ return (qc == "[图片]") and bool(qsid_raw)
+
+ def is_quoted_link() -> bool:
+ if qtype == "49":
+ return True
+ return bool(re.match(r"^\[链接\]\s*", qc))
+
+ def get_quoted_link_text() -> str:
+ if not qc:
+ return ""
+ return re.sub(r"^\[链接\]\s*", "", qc).strip() or qc
+
+ quoted_voice = is_quoted_voice()
+ quoted_image = is_quoted_image()
+ quoted_link = is_quoted_link()
+
+ quote_voice_url = ""
+ if include_media and ("voice" in media_kinds) and quoted_voice and qsid:
+ try:
+ arc, is_new = _materialize_voice(
+ zf=zf,
+ media_db_path=media_db_path,
+ server_id=int(qsid),
+ media_written=media_written,
+ )
+ except Exception:
+ arc, is_new = "", False
+ if arc:
+ quote_voice_url = rel_path(arc)
+ if is_new:
+ with lock:
+ job.progress.media_copied += 1
+
+ quote_image_url = ""
+ if include_media and ("image" in media_kinds) and quoted_image and qsid and resource_conn is not None:
+ md5_hit = ""
+ try:
+ md5_hit = _lookup_resource_md5(
+ resource_conn,
+ resource_chat_id,
+ message_local_type=3,
+ server_id=int(qsid),
+ local_id=0,
+ create_time=0,
+ )
+ except Exception:
+ md5_hit = ""
+
+ if md5_hit:
+ try:
+ arc, is_new = _materialize_media(
+ zf=zf,
+ account_dir=account_dir,
+ conv_username=conv_username,
+ kind="image",
+ md5=str(md5_hit or "").strip().lower(),
+ file_id="",
+ media_written=media_written,
+ suggested_name="",
+ )
+ except Exception:
+ arc, is_new = "", False
+ if arc:
+ quote_image_url = rel_path(arc)
+ if is_new:
+ with lock:
+ job.progress.media_copied += 1
+
+ qthumb_url = ""
+ if is_http_url(qthumb):
+ qthumb_local = maybe_download_remote_image(qthumb) if download_remote_media else ""
+ qthumb_url = qthumb_local or qthumb
+
+ if qt or qc:
+ tw.write(
+ '
\n'
+ )
+ tw.write('
\n')
+ if quoted_voice:
+ seconds = get_voice_duration_in_seconds(msg.get("quoteVoiceLength"))
+ disabled = not bool(quote_voice_url)
+ btn_cls = "flex items-center gap-1 min-w-0 hover:opacity-80"
+ if disabled:
+ btn_cls += " opacity-60 cursor-not-allowed"
+ dis_attr = " disabled" if disabled else ""
+ tw.write('
\n')
+ if qt:
+ tw.write(f'
{esc_text(qt)}: \n')
+ tw.write(
+ f'
\n'
+ )
+ tw.write(
+ ' \n'
+ )
+ tw.write(
+ ' \n'
+ )
+ tw.write(
+ ' \n'
+ )
+ tw.write(
+ ' \n'
+ )
+ tw.write(" \n")
+ if seconds > 0:
+ tw.write(f' {esc_text(seconds)}" \n')
+ else:
+ tw.write(' 语音 \n')
+ tw.write(" \n")
+ if quote_voice_url:
+ tw.write(
+ f'
\n'
+ )
+ tw.write("
\n")
+ else:
+ tw.write('
\n')
+ if quoted_link:
+ link_text = get_quoted_link_text()
+ tw.write('
\n')
+ if qt:
+ tw.write(f' {esc_text(qt)}: \n')
+ if link_text:
+ ml = ' class="ml-1"' if qt else ""
+ tw.write(f' 🔗 {esc_text(link_text)} \n')
+ tw.write("
\n")
+ else:
+ hide_qc = quoted_image and qt and bool(quote_image_url)
+ tw.write('
\n')
+ if qt:
+ tw.write(f' {esc_text(qt)}: \n')
+ if qc and (not hide_qc):
+ ml = ' class="ml-1"' if qt else ""
+ tw.write(f' {esc_text(qc)} \n')
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write("
\n")
+
+ if quoted_link and qthumb_url:
+ tw.write(
+ f'
\n'
+ )
+ tw.write(
+ f' \n'
+ )
+ tw.write(" \n")
+
+ if (not quoted_link) and quoted_image and quote_image_url:
+ tw.write(
+ f'
\n'
+ )
+ tw.write(
+ f' \n'
+ )
+ tw.write(" \n")
+
+ tw.write("
\n")
+ elif rt == "chatHistory":
+ title = str(msg.get("title") or "").strip() or "聊天记录"
+ record_item = str(msg.get("recordItem") or "").strip()
+ record_item_b64 = ""
+ if record_item:
+ try:
+ record_item_b64 = base64.b64encode(record_item.encode("utf-8", errors="replace")).decode("ascii")
+ except Exception:
+ record_item_b64 = ""
+
+ if record_item and include_media and (not privacy_mode):
+ try:
+ for m in _CHAT_HISTORY_MD5_TAG_RE.findall(record_item):
+ _ensure_chat_history_md5(m)
+ except Exception:
+ pass
+ if resource_conn is not None:
+ try:
+ server_map = page_media_index.get("serverMd5")
+ if not isinstance(server_map, dict):
+ server_map = {}
+ page_media_index["serverMd5"] = server_map
+
+ for sid_raw in _CHAT_HISTORY_SERVER_ID_TAG_RE.findall(record_item):
+ sid_text = str(sid_raw or "").strip()
+ if not sid_text or sid_text in server_map:
+ continue
+ if (len(sid_text) > 24) or (not sid_text.isdigit()):
+ continue
+ sid = int(sid_text)
+ if sid <= 0:
+ continue
+
+ md5_hit = ""
+ try:
+ md5_hit = _lookup_resource_md5(
+ resource_conn,
+ None, # do NOT filter by chat_id: merged-forward records come from other chats
+ 0, # do NOT filter by local_type
+ int(sid),
+ 0,
+ 0,
+ )
+ except Exception:
+ md5_hit = ""
+
+ md5_hit = str(md5_hit or "").strip().lower()
+ if not _is_md5(md5_hit):
+ continue
+ if _ensure_chat_history_md5(md5_hit):
+ server_map[sid_text] = md5_hit
+ except Exception:
+ pass
+ if download_remote_media:
+ try:
+ for u in _CHAT_HISTORY_URL_TAG_RE.findall(record_item):
+ maybe_download_remote_image(u)
+ except Exception:
+ pass
+
+ lines = get_chat_history_preview_lines(msg)
+ sent_side_cls = " wechat-special-sent-side" if is_sent else ""
+ cls = f"wechat-chat-history-card wechat-special-card msg-radius{sent_side_cls} cursor-pointer"
+ tw.write(
+ f'
\n'
+ )
+ tw.write('
\n')
+ tw.write(f'
{esc_text(title)}
\n')
+ if lines:
+ tw.write('
\n')
+ for line in lines:
+ tw.write(f'
{esc_text(line)}
\n')
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write('
聊天记录
\n')
+ tw.write("
\n")
+ elif rt == "transfer":
+ received = is_transfer_received(msg)
+ returned = is_transfer_returned(msg)
+ overdue = is_transfer_overdue(msg)
+ side_cls = "wechat-transfer-sent-side" if is_sent else "wechat-transfer-received-side"
+ cls_parts = ["wechat-transfer-card", "msg-radius", side_cls]
+ if received:
+ cls_parts.append("wechat-transfer-received")
+ if returned:
+ cls_parts.append("wechat-transfer-returned")
+ if overdue:
+ cls_parts.append("wechat-transfer-overdue")
+ cls = " ".join(cls_parts)
+ if returned:
+ icon = "wechat-returned.png"
+ elif overdue:
+ icon = "overdue.png"
+ elif received:
+ icon = "wechat-trans-icon2.png"
+ else:
+ icon = "wechat-trans-icon1.png"
+ amount = format_transfer_amount(msg.get("amount"))
+ status = get_transfer_title(msg, is_sent=is_sent)
+ tw.write(f'
\n')
+ tw.write('
\n')
+ tw.write(f'
\n')
+ tw.write('
\n')
+ if amount:
+ tw.write(f' ¥{esc_text(amount)} \n')
+ tw.write(f' {esc_text(status)} \n')
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write('
微信转账
\n')
+ tw.write("
\n")
+ elif rt == "redPacket":
+ received = False
+ cls_parts = ["wechat-redpacket-card", "wechat-special-card", "msg-radius"]
+ if received:
+ cls_parts.append("wechat-redpacket-received")
+ if is_sent:
+ cls_parts.append("wechat-special-sent-side")
+ icon = "wechat-trans-icon4.png" if received else "wechat-trans-icon3.png"
+ tw.write(f'
\n')
+ tw.write('
\n')
+ tw.write(f'
\n')
+ tw.write('
\n')
+ tw.write(f' {esc_text(get_red_packet_text(msg))} \n')
+ if received:
+ tw.write(' 已领取 \n')
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write('
微信红包
\n')
+ tw.write("
\n")
+ elif rt == "text":
+ tw.write(f'
{render_text_with_emojis(msg.get("content") or "")}
\n')
+ else:
+ content = str(msg.get("content") or "").strip()
+ if not content:
+ content = f"[{str(msg.get('type') or 'unknown')}] 消息"
+ tw.write(f'
{render_text_with_emojis(content)}
\n')
+
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write("
\n")
+
+ _mark_exported()
+ if ts:
+ prev_ts = ts
if scanned % 500 == 0 and job.cancel_requested:
raise _JobCancelled()
+ if page_size > 0:
+ _close_page_fp()
+ paged_total_pages = max(1, len(page_frag_paths))
+ paged_pad_width = max(4, len(str(paged_total_pages)))
+ if page_frag_paths:
+ paged_old_page_paths = list(page_frag_paths[:-1])
+ tw.set_target(hw)
+ try:
+ tw.write(page_frag_paths[-1].read_text(encoding="utf-8"))
+ except Exception:
+ try:
+ tw.write(page_frag_paths[-1].read_text(encoding="utf-8", errors="ignore"))
+ except Exception:
+ pass
+ else:
+ paged_old_page_paths = []
+ tw.set_target(hw)
+
+ # Close message list + container
+ tw.set_target(hw)
+ tw.write("
\n")
+ tw.write("
\n")
+
+ if page_size > 0 and paged_total_pages > 1:
+ page_meta = {
+ "schemaVersion": 1,
+ "pageSize": int(page_size),
+ "totalPages": int(paged_total_pages),
+ "initialPage": int(paged_total_pages),
+ "totalMessages": int(exported),
+ "padWidth": int(paged_pad_width),
+ "pageFilePrefix": "pages/page-",
+ "pageFileSuffix": ".js",
+ "inlinedPages": [int(paged_total_pages)],
+ }
+ try:
+ page_meta_payload = json.dumps(page_meta, ensure_ascii=False)
+ except Exception:
+ page_meta_payload = "{}"
+ page_meta_payload = page_meta_payload.replace("", "<\\/")
+ tw.write(f'\n')
+
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write("
\n")
+ tw.write(" \n")
+
+ try:
+ media_index_payload = json.dumps(page_media_index, ensure_ascii=False)
+ except Exception:
+ media_index_payload = "{}"
+ media_index_payload = media_index_payload.replace("", "<\\/")
+ tw.write(f'\n')
+
+ tw.write("\n")
+ tw.write("\n")
tw.flush()
zf.write(str(tmp_path), arcname)
- if contact_conn is not None:
- try:
- contact_conn.close()
- except Exception:
- pass
+
+ if page_size > 0 and paged_old_page_paths:
+ for page_no, frag_path in enumerate(paged_old_page_paths, start=1):
+ try:
+ frag_text = frag_path.read_text(encoding="utf-8")
+ except Exception:
+ try:
+ frag_text = frag_path.read_text(encoding="utf-8", errors="ignore")
+ except Exception:
+ frag_text = ""
+
+ try:
+ frag_json = json.dumps(frag_text, ensure_ascii=False)
+ except Exception:
+ frag_json = json.dumps("", ensure_ascii=False)
+
+ num = str(page_no).zfill(int(paged_pad_width or 4))
+ arc_js = f"{conv_dir}/pages/page-{num}.js"
+ js_payload = (
+ "(() => {\n"
+ f" const pageNo = {int(page_no)};\n"
+ f" const html = {frag_json};\n"
+ " try {\n"
+ " const fn = window.__WCE_PAGE_LOADED__;\n"
+ " if (typeof fn === 'function') fn(pageNo, html);\n"
+ " else {\n"
+ " const q = (window.__WCE_PAGE_QUEUE__ = window.__WCE_PAGE_QUEUE__ || []);\n"
+ " q.push([pageNo, html]);\n"
+ " }\n"
+ " } catch {}\n"
+ "})();\n"
+ )
+ zf.writestr(arc_js, js_payload)
return exported
@@ -1666,9 +5698,16 @@ def _privacy_scrub_message(
for k in (
"title",
"url",
+ "from",
+ "fromUsername",
+ "linkType",
+ "linkStyle",
"thumbUrl",
+ "recordItem",
"imageMd5",
"imageFileId",
+ "imageMd5Candidates",
+ "imageFileIdCandidates",
"imageUrl",
"emojiMd5",
"emojiUrl",
@@ -1679,6 +5718,11 @@ def _privacy_scrub_message(
"videoUrl",
"videoThumbUrl",
"voiceLength",
+ "quoteUsername",
+ "quoteServerId",
+ "quoteType",
+ "quoteThumbUrl",
+ "quoteVoiceLength",
"quoteTitle",
"quoteContent",
"amount",
@@ -1733,25 +5777,88 @@ def record_missing(kind: str, ident: str) -> None:
offline: list[dict[str, Any]] = []
if rt == "image" and "image" in media_kinds:
- md5 = str(msg.get("imageMd5") or "").strip().lower()
- file_id = str(msg.get("imageFileId") or "").strip()
- arc, is_new = _materialize_media(
- zf=zf,
- account_dir=account_dir,
- conv_username=conv_username,
- kind="image",
- md5=md5 if _is_md5(md5) else "",
- file_id=file_id,
- media_written=media_written,
- suggested_name="",
- )
+ primary_md5 = str(msg.get("imageMd5") or "").strip().lower()
+ primary_file_id = str(msg.get("imageFileId") or "").strip()
+
+ md5_candidates_raw = msg.get("imageMd5Candidates") or []
+ file_id_candidates_raw = msg.get("imageFileIdCandidates") or []
+ md5_candidates = md5_candidates_raw if isinstance(md5_candidates_raw, list) else []
+ file_id_candidates = file_id_candidates_raw if isinstance(file_id_candidates_raw, list) else []
+
+ md5s: list[str] = []
+ file_ids: list[str] = []
+
+ def add_md5(v: Any) -> None:
+ s = str(v or "").strip().lower()
+ if _is_md5(s) and s not in md5s:
+ md5s.append(s)
+
+ def add_file_id(v: Any) -> None:
+ s = str(v or "").strip()
+ if s and s not in file_ids:
+ file_ids.append(s)
+
+ add_md5(primary_md5)
+ for v in md5_candidates:
+ add_md5(v)
+
+ add_file_id(primary_file_id)
+ for v in file_id_candidates:
+ add_file_id(v)
+
+ arc = ""
+ is_new = False
+ used_md5 = ""
+ used_file_id = ""
+
+ # Prefer md5-based resolution first (more reliable), then fall back to file_id search.
+ for md5 in md5s:
+ arc, is_new = _materialize_media(
+ zf=zf,
+ account_dir=account_dir,
+ conv_username=conv_username,
+ kind="image",
+ md5=md5,
+ file_id="",
+ media_written=media_written,
+ suggested_name="",
+ )
+ if arc:
+ used_md5 = md5
+ break
+
+ if not arc:
+ for file_id in file_ids:
+ arc, is_new = _materialize_media(
+ zf=zf,
+ account_dir=account_dir,
+ conv_username=conv_username,
+ kind="image",
+ md5="",
+ file_id=file_id,
+ media_written=media_written,
+ suggested_name="",
+ )
+ if arc:
+ used_file_id = file_id
+ break
+
if arc:
- offline.append({"kind": "image", "path": arc, "md5": md5, "fileId": file_id})
+ # Keep primary fields in sync with what actually resolved.
+ try:
+ if used_md5:
+ msg["imageMd5"] = used_md5
+ if used_file_id:
+ msg["imageFileId"] = used_file_id
+ except Exception:
+ pass
+
+ offline.append({"kind": "image", "path": arc, "md5": used_md5 or primary_md5, "fileId": used_file_id or primary_file_id})
if is_new:
with lock:
job.progress.media_copied += 1
else:
- record_missing("image", md5 or file_id)
+ record_missing("image", primary_md5 or primary_file_id)
if rt == "emoji" and "emoji" in media_kinds:
md5 = str(msg.get("emojiMd5") or "").strip().lower()
@@ -1951,13 +6058,9 @@ def _materialize_voice(
if not isinstance(data, (bytes, bytearray)):
data = bytes(data)
- wav = _convert_silk_to_wav(data)
- if wav != data and wav[:4] == b"RIFF":
- ext = "wav"
- payload = wav
- else:
- ext = "silk"
- payload = data
+ payload, ext, _media_type = _convert_silk_to_browser_audio(data, preferred_format="mp3")
+ if not payload:
+ return "", False
arc = f"media/voices/voice_{int(server_id)}.{ext}"
zf.writestr(arc, payload)
@@ -2026,20 +6129,27 @@ def _materialize_media(
except Exception:
return "", False
+ try:
+ with open(src, "rb") as f:
+ head = f.read(64)
+ except Exception:
+ head = b""
+
+ head_mt = _detect_image_media_type(head[:32])
+ looks_like_mp4 = len(head) >= 8 and head[4:8] == b"ftyp"
+
ext = src.suffix.lstrip(".").lower()
if not ext:
- try:
- head = src.read_bytes()[:32]
- except Exception:
- head = b""
- mt = _detect_image_media_type(head)
- if mt.startswith("image/"):
- ext = mt.split("/", 1)[-1]
- elif len(head) >= 8 and head[4:8] == b"ftyp":
+ if head_mt.startswith("image/"):
+ ext = head_mt.split("/", 1)[-1]
+ elif looks_like_mp4:
ext = "mp4"
else:
ext = "dat"
+ if ext == "jpeg":
+ ext = "jpg"
+
folder = "misc"
if kind == "image":
folder = "images"
@@ -2061,10 +6171,62 @@ def _materialize_media(
arc_name = arc_name[:160]
arc = f"media/{folder}/{arc_name}"
- try:
- zf.write(src, arcname=arc)
- except Exception:
- return "", False
+ should_stream_copy = False
+ if kind == "file":
+ should_stream_copy = True
+ elif kind in {"image", "emoji", "video_thumb"}:
+ should_stream_copy = (
+ (ext == "jpg" and head_mt == "image/jpeg")
+ or (ext == "png" and head_mt == "image/png")
+ or (ext == "gif" and head_mt == "image/gif")
+ or (ext == "webp" and head_mt == "image/webp")
+ )
+ elif kind == "video":
+ should_stream_copy = ext == "mp4" and looks_like_mp4
+
+ if should_stream_copy or (kind not in {"image", "emoji", "video", "video_thumb"}):
+ try:
+ zf.write(src, arcname=arc)
+ except Exception:
+ return "", False
+ else:
+ try:
+ data, mt = _read_and_maybe_decrypt_media(src, account_dir=account_dir)
+ except Exception:
+ try:
+ zf.write(src, arcname=arc)
+ except Exception:
+ return "", False
+ media_written[key] = arc
+ return arc, True
+
+ mt = str(mt or "").strip()
+ if mt == "image/png":
+ ext2 = "png"
+ elif mt == "image/jpeg":
+ ext2 = "jpg"
+ elif mt == "image/gif":
+ ext2 = "gif"
+ elif mt == "image/webp":
+ ext2 = "webp"
+ elif mt == "video/mp4":
+ ext2 = "mp4"
+ else:
+ ext2 = "dat" if mt == "application/octet-stream" else (ext or "dat")
+
+ if ext2 != ext:
+ if nice and kind == "file":
+ arc_name = f"{nice}_{ident}.{ext2}" if ext2 else f"{nice}_{ident}"
+ else:
+ arc_name = f"{ident}.{ext2}" if ext2 else ident
+ if len(arc_name) > 160:
+ arc_name = arc_name[:160]
+ arc = f"media/{folder}/{arc_name}"
+
+ try:
+ zf.writestr(arc, data)
+ except Exception:
+ return "", False
media_written[key] = arc
return arc, True
diff --git a/src/wechat_decrypt_tool/chat_helpers.py b/src/wechat_decrypt_tool/chat_helpers.py
index 57f44c2..603bb81 100644
--- a/src/wechat_decrypt_tool/chat_helpers.py
+++ b/src/wechat_decrypt_tool/chat_helpers.py
@@ -7,8 +7,8 @@
from collections import Counter
from datetime import datetime
from pathlib import Path
-from typing import Any, Optional
-from urllib.parse import quote
+from typing import Any, Callable, Optional
+from urllib.parse import parse_qs, quote, urlparse
from fastapi import HTTPException
@@ -24,6 +24,17 @@
_OUTPUT_DATABASES_DIR = get_output_databases_dir()
_DEBUG_SESSIONS = os.environ.get("WECHAT_TOOL_DEBUG_SESSIONS", "0") == "1"
+_SQLITE_HEADER = b"SQLite format 3\x00"
+
+
+def _is_valid_decrypted_sqlite(path: Path) -> bool:
+ try:
+ if not path.exists() or (not path.is_file()):
+ return False
+ with path.open("rb") as f:
+ return f.read(len(_SQLITE_HEADER)) == _SQLITE_HEADER
+ except Exception:
+ return False
def _list_decrypted_accounts() -> list[str]:
@@ -34,7 +45,7 @@ def _list_decrypted_accounts() -> list[str]:
for p in _OUTPUT_DATABASES_DIR.iterdir():
if not p.is_dir():
continue
- if (p / "session.db").exists() and (p / "contact.db").exists():
+ if _is_valid_decrypted_sqlite(p / "session.db") and _is_valid_decrypted_sqlite(p / "contact.db"):
accounts.append(p.name)
accounts.sort()
@@ -49,7 +60,9 @@ def _resolve_account_dir(account: Optional[str]) -> Path:
detail="No decrypted databases found. Please decrypt first.",
)
- selected = account or accounts[0]
+ selected = str(account or "").strip() or accounts[0]
+ if selected not in accounts:
+ raise HTTPException(status_code=404, detail="Account not found.")
base = _OUTPUT_DATABASES_DIR.resolve()
candidate = (_OUTPUT_DATABASES_DIR / selected).resolve()
@@ -618,6 +631,73 @@ def _normalize_xml_url(url: str) -> str:
return u.replace("&", "&").strip()
+def _is_mp_weixin_article_url(url: str) -> bool:
+ u = str(url or "").strip()
+ if not u:
+ return False
+
+ try:
+ host = str(urlparse(u).hostname or "").strip().lower()
+ if host == "mp.weixin.qq.com" or host.endswith(".mp.weixin.qq.com"):
+ return True
+ except Exception:
+ pass
+
+ lu = u.lower()
+ return "mp.weixin.qq.com/" in lu
+
+
+def _is_mp_weixin_feed_article_url(url: str) -> bool:
+ """Detect WeChat's PC feed/recommendation mp.weixin.qq.com share URLs.
+
+ These links often carry an `exptype` like:
+ masonry_feed_brief_content_elite_for_pcfeeds_u2i
+
+ WeChat desktop tends to render them in a cover-card style (image + bottom title),
+ so we use this as a hint to choose the 'cover' linkStyle.
+ """
+
+ u = str(url or "").strip()
+ if not u:
+ return False
+
+ try:
+ parsed = urlparse(u)
+ q = parse_qs(parsed.query or "")
+ for v in (q.get("exptype") or []):
+ if "masonry_feed" in str(v or "").lower():
+ return True
+ except Exception:
+ pass
+
+ return "exptype=masonry_feed" in u.lower()
+
+
+def _classify_link_share(*, app_type: int, url: str, source_username: str, desc: str) -> tuple[str, str]:
+ src = str(source_username or "").strip().lower()
+ is_official_article = bool(
+ app_type in (5, 68)
+ and (_is_mp_weixin_article_url(url) or src.startswith("gh_"))
+ )
+
+ link_type = "official_article" if is_official_article else "web_link"
+
+ d = str(desc or "").strip()
+ hashtag_count = len(re.findall(r"#[^#\s]+", d))
+
+ # 公众号文章中「封面图 + 底栏标题」卡片特征:摘要以 #话题# 风格为主。
+ cover_like = bool(
+ is_official_article
+ and (
+ d.startswith("#")
+ or hashtag_count >= 2
+ or _is_mp_weixin_feed_article_url(url)
+ )
+ )
+ link_style = "cover" if cover_like else "default"
+ return link_type, link_style
+
+
def _extract_xml_tag_text(xml_text: str, tag: str) -> str:
if not xml_text or not tag:
return ""
@@ -645,6 +725,215 @@ def _extract_xml_tag_or_attr(xml_text: str, name: str) -> str:
return _extract_xml_attr(xml_text, name)
+def _parse_location_message(text: str) -> dict[str, Any]:
+ raw = html.unescape(str(text or "").strip())
+
+ def _clean(value: Any) -> str:
+ candidate = _strip_cdata(str(value or "").strip())
+ if not candidate:
+ return ""
+ candidate = html.unescape(candidate)
+ candidate = re.sub(r"\s+", " ", candidate).strip()
+ return candidate
+
+ def _to_float(value: Any) -> Optional[float]:
+ s = str(value or "").strip()
+ if not s:
+ return None
+ try:
+ num = float(s)
+ except Exception:
+ return None
+ if not (-180.0 <= num <= 180.0):
+ return None
+ return num
+
+ poiname = _clean(
+ _extract_xml_tag_or_attr(raw, "poiname")
+ or _extract_xml_tag_or_attr(raw, "poiName")
+ or _extract_xml_tag_or_attr(raw, "name")
+ )
+ label = _clean(
+ _extract_xml_tag_or_attr(raw, "label")
+ or _extract_xml_tag_or_attr(raw, "labelname")
+ or _extract_xml_tag_or_attr(raw, "address")
+ )
+
+ lat = _to_float(
+ _extract_xml_tag_or_attr(raw, "x")
+ or _extract_xml_tag_or_attr(raw, "latitude")
+ or _extract_xml_tag_or_attr(raw, "lat")
+ )
+ lng = _to_float(
+ _extract_xml_tag_or_attr(raw, "y")
+ or _extract_xml_tag_or_attr(raw, "longitude")
+ or _extract_xml_tag_or_attr(raw, "lng")
+ or _extract_xml_tag_or_attr(raw, "lon")
+ )
+
+ if lat is not None and not (-90.0 <= lat <= 90.0):
+ lat = None
+ if lng is not None and not (-180.0 <= lng <= 180.0):
+ lng = None
+
+ title = poiname or label or "位置"
+ return {
+ "renderType": "location",
+ "content": title or "[Location]",
+ "locationLat": lat,
+ "locationLng": lng,
+ "locationPoiname": poiname,
+ "locationLabel": label,
+ }
+
+
+def _extract_chatroom_top_message_metadata(raw_text: str) -> dict[str, str]:
+ text = str(raw_text or "").strip()
+ if not text:
+ return {}
+
+ lower_text = text.lower()
+ if "