diff --git a/echotrace b/echotrace new file mode 160000 index 0000000..98280f0 --- /dev/null +++ b/echotrace @@ -0,0 +1 @@ +Subproject commit 98280f0d0d7b764b2f2027fc4d103024e8ed30fd diff --git a/electron/imageSearchWorker.ts b/electron/imageSearchWorker.ts index 2f99546..56826a2 100644 --- a/electron/imageSearchWorker.ts +++ b/electron/imageSearchWorker.ts @@ -62,6 +62,12 @@ function isThumbnailDat(fileName: string): boolean { return fileName.includes('.t.dat') || fileName.includes('_t.dat') } +function isHdDat(fileName: string): boolean { + const lower = fileName.toLowerCase() + const base = lower.endsWith('.dat') ? lower.slice(0, -4) : lower + return base.endsWith('_hd') || base.endsWith('_h') +} + function walkForDat( root: string, datName: string, @@ -101,6 +107,8 @@ function walkForDat( if (!isLikelyImageDatBase(baseLower)) continue if (!hasXVariant(baseLower)) continue if (!matchesDatName(lower, datName)) continue + // 排除高清图片格式 (_hd, _h) + if (isHdDat(lower)) continue matchedBases.add(baseLower) const isThumb = isThumbnailDat(lower) if (!allowThumbnail && isThumb) continue diff --git a/electron/main.ts b/electron/main.ts index a5a2001..84332c9 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -382,6 +382,8 @@ function registerIpcHandlers() { return true }) + + // 聊天相关 ipcMain.handle('chat:connect', async () => { return chatService.connect() @@ -468,8 +470,8 @@ function registerIpcHandlers() { }) // 数据分析相关 - ipcMain.handle('analytics:getOverallStatistics', async () => { - return analyticsService.getOverallStatistics() + ipcMain.handle('analytics:getOverallStatistics', async (_, force?: boolean) => { + return analyticsService.getOverallStatistics(force) }) ipcMain.handle('analytics:getContactRankings', async (_, limit?: number) => { diff --git a/electron/preload.ts b/electron/preload.ts index 1b9361e..897d9b7 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -69,7 +69,8 @@ contextBridge.exposeInMainWorld('electronAPI', { ipcRenderer.invoke('wcdb:testConnection', dbPath, hexKey, wxid), open: (dbPath: string, hexKey: string, wxid: string) => ipcRenderer.invoke('wcdb:open', dbPath, hexKey, wxid), - close: () => ipcRenderer.invoke('wcdb:close') + close: () => ipcRenderer.invoke('wcdb:close'), + }, // 密钥获取 diff --git a/electron/services/analyticsService.ts b/electron/services/analyticsService.ts index a033fb6..9e83f64 100644 --- a/electron/services/analyticsService.ts +++ b/electron/services/analyticsService.ts @@ -1,5 +1,8 @@ import { ConfigService } from './config' import { wcdbService } from './wcdbService' +import { join } from 'path' +import { readFile, writeFile } from 'fs/promises' +import { app } from 'electron' export interface ChatStatistics { totalMessages: number @@ -253,15 +256,31 @@ class AnalyticsService { sessionIds: string[], beginTimestamp = 0, endTimestamp = 0, - window?: any + window?: any, + force = false ): Promise<{ success: boolean; data?: any; source?: string; error?: string }> { const cacheKey = this.buildAggregateCacheKey(sessionIds, beginTimestamp, endTimestamp) - if (this.aggregateCache && this.aggregateCache.key === cacheKey) { + + if (force) { + if (this.aggregateCache) this.aggregateCache = null + if (this.fallbackAggregateCache) this.fallbackAggregateCache = null + } + + if (!force && this.aggregateCache && this.aggregateCache.key === cacheKey) { if (Date.now() - this.aggregateCache.updatedAt < 5 * 60 * 1000) { return { success: true, data: this.aggregateCache.data, source: 'cache' } } } + // 尝试从文件加载缓存 + if (!force) { + const fileCache = await this.loadCacheFromFile() + if (fileCache && fileCache.key === cacheKey) { + this.aggregateCache = fileCache + return { success: true, data: fileCache.data, source: 'file-cache' } + } + } + if (this.aggregatePromise && this.aggregatePromise.key === cacheKey) { return this.aggregatePromise.promise } @@ -291,7 +310,12 @@ class AnalyticsService { this.aggregatePromise = { key: cacheKey, promise } try { - return await promise + const result = await promise + // 如果计算成功,同时写入此文件缓存 + if (result.success && result.data && result.source !== 'cache') { + this.saveCacheToFile({ key: cacheKey, data: this.aggregateCache?.data, updatedAt: Date.now() }) + } + return result } finally { if (this.aggregatePromise && this.aggregatePromise.key === cacheKey) { this.aggregatePromise = null @@ -299,6 +323,25 @@ class AnalyticsService { } } + private getCacheFilePath(): string { + return join(app.getPath('userData'), 'analytics_cache.json') + } + + private async loadCacheFromFile(): Promise<{ key: string; data: any; updatedAt: number } | null> { + try { + const raw = await readFile(this.getCacheFilePath(), 'utf-8') + return JSON.parse(raw) + } catch { return null } + } + + private async saveCacheToFile(data: any) { + try { + await writeFile(this.getCacheFilePath(), JSON.stringify(data)) + } catch (e) { + console.error('保存统计缓存失败:', e) + } + } + private normalizeAggregateSessions( sessions: Record | undefined, idMap: Record | undefined @@ -326,7 +369,7 @@ class AnalyticsService { void results } - async getOverallStatistics(): Promise<{ success: boolean; data?: ChatStatistics; error?: string }> { + async getOverallStatistics(force = false): Promise<{ success: boolean; data?: ChatStatistics; error?: string }> { try { const conn = await this.ensureConnected() if (!conn.success || !conn.cleanedWxid) return { success: false, error: conn.error } @@ -340,7 +383,7 @@ class AnalyticsService { const win = BrowserWindow.getAllWindows()[0] this.setProgress(win, '正在执行原生数据聚合...', 30) - const result = await this.getAggregateWithFallback(sessionInfo.usernames, 0, 0, win) + const result = await this.getAggregateWithFallback(sessionInfo.usernames, 0, 0, win, force) if (!result.success || !result.data) { return { success: false, error: result.error || '聚合统计失败' } @@ -458,8 +501,8 @@ class AnalyticsService { const d = result.data - // SQLite strftime('%w') 返回 0=Sun, 1=Mon...6=Sat - // 前端期望 1=Mon...7=Sun + // SQLite strftime('%w') 返回 0=周日, 1=周一...6=周六 + // 前端期望 1=周一...7=周日 const weekdayDistribution: Record = {} for (const [w, count] of Object.entries(d.weekday)) { const sqliteW = parseInt(w, 10) diff --git a/electron/services/chatService.ts b/electron/services/chatService.ts index 9313233..17cabfe 100644 --- a/electron/services/chatService.ts +++ b/electron/services/chatService.ts @@ -667,24 +667,24 @@ class ChatService { const messages: Message[] = [] for (const row of rows) { - const content = this.decodeMessageContent( - this.getRowField(row, [ - 'message_content', - 'messageContent', - 'content', - 'msg_content', - 'msgContent', - 'WCDB_CT_message_content', - 'WCDB_CT_messageContent' - ]), - this.getRowField(row, [ - 'compress_content', - 'compressContent', - 'compressed_content', - 'WCDB_CT_compress_content', - 'WCDB_CT_compressContent' - ]) - ) + const rawMessageContent = this.getRowField(row, [ + 'message_content', + 'messageContent', + 'content', + 'msg_content', + 'msgContent', + 'WCDB_CT_message_content', + 'WCDB_CT_messageContent' + ]); + const rawCompressContent = this.getRowField(row, [ + 'compress_content', + 'compressContent', + 'compressed_content', + 'WCDB_CT_compress_content', + 'WCDB_CT_compressContent' + ]); + + const content = this.decodeMessageContent(rawMessageContent, rawCompressContent); const localType = this.getRowInt(row, ['local_type', 'localType', 'type', 'msg_type', 'msgType', 'WCDB_CT_local_type'], 1) const isSendRaw = this.getRowField(row, ['computed_is_send', 'computedIsSend', 'is_send', 'isSend', 'WCDB_CT_is_send']) let isSend = isSendRaw === null ? null : parseInt(isSendRaw, 10) @@ -696,6 +696,16 @@ class ChatService { const expectedIsSend = (senderLower === myWxidLower || senderLower === cleanedWxidLower) ? 1 : 0 if (isSend === null) { isSend = expectedIsSend + // [DEBUG] Issue #34: 记录 isSend 推断过程 + if (expectedIsSend === 0 && localType === 1) { + // 仅在被判为接收且是文本消息时记录,避免刷屏 + // console.log(`[ChatService] inferred isSend=0: sender=${senderUsername}, myWxid=${myWxid} (cleaned=${cleanedWxid})`) + } + } + } else if (senderUsername && !myWxid) { + // [DEBUG] Issue #34: 未配置 myWxid,无法判断是否发送 + if (messages.length < 5) { + console.warn(`[ChatService] Warning: myWxid not set. Cannot determine if message is sent by me. sender=${senderUsername}`) } } @@ -1481,9 +1491,9 @@ class ChatService { */ private decodeMessageContent(messageContent: any, compressContent: any): string { // 优先使用 compress_content - let content = this.decodeMaybeCompressed(compressContent) + let content = this.decodeMaybeCompressed(compressContent, 'compress_content') if (!content || content.length === 0) { - content = this.decodeMaybeCompressed(messageContent) + content = this.decodeMaybeCompressed(messageContent, 'message_content') } return content } @@ -1491,12 +1501,14 @@ class ChatService { /** * 尝试解码可能压缩的内容 */ - private decodeMaybeCompressed(raw: any): string { + private decodeMaybeCompressed(raw: any, fieldName: string = 'unknown'): string { if (!raw) return '' + // console.log(`[ChatService] Decoding ${fieldName}: type=${typeof raw}`, raw) + // 如果是 Buffer/Uint8Array if (Buffer.isBuffer(raw) || raw instanceof Uint8Array) { - return this.decodeBinaryContent(Buffer.from(raw)) + return this.decodeBinaryContent(Buffer.from(raw), String(raw)) } // 如果是字符串 @@ -1507,7 +1519,9 @@ class ChatService { if (this.looksLikeHex(raw)) { const bytes = Buffer.from(raw, 'hex') if (bytes.length > 0) { - return this.decodeBinaryContent(bytes) + const result = this.decodeBinaryContent(bytes, raw) + // console.log(`[ChatService] HEX decoded result: ${result}`) + return result } } @@ -1515,7 +1529,7 @@ class ChatService { if (this.looksLikeBase64(raw)) { try { const bytes = Buffer.from(raw, 'base64') - return this.decodeBinaryContent(bytes) + return this.decodeBinaryContent(bytes, raw) } catch { } } @@ -1529,7 +1543,7 @@ class ChatService { /** * 解码二进制内容(处理 zstd 压缩) */ - private decodeBinaryContent(data: Buffer): string { + private decodeBinaryContent(data: Buffer, fallbackValue?: string): string { if (data.length === 0) return '' try { @@ -1556,10 +1570,16 @@ class ChatService { return decoded.replace(/\uFFFD/g, '') } + // 如果提供了 fallbackValue,且解码结果看起来像二进制垃圾,则返回 fallbackValue + if (fallbackValue && replacementCount > 0) { + // console.log(`[ChatService] Binary garbage detected, using fallback: ${fallbackValue}`) + return fallbackValue + } + // 尝试 latin1 解码 return data.toString('latin1') } catch { - return '' + return fallbackValue || '' } } diff --git a/electron/services/exportService.ts b/electron/services/exportService.ts index 4a5550d..b022107 100644 --- a/electron/services/exportService.ts +++ b/electron/services/exportService.ts @@ -459,7 +459,7 @@ class ExportService { const [displayNames, avatarUrls] = await Promise.all([ wcdbService.getDisplayNames(Array.from(lookupUsernames)), - includeAvatars ? wcdbService.getAvatarUrls(Array.from(lookupUsernames)) : Promise.resolve({ success: true, map: {} }) + includeAvatars ? wcdbService.getAvatarUrls(Array.from(lookupUsernames)) : Promise.resolve({ success: true, map: {} as Record }) ]) for (const member of rawMembers) { @@ -590,7 +590,11 @@ class ExportService { } } if (!data) continue - const finalMime = mime || this.inferImageMime(fileInfo.ext) + + // 优先使用内容检测出的 MIME 类型 + const detectedMime = this.detectMimeType(data) + const finalMime = detectedMime || mime || this.inferImageMime(fileInfo.ext) + const base64 = data.toString('base64') result.set(member.username, `data:${finalMime};base64,${base64}`) } catch { @@ -601,6 +605,39 @@ class ExportService { return result } + private detectMimeType(buffer: Buffer): string | null { + if (buffer.length < 4) return null + + // PNG: 89 50 4E 47 + if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4E && buffer[3] === 0x47) { + return 'image/png' + } + + // JPEG: FF D8 FF + if (buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) { + return 'image/jpeg' + } + + // GIF: 47 49 46 38 + if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x38) { + return 'image/gif' + } + + // WEBP: RIFF ... WEBP + if (buffer.length >= 12 && + buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46 && + buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) { + return 'image/webp' + } + + // BMP: 42 4D + if (buffer[0] === 0x42 && buffer[1] === 0x4D) { + return 'image/bmp' + } + + return null + } + private inferImageMime(ext: string): string { switch (ext.toLowerCase()) { case '.png': @@ -659,7 +696,8 @@ class ExportService { const chatLabMessages: ChatLabMessage[] = allMessages.map((msg) => { const memberInfo = collected.memberSet.get(msg.senderUsername)?.member || { platformId: msg.senderUsername, - accountName: msg.senderUsername + accountName: msg.senderUsername, + groupNickname: undefined } return { sender: msg.senderUsername, @@ -811,7 +849,8 @@ class ExportService { displayName: sessionInfo.displayName, type: isGroup ? '群聊' : '私聊', lastTimestamp: collected.lastTime, - messageCount: allMessages.length + messageCount: allMessages.length, + avatar: undefined as string | undefined }, messages: allMessages } diff --git a/electron/services/groupAnalyticsService.ts b/electron/services/groupAnalyticsService.ts index 9758a7b..628e0bb 100644 --- a/electron/services/groupAnalyticsService.ts +++ b/electron/services/groupAnalyticsService.ts @@ -181,6 +181,8 @@ class GroupAnalyticsService { } } + + async getGroupActiveHours(chatroomId: string, startTime?: number, endTime?: number): Promise<{ success: boolean; data?: GroupActiveHours; error?: string }> { try { const conn = await this.ensureConnected() diff --git a/electron/services/imageDecryptService.ts b/electron/services/imageDecryptService.ts index 6797270..585b44e 100644 --- a/electron/services/imageDecryptService.ts +++ b/electron/services/imageDecryptService.ts @@ -1,14 +1,14 @@ import { app, BrowserWindow } from 'electron' import { basename, dirname, extname, join } from 'path' import { pathToFileURL } from 'url' -import { existsSync, mkdirSync, readdirSync, readFileSync, statSync } from 'fs' +import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, appendFileSync } from 'fs' import { writeFile } from 'fs/promises' import crypto from 'crypto' import { Worker } from 'worker_threads' import { ConfigService } from './config' import { wcdbService } from './wcdbService' -type DecryptResult = { +type DecryptResult = { success: boolean localPath?: string error?: string @@ -32,11 +32,45 @@ export class ImageDecryptService { private logInfo(message: string, meta?: Record): void { if (!this.configService.get('logEnabled')) return + const timestamp = new Date().toISOString() + const metaStr = meta ? ` ${JSON.stringify(meta)}` : '' + const logLine = `[${timestamp}] [ImageDecrypt] ${message}${metaStr}\n` + + // 同时输出到控制台 if (meta) { console.info(message, meta) } else { console.info(message) } + + // 写入日志文件 + this.writeLog(logLine) + } + + private logError(message: string, error?: unknown, meta?: Record): void { + if (!this.configService.get('logEnabled')) return + const timestamp = new Date().toISOString() + const errorStr = error ? ` Error: ${String(error)}` : '' + const metaStr = meta ? ` ${JSON.stringify(meta)}` : '' + const logLine = `[${timestamp}] [ImageDecrypt] ERROR: ${message}${errorStr}${metaStr}\n` + + // 同时输出到控制台 + console.error(message, error, meta) + + // 写入日志文件 + this.writeLog(logLine) + } + + private writeLog(line: string): void { + try { + const logDir = join(app.getPath('userData'), 'logs') + if (!existsSync(logDir)) { + mkdirSync(logDir, { recursive: true }) + } + appendFileSync(join(logDir, 'wcdb.log'), line, { encoding: 'utf8' }) + } catch (err) { + console.error('写入日志失败:', err) + } } async resolveCachedImage(payload: { sessionId?: string; imageMd5?: string; imageDatName?: string }): Promise { @@ -49,6 +83,7 @@ export class ImageDecryptService { for (const key of cacheKeys) { const cached = this.resolvedCache.get(key) if (cached && existsSync(cached) && this.isImageFile(cached)) { + this.logInfo('缓存命中(从Map)', { key, path: cached, isThumb: this.isThumbnailPath(cached) }) const dataUrl = this.fileToDataUrl(cached) const isThumb = this.isThumbnailPath(cached) const hasUpdate = isThumb ? (this.updateFlags.get(key) ?? false) : false @@ -68,6 +103,7 @@ export class ImageDecryptService { for (const key of cacheKeys) { const existing = this.findCachedOutput(key, false, payload.sessionId) if (existing) { + this.logInfo('缓存命中(文件系统)', { key, path: existing, isThumb: this.isThumbnailPath(existing) }) this.cacheResolvedPaths(key, payload.imageMd5, payload.imageDatName, existing) const dataUrl = this.fileToDataUrl(existing) const isThumb = this.isThumbnailPath(existing) @@ -81,6 +117,7 @@ export class ImageDecryptService { return { success: true, localPath: dataUrl || this.filePathToUrl(existing), hasUpdate } } } + this.logInfo('未找到缓存', { md5: payload.imageMd5, datName: payload.imageDatName }) return { success: false, error: '未找到缓存图片' } } @@ -120,15 +157,18 @@ export class ImageDecryptService { payload: { sessionId?: string; imageMd5?: string; imageDatName?: string; force?: boolean }, cacheKey: string ): Promise { + this.logInfo('开始解密图片', { md5: payload.imageMd5, datName: payload.imageDatName, force: payload.force }) try { const wxid = this.configService.get('myWxid') const dbPath = this.configService.get('dbPath') if (!wxid || !dbPath) { + this.logError('配置缺失', undefined, { wxid: !!wxid, dbPath: !!dbPath }) return { success: false, error: '未配置账号或数据库路径' } } const accountDir = this.resolveAccountDir(dbPath, wxid) if (!accountDir) { + this.logError('未找到账号目录', undefined, { dbPath, wxid }) return { success: false, error: '未找到账号目录' } } @@ -139,15 +179,19 @@ export class ImageDecryptService { payload.sessionId, { allowThumbnail: !payload.force, skipResolvedCache: Boolean(payload.force) } ) - + // 如果要求高清图但没找到,直接返回提示 if (!datPath && payload.force) { + this.logError('未找到高清图', undefined, { md5: payload.imageMd5, datName: payload.imageDatName }) return { success: false, error: '未找到高清图,请在微信中点开该图片查看后重试' } } if (!datPath) { + this.logError('未找到DAT文件', undefined, { md5: payload.imageMd5, datName: payload.imageDatName }) return { success: false, error: '未找到图片文件' } } + this.logInfo('找到DAT文件', { datPath }) + if (!extname(datPath).toLowerCase().includes('dat')) { this.cacheResolvedPaths(cacheKey, payload.imageMd5, payload.imageDatName, datPath) const dataUrl = this.fileToDataUrl(datPath) @@ -160,6 +204,7 @@ export class ImageDecryptService { // 查找已缓存的解密文件 const existing = this.findCachedOutput(cacheKey, payload.force, payload.sessionId) if (existing) { + this.logInfo('找到已解密文件', { existing, isHd: this.isHdPath(existing) }) const isHd = this.isHdPath(existing) // 如果要求高清但找到的是缩略图,继续解密高清图 if (!(payload.force && !isHd)) { @@ -192,13 +237,15 @@ export class ImageDecryptService { const aesKeyRaw = this.configService.get('imageAesKey') const aesKey = this.resolveAesKey(aesKeyRaw) + this.logInfo('开始解密DAT文件', { datPath, xorKey, hasAesKey: !!aesKey }) const decrypted = await this.decryptDatAuto(datPath, xorKey, aesKey) - + const ext = this.detectImageExtension(decrypted) || '.jpg' const outputPath = this.getCacheOutputPathFromDat(datPath, ext, payload.sessionId) await writeFile(outputPath, decrypted) - + this.logInfo('解密成功', { outputPath, size: decrypted.length }) + const isThumb = this.isThumbnailPath(datPath) this.cacheResolvedPaths(cacheKey, payload.imageMd5, payload.imageDatName, outputPath) if (!isThumb) { @@ -209,6 +256,7 @@ export class ImageDecryptService { this.emitCacheResolved(payload, cacheKey, localPath) return { success: true, localPath, isThumb } } catch (e) { + this.logError('解密失败', e, { md5: payload.imageMd5, datName: payload.imageDatName }) return { success: false, error: String(e) } } } @@ -233,7 +281,7 @@ export class ImageDecryptService { if (this.isAccountDir(entryPath)) return entryPath } } - } catch {} + } catch { } return null } @@ -244,10 +292,10 @@ export class ImageDecryptService { private getDecryptedCacheDir(wxid: string): string | null { const cachePath = this.configService.get('cachePath') if (!cachePath) return null - + const cleanedWxid = this.cleanAccountDirName(wxid) const cacheAccountDir = join(cachePath, cleanedWxid) - + // 检查缓存目录下是否有 hardlink.db if (existsSync(join(cacheAccountDir, 'hardlink.db'))) { return cacheAccountDir @@ -312,7 +360,7 @@ export class ImageDecryptService { allowThumbnail, skipResolvedCache }) - + // 优先通过 hardlink.db 查询 if (imageMd5) { this.logInfo('[ImageDecrypt] hardlink lookup (md5)', { imageMd5, sessionId }) @@ -474,7 +522,7 @@ export class ImageDecryptService { if (!hasUpdate) return this.updateFlags.set(cacheKey, true) this.emitImageUpdate(payload, cacheKey) - }).catch(() => {}) + }).catch(() => { }) } private looksLikeMd5(value: string): boolean { @@ -528,7 +576,7 @@ export class ImageDecryptService { this.logInfo('[ImageDecrypt] hardlink row miss', { md5, table: state.imageTable }) return null } - + const dir1 = this.getRowValue(row, 'dir1') const dir2 = this.getRowValue(row, 'dir2') const fileName = this.getRowValue(row, 'file_name') ?? this.getRowValue(row, 'fileName') @@ -549,7 +597,7 @@ export class ImageDecryptService { // dir1 和 dir2 是 rowid,需要从 dir2id 表查询对应的目录名 let dir1Name: string | null = null let dir2Name: string | null = null - + if (state.dirTable) { try { // 通过 rowid 查询目录名 @@ -562,7 +610,7 @@ export class ImageDecryptService { const value = this.getRowValue(dir1Result.rows[0], 'username') if (value) dir1Name = String(value) } - + const dir2Result = await wcdbService.execQuery( 'media', hardlinkPath, @@ -588,14 +636,14 @@ export class ImageDecryptService { join(accountDir, 'msg', 'attach', dir1Name, dir2Name, 'mg', fileName), join(accountDir, 'msg', 'attach', dir1Name, dir2Name, fileName), ] - + for (const fullPath of possiblePaths) { if (existsSync(fullPath)) { this.logInfo('[ImageDecrypt] hardlink path hit', { fullPath }) return fullPath } } - + this.logInfo('[ImageDecrypt] hardlink path miss', { possiblePaths }) return null } catch { @@ -829,14 +877,14 @@ export class ImageDecryptService { } } } - + // 新目录结构: Images/{normalizedKey}/{normalizedKey}_thumb.jpg 或 _hd.jpg const imageDir = join(root, normalizedKey) if (existsSync(imageDir)) { const hit = this.findCachedOutputInDir(imageDir, normalizedKey, extensions, preferHd) if (hit) return hit } - + // 兼容旧的平铺结构 for (const ext of extensions) { const candidate = join(root, `${cacheKey}${ext}`) @@ -846,7 +894,7 @@ export class ImageDecryptService { const candidate = join(root, `${cacheKey}_t${ext}`) if (existsSync(candidate)) return candidate } - + return null } @@ -863,6 +911,8 @@ export class ImageDecryptService { } const thumbPath = join(dirPath, `${normalizedKey}_thumb${ext}`) if (existsSync(thumbPath)) return thumbPath + + // 允许返回 _hd 格式(因为它有 _hd 变体后缀) if (!preferHd) { const hdPath = join(dirPath, `${normalizedKey}_hd${ext}`) if (existsSync(hdPath)) return hdPath @@ -875,14 +925,14 @@ export class ImageDecryptService { const name = basename(datPath) const lower = name.toLowerCase() const base = lower.endsWith('.dat') ? name.slice(0, -4) : name - + // 提取基础名称(去掉 _t, _h 等后缀) const normalizedBase = this.normalizeDatBase(base) - + // 判断是缩略图还是高清图 const isThumb = this.isThumbnailDat(lower) const suffix = isThumb ? '_thumb' : '_hd' - + const contactDir = this.sanitizeDirName(sessionId || 'unknown') const timeDir = this.resolveTimeDir(datPath) const outputDir = join(this.getCacheRoot(), contactDir, timeDir) @@ -960,8 +1010,9 @@ export class ImageDecryptService { const lower = entry.toLowerCase() if (!lower.endsWith('.dat')) continue if (this.isThumbnailDat(lower)) continue - if (!this.hasXVariant(lower.slice(0, -4))) continue const baseLower = lower.slice(0, -4) + // 只排除没有 _x 变体后缀的文件(允许 _hd、_h 等所有带变体的) + if (!this.hasXVariant(baseLower)) continue if (this.normalizeDatBase(baseLower) !== target) continue return join(dirPath, entry) } @@ -973,6 +1024,7 @@ export class ImageDecryptService { if (!lower.endsWith('.dat')) return false if (this.isThumbnailDat(lower)) return false const baseLower = lower.slice(0, -4) + // 只检查是否有 _x 变体后缀(允许 _hd、_h 等所有带变体的) return this.hasXVariant(baseLower) } @@ -1079,7 +1131,7 @@ export class ImageDecryptService { private async decryptDatAuto(datPath: string, xorKey: number, aesKey: Buffer | null): Promise { const version = this.getDatVersion(datPath) - + if (version === 0) { return this.decryptDatV3(datPath, xorKey) } @@ -1136,7 +1188,7 @@ export class ImageDecryptService { // 当 aesSize % 16 === 0 时,仍需要额外 16 字节的填充 const remainder = ((aesSize % 16) + 16) % 16 const alignedAesSize = aesSize + (16 - remainder) - + if (alignedAesSize > data.length) { throw new Error('文件格式异常:AES 数据长度超过文件实际长度') } @@ -1147,7 +1199,7 @@ export class ImageDecryptService { const decipher = crypto.createDecipheriv('aes-128-ecb', aesKey, null) decipher.setAutoPadding(false) const decrypted = Buffer.concat([decipher.update(aesData), decipher.final()]) - + // 使用 PKCS7 填充移除 unpadded = this.strictRemovePadding(decrypted) } @@ -1214,7 +1266,7 @@ export class ImageDecryptService { if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) return '.png' if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) return '.jpg' if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46 && - buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) { + buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) { return '.webp' } return null @@ -1332,10 +1384,10 @@ export class ImageDecryptService { keyCount.set(key, (keyCount.get(key) || 0) + 1) filesChecked++ } - } catch {} + } catch { } } } - } catch {} + } catch { } } scanDir(dirPath) diff --git a/electron/services/keyService.ts b/electron/services/keyService.ts index d78edbb..8b51c8d 100644 --- a/electron/services/keyService.ts +++ b/electron/services/keyService.ts @@ -1,9 +1,10 @@ import { app } from 'electron' import { join, dirname, basename } from 'path' -import { existsSync, readdirSync, readFileSync, statSync } from 'fs' +import { existsSync, readdirSync, readFileSync, statSync, copyFileSync, mkdirSync } from 'fs' import { execFile, spawn } from 'child_process' import { promisify } from 'util' import crypto from 'crypto' +import os from 'os' const execFileAsync = promisify(execFile) @@ -57,18 +58,94 @@ export class KeyService { private readonly ERROR_SUCCESS = 0 private getDllPath(): string { - const resourcesPath = app.isPackaged - ? join(process.resourcesPath, 'resources') - : join(app.getAppPath(), 'resources') - return join(resourcesPath, 'wx_key.dll') + const isPackaged = typeof app !== 'undefined' && app ? app.isPackaged : process.env.NODE_ENV === 'production' + + // 候选路径列表 + const candidates: string[] = [] + + // 1. 显式环境变量 (最高优先级) + if (process.env.WX_KEY_DLL_PATH) { + candidates.push(process.env.WX_KEY_DLL_PATH) + } + + if (isPackaged) { + // 生产环境: 通常在 resources 目录下,但也可能直接在 resources 根目录 + candidates.push(join(process.resourcesPath, 'resources', 'wx_key.dll')) + candidates.push(join(process.resourcesPath, 'wx_key.dll')) + } else { + // 开发环境 + const cwd = process.cwd() + candidates.push(join(cwd, 'resources', 'wx_key.dll')) + candidates.push(join(app.getAppPath(), 'resources', 'wx_key.dll')) + } + + // 检查并返回第一个存在的路径 + for (const path of candidates) { + if (existsSync(path)) { + return path + } + } + + // 如果都没找到,返回最可能的路径以便报错信息有参考 + return candidates[0] + } + + // 检查路径是否为 UNC 路径或网络路径 + private isNetworkPath(path: string): boolean { + // UNC 路径以 \\ 开头 + if (path.startsWith('\\\\')) { + return true + } + // 检查是否为网络映射驱动器(简化检测:A: 表示驱动器) + // 注意:这是一个启发式检测,更准确的方式需要调用 GetDriveType Windows API + // 但对于大多数 VM 共享场景,UNC 路径检测已足够 + return false + } + + // 将 DLL 复制到本地临时目录 + private localizeNetworkDll(originalPath: string): string { + try { + const tempDir = join(os.tmpdir(), 'weflow_dll_cache') + if (!existsSync(tempDir)) { + mkdirSync(tempDir, { recursive: true }) + } + const localPath = join(tempDir, 'wx_key.dll') + + // 检查是否已经有本地副本,如果有就使用它 + if (existsSync(localPath)) { + console.log(`使用已存在的 DLL 本地副本: ${localPath}`) + return localPath + } + + console.log(`检测到网络路径 DLL,正在复制到本地: ${originalPath} -> ${localPath}`) + copyFileSync(originalPath, localPath) + console.log('DLL 本地化成功') + return localPath + } catch (e) { + console.error('DLL 本地化失败:', e) + // 如果本地化失败,返回原路径 + return originalPath + } } private ensureLoaded(): boolean { if (this.initialized) return true + + let dllPath = '' try { this.koffi = require('koffi') - const dllPath = this.getDllPath() - if (!existsSync(dllPath)) return false + dllPath = this.getDllPath() + + if (!existsSync(dllPath)) { + console.error(`wx_key.dll 不存在于路径: ${dllPath}`) + return false + } + + // 检查是否为网络路径,如果是则本地化 + if (this.isNetworkPath(dllPath)) { + console.log('检测到网络路径,将进行本地化处理') + dllPath = this.localizeNetworkDll(dllPath) + } this.lib = this.koffi.load(dllPath) this.initHook = this.lib.func('bool InitializeHook(uint32 targetPid)') @@ -80,7 +157,14 @@ export class KeyService { this.initialized = true return true } catch (e) { - console.error('加载 wx_key.dll 失败:', e) + const errorMsg = e instanceof Error ? e.message : String(e) + const errorStack = e instanceof Error ? e.stack : '' + console.error(`加载 wx_key.dll 失败`) + console.error(` 路径: ${dllPath}`) + console.error(` 错误: ${errorMsg}`) + if (errorStack) { + console.error(` 堆栈: ${errorStack}`) + } return false } } @@ -831,17 +915,40 @@ export class KeyService { return buffer.subarray(0, bytesRead[0]) } - private async getAesKeyFromMemory(pid: number, ciphertext: Buffer): Promise { + private async getAesKeyFromMemory( + pid: number, + ciphertext: Buffer, + onProgress?: (current: number, total: number, message: string) => void + ): Promise { if (!this.ensureKernel32()) return null const hProcess = this.OpenProcess(this.PROCESS_ALL_ACCESS, false, pid) if (!hProcess) return null try { - const regions = this.getMemoryRegions(hProcess) - const chunkSize = 4 * 1024 * 1024 + const allRegions = this.getMemoryRegions(hProcess) + + // 优化1: 只保留小内存区域(< 10MB)- 密钥通常在小区域,可大幅减少扫描时间 + const filteredRegions = allRegions.filter(([_, size]) => size <= 10 * 1024 * 1024) + + // 优化2: 优先级排序 - 按大小升序,先扫描小区域(密钥通常在较小区域) + const sortedRegions = filteredRegions.sort((a, b) => a[1] - b[1]) + + // 优化3: 计算总字节数用于精确进度报告 + const totalBytes = sortedRegions.reduce((sum, [_, size]) => sum + size, 0) + let processedBytes = 0 + + // 优化4: 减小分块大小到 1MB(参考 wx_key 项目) + const chunkSize = 1 * 1024 * 1024 const overlap = 65 - for (const [baseAddress, regionSize] of regions) { - if (regionSize > 100 * 1024 * 1024) continue + let currentRegion = 0 + + for (const [baseAddress, regionSize] of sortedRegions) { + currentRegion++ + const progress = totalBytes > 0 ? Math.floor((processedBytes / totalBytes) * 100) : 0 + onProgress?.(progress, 100, `扫描内存 ${progress}% (${currentRegion}/${sortedRegions.length})`) + + // 每个区域都让出主线程,确保UI流畅 + await new Promise(resolve => setImmediate(resolve)) let offset = 0 let trailing: Buffer | null = null while (offset < regionSize) { @@ -896,6 +1003,9 @@ export class KeyService { trailing = dataToScan.subarray(start < 0 ? 0 : start) offset += currentChunkSize } + + // 更新已处理字节数 + processedBytes += regionSize } return null } finally { @@ -933,7 +1043,9 @@ export class KeyService { if (!pid) return { success: false, error: '未检测到微信进程' } onProgress?.('正在扫描内存获取 AES 密钥...') - const aesKey = await this.getAesKeyFromMemory(pid, ciphertext) + const aesKey = await this.getAesKeyFromMemory(pid, ciphertext, (current, total, msg) => { + onProgress?.(`${msg} (${current}/${total})`) + }) if (!aesKey) { return { success: false, diff --git a/electron/services/wcdbCore.ts b/electron/services/wcdbCore.ts new file mode 100644 index 0000000..763fd87 --- /dev/null +++ b/electron/services/wcdbCore.ts @@ -0,0 +1,1317 @@ +import { join, dirname, basename } from 'path' +import { appendFileSync, existsSync, mkdirSync, readdirSync, statSync, readFileSync } from 'fs' + +export class WcdbCore { + private resourcesPath: string | null = null + private userDataPath: string | null = null + private logEnabled = false + private lib: any = null + private koffi: any = null + private initialized = false + private handle: number | null = null + private currentPath: string | null = null + private currentKey: string | null = null + private currentWxid: string | null = null + + // 函数引用 + private wcdbInit: any = null + private wcdbShutdown: any = null + private wcdbOpenAccount: any = null + private wcdbCloseAccount: any = null + private wcdbSetMyWxid: any = null + private wcdbFreeString: any = null + private wcdbGetSessions: any = null + private wcdbGetMessages: any = null + private wcdbGetMessageCount: any = null + private wcdbGetDisplayNames: any = null + private wcdbGetAvatarUrls: any = null + private wcdbGetGroupMemberCount: any = null + private wcdbGetGroupMemberCounts: any = null + private wcdbGetGroupMembers: any = null + private wcdbGetMessageTables: any = null + private wcdbGetMessageMeta: any = null + private wcdbGetContact: any = null + private wcdbGetMessageTableStats: any = null + private wcdbGetAggregateStats: any = null + private wcdbGetAvailableYears: any = null + private wcdbGetAnnualReportStats: any = null + private wcdbGetAnnualReportExtras: any = null + private wcdbGetGroupStats: any = null + private wcdbOpenMessageCursor: any = null + private wcdbOpenMessageCursorLite: any = null + private wcdbFetchMessageBatch: any = null + private wcdbCloseMessageCursor: any = null + private wcdbGetLogs: any = null + private wcdbExecQuery: any = null + private wcdbListMessageDbs: any = null + private wcdbListMediaDbs: any = null + private wcdbGetMessageById: any = null + private wcdbGetEmoticonCdnUrl: any = null + private wcdbGetDbStatus: any = null + private avatarUrlCache: Map = new Map() + private readonly avatarCacheTtlMs = 10 * 60 * 1000 + private logTimer: NodeJS.Timeout | null = null + private lastLogTail: string | null = null + + setPaths(resourcesPath: string, userDataPath: string): void { + this.resourcesPath = resourcesPath + this.userDataPath = userDataPath + } + + setLogEnabled(enabled: boolean): void { + this.logEnabled = enabled + if (this.isLogEnabled() && this.initialized) { + this.startLogPolling() + } else { + this.stopLogPolling() + } + } + + /** + * 获取 DLL 路径 + */ + private getDllPath(): string { + const envDllPath = process.env.WCDB_DLL_PATH + if (envDllPath && envDllPath.length > 0) { + return envDllPath + } + + // 基础路径探测 + const isPackaged = typeof process['resourcesPath'] !== 'undefined' + const resourcesPath = isPackaged ? process.resourcesPath : join(process.cwd(), 'resources') + + const candidates = [ + // 环境变量指定 resource 目录 + process.env.WCDB_RESOURCES_PATH ? join(process.env.WCDB_RESOURCES_PATH, 'wcdb_api.dll') : null, + // 显式 setPaths 设置的路径 + this.resourcesPath ? join(this.resourcesPath, 'wcdb_api.dll') : null, + // text/resources/wcdb_api.dll (打包常见结构) + join(resourcesPath, 'resources', 'wcdb_api.dll'), + // items/resourcesPath/wcdb_api.dll (扁平结构) + join(resourcesPath, 'wcdb_api.dll'), + // CWD fallback + join(process.cwd(), 'resources', 'wcdb_api.dll') + ].filter(Boolean) as string[] + + for (const path of candidates) { + if (existsSync(path)) return path + } + + return candidates[0] || 'wcdb_api.dll' + } + + private isLogEnabled(): boolean { + if (process.env.WEFLOW_WORKER === '1') return false + if (process.env.WCDB_LOG_ENABLED === '1') return true + return this.logEnabled + } + + private writeLog(message: string, force = false): void { + if (!force && !this.isLogEnabled()) return + try { + const base = this.userDataPath || process.env.WCDB_LOG_DIR || process.cwd() + const dir = join(base, 'logs') + if (!existsSync(dir)) mkdirSync(dir, { recursive: true }) + const line = `[${new Date().toISOString()}] ${message}\n` + appendFileSync(join(dir, 'wcdb.log'), line, { encoding: 'utf8' }) + } catch { } + } + + /** + * 递归查找 session.db 文件 + */ + private findSessionDb(dir: string, depth = 0): string | null { + if (depth > 5) return null + + try { + const entries = readdirSync(dir) + + for (const entry of entries) { + if (entry.toLowerCase() === 'session.db') { + const fullPath = join(dir, entry) + if (statSync(fullPath).isFile()) { + return fullPath + } + } + } + + for (const entry of entries) { + const fullPath = join(dir, entry) + try { + if (statSync(fullPath).isDirectory()) { + const found = this.findSessionDb(fullPath, depth + 1) + if (found) return found + } + } catch { } + } + } catch (e) { + console.error('查找 session.db 失败:', e) + } + + return null + } + + private resolveDbStoragePath(basePath: string, wxid: string): string | null { + if (!basePath) return null + const normalized = basePath.replace(/[\\\\/]+$/, '') + if (normalized.toLowerCase().endsWith('db_storage') && existsSync(normalized)) { + return normalized + } + const direct = join(normalized, 'db_storage') + if (existsSync(direct)) { + return direct + } + if (wxid) { + const viaWxid = join(normalized, wxid, 'db_storage') + if (existsSync(viaWxid)) { + return viaWxid + } + // 兼容目录名包含额外后缀(如 wxid_xxx_1234) + try { + const entries = readdirSync(normalized) + const lowerWxid = wxid.toLowerCase() + const candidates = entries.filter((entry) => { + const entryPath = join(normalized, entry) + try { + if (!statSync(entryPath).isDirectory()) return false + } catch { + return false + } + const lowerEntry = entry.toLowerCase() + return lowerEntry === lowerWxid || lowerEntry.startsWith(`${lowerWxid}_`) + }) + for (const entry of candidates) { + const candidate = join(normalized, entry, 'db_storage') + if (existsSync(candidate)) { + return candidate + } + } + } catch { } + } + return null + } + + /** + * 初始化 WCDB + */ + async initialize(): Promise { + if (this.initialized) return true + + try { + this.koffi = require('koffi') + const dllPath = this.getDllPath() + + if (!existsSync(dllPath)) { + console.error('WCDB DLL 不存在:', dllPath) + return false + } + + this.lib = this.koffi.load(dllPath) + + // 定义类型 + // wcdb_status wcdb_init() + this.wcdbInit = this.lib.func('int32 wcdb_init()') + + // wcdb_status wcdb_shutdown() + this.wcdbShutdown = this.lib.func('int32 wcdb_shutdown()') + + // wcdb_status wcdb_open_account(const char* session_db_path, const char* hex_key, wcdb_handle* out_handle) + // wcdb_handle 是 int64_t + this.wcdbOpenAccount = this.lib.func('int32 wcdb_open_account(const char* path, const char* key, _Out_ int64* handle)') + + // wcdb_status wcdb_close_account(wcdb_handle handle) + // C 接口是 int64, koffi 返回 handle 是 number 类型 + this.wcdbCloseAccount = this.lib.func('int32 wcdb_close_account(int64 handle)') + + // wcdb_status wcdb_set_my_wxid(wcdb_handle handle, const char* wxid) + try { + this.wcdbSetMyWxid = this.lib.func('int32 wcdb_set_my_wxid(int64 handle, const char* wxid)') + } catch { + this.wcdbSetMyWxid = null + } + + // void wcdb_free_string(char* ptr) + this.wcdbFreeString = this.lib.func('void wcdb_free_string(void* ptr)') + + // wcdb_status wcdb_get_sessions(wcdb_handle handle, char** out_json) + this.wcdbGetSessions = this.lib.func('int32 wcdb_get_sessions(int64 handle, _Out_ void** outJson)') + + // wcdb_status wcdb_get_messages(wcdb_handle handle, const char* username, int32_t limit, int32_t offset, char** out_json) + this.wcdbGetMessages = this.lib.func('int32 wcdb_get_messages(int64 handle, const char* username, int32 limit, int32 offset, _Out_ void** outJson)') + + // wcdb_status wcdb_get_message_count(wcdb_handle handle, const char* username, int32_t* out_count) + this.wcdbGetMessageCount = this.lib.func('int32 wcdb_get_message_count(int64 handle, const char* username, _Out_ int32* outCount)') + + // wcdb_status wcdb_get_display_names(wcdb_handle handle, const char* usernames_json, char** out_json) + this.wcdbGetDisplayNames = this.lib.func('int32 wcdb_get_display_names(int64 handle, const char* usernamesJson, _Out_ void** outJson)') + + // wcdb_status wcdb_get_avatar_urls(wcdb_handle handle, const char* usernames_json, char** out_json) + this.wcdbGetAvatarUrls = this.lib.func('int32 wcdb_get_avatar_urls(int64 handle, const char* usernamesJson, _Out_ void** outJson)') + + // wcdb_status wcdb_get_group_member_count(wcdb_handle handle, const char* chatroom_id, int32_t* out_count) + this.wcdbGetGroupMemberCount = this.lib.func('int32 wcdb_get_group_member_count(int64 handle, const char* chatroomId, _Out_ int32* outCount)') + + // wcdb_status wcdb_get_group_member_counts(wcdb_handle handle, const char* chatroom_ids_json, char** out_json) + try { + this.wcdbGetGroupMemberCounts = this.lib.func('int32 wcdb_get_group_member_counts(int64 handle, const char* chatroomIdsJson, _Out_ void** outJson)') + } catch { + this.wcdbGetGroupMemberCounts = null + } + + // wcdb_status wcdb_get_group_members(wcdb_handle handle, const char* chatroom_id, char** out_json) + this.wcdbGetGroupMembers = this.lib.func('int32 wcdb_get_group_members(int64 handle, const char* chatroomId, _Out_ void** outJson)') + + // wcdb_status wcdb_get_message_tables(wcdb_handle handle, const char* session_id, char** out_json) + this.wcdbGetMessageTables = this.lib.func('int32 wcdb_get_message_tables(int64 handle, const char* sessionId, _Out_ void** outJson)') + + // wcdb_status wcdb_get_message_meta(wcdb_handle handle, const char* db_path, const char* table_name, int32_t limit, int32_t offset, char** out_json) + this.wcdbGetMessageMeta = this.lib.func('int32 wcdb_get_message_meta(int64 handle, const char* dbPath, const char* tableName, int32 limit, int32 offset, _Out_ void** outJson)') + + // wcdb_status wcdb_get_contact(wcdb_handle handle, const char* username, char** out_json) + this.wcdbGetContact = this.lib.func('int32 wcdb_get_contact(int64 handle, const char* username, _Out_ void** outJson)') + + // wcdb_status wcdb_get_message_table_stats(wcdb_handle handle, const char* session_id, char** out_json) + this.wcdbGetMessageTableStats = this.lib.func('int32 wcdb_get_message_table_stats(int64 handle, const char* sessionId, _Out_ void** outJson)') + + // wcdb_status wcdb_get_aggregate_stats(wcdb_handle handle, const char* session_ids_json, int32_t begin_timestamp, int32_t end_timestamp, char** out_json) + this.wcdbGetAggregateStats = this.lib.func('int32 wcdb_get_aggregate_stats(int64 handle, const char* sessionIdsJson, int32 begin, int32 end, _Out_ void** outJson)') + + // wcdb_status wcdb_get_available_years(wcdb_handle handle, const char* session_ids_json, char** out_json) + try { + this.wcdbGetAvailableYears = this.lib.func('int32 wcdb_get_available_years(int64 handle, const char* sessionIdsJson, _Out_ void** outJson)') + } catch { + this.wcdbGetAvailableYears = null + } + + // wcdb_status wcdb_get_annual_report_stats(wcdb_handle handle, const char* session_ids_json, int32_t begin_timestamp, int32_t end_timestamp, char** out_json) + try { + this.wcdbGetAnnualReportStats = this.lib.func('int32 wcdb_get_annual_report_stats(int64 handle, const char* sessionIdsJson, int32 begin, int32 end, _Out_ void** outJson)') + } catch { + this.wcdbGetAnnualReportStats = null + } + + // wcdb_status wcdb_get_annual_report_extras(wcdb_handle handle, const char* session_ids_json, int32_t begin_timestamp, int32_t end_timestamp, int32_t peak_day_begin, int32_t peak_day_end, char** out_json) + try { + this.wcdbGetAnnualReportExtras = this.lib.func('int32 wcdb_get_annual_report_extras(int64 handle, const char* sessionIdsJson, int32 begin, int32 end, int32 peakBegin, int32 peakEnd, _Out_ void** outJson)') + } catch { + this.wcdbGetAnnualReportExtras = null + } + + // wcdb_status wcdb_get_group_stats(wcdb_handle handle, const char* chatroom_id, int32_t begin_timestamp, int32_t end_timestamp, char** out_json) + try { + this.wcdbGetGroupStats = this.lib.func('int32 wcdb_get_group_stats(int64 handle, const char* chatroomId, int32 begin, int32 end, _Out_ void** outJson)') + } catch { + this.wcdbGetGroupStats = null + } + + // wcdb_status wcdb_open_message_cursor(wcdb_handle handle, const char* session_id, int32_t batch_size, int32_t ascending, int32_t begin_timestamp, int32_t end_timestamp, wcdb_cursor* out_cursor) + this.wcdbOpenMessageCursor = this.lib.func('int32 wcdb_open_message_cursor(int64 handle, const char* sessionId, int32 batchSize, int32 ascending, int32 beginTimestamp, int32 endTimestamp, _Out_ int64* outCursor)') + + // wcdb_status wcdb_open_message_cursor_lite(wcdb_handle handle, const char* session_id, int32_t batch_size, int32_t ascending, int32_t begin_timestamp, int32_t end_timestamp, wcdb_cursor* out_cursor) + try { + this.wcdbOpenMessageCursorLite = this.lib.func('int32 wcdb_open_message_cursor_lite(int64 handle, const char* sessionId, int32 batchSize, int32 ascending, int32 beginTimestamp, int32 endTimestamp, _Out_ int64* outCursor)') + } catch { + this.wcdbOpenMessageCursorLite = null + } + + // wcdb_status wcdb_fetch_message_batch(wcdb_handle handle, wcdb_cursor cursor, char** out_json, int32_t* out_has_more) + this.wcdbFetchMessageBatch = this.lib.func('int32 wcdb_fetch_message_batch(int64 handle, int64 cursor, _Out_ void** outJson, _Out_ int32* outHasMore)') + + // wcdb_status wcdb_close_message_cursor(wcdb_handle handle, wcdb_cursor cursor) + this.wcdbCloseMessageCursor = this.lib.func('int32 wcdb_close_message_cursor(int64 handle, int64 cursor)') + + // wcdb_status wcdb_get_logs(char** out_json) + this.wcdbGetLogs = this.lib.func('int32 wcdb_get_logs(_Out_ void** outJson)') + + // wcdb_status wcdb_exec_query(wcdb_handle handle, const char* db_kind, const char* db_path, const char* sql, char** out_json) + this.wcdbExecQuery = this.lib.func('int32 wcdb_exec_query(int64 handle, const char* kind, const char* path, const char* sql, _Out_ void** outJson)') + + // wcdb_status wcdb_get_emoticon_cdn_url(wcdb_handle handle, const char* db_path, const char* md5, char** out_url) + this.wcdbGetEmoticonCdnUrl = this.lib.func('int32 wcdb_get_emoticon_cdn_url(int64 handle, const char* dbPath, const char* md5, _Out_ void** outUrl)') + + // wcdb_status wcdb_list_message_dbs(wcdb_handle handle, char** out_json) + this.wcdbListMessageDbs = this.lib.func('int32 wcdb_list_message_dbs(int64 handle, _Out_ void** outJson)') + + // wcdb_status wcdb_list_media_dbs(wcdb_handle handle, char** out_json) + this.wcdbListMediaDbs = this.lib.func('int32 wcdb_list_media_dbs(int64 handle, _Out_ void** outJson)') + + // wcdb_status wcdb_get_message_by_id(wcdb_handle handle, const char* session_id, int32 local_id, char** out_json) + this.wcdbGetMessageById = this.lib.func('int32 wcdb_get_message_by_id(int64 handle, const char* sessionId, int32 localId, _Out_ void** outJson)') + + // wcdb_status wcdb_get_db_status(wcdb_handle handle, char** out_json) + try { + this.wcdbGetDbStatus = this.lib.func('int32 wcdb_get_db_status(int64 handle, _Out_ void** outJson)') + } catch { + this.wcdbGetDbStatus = null + } + + // 初始化 + const initResult = this.wcdbInit() + if (initResult !== 0) { + console.error('WCDB 初始化失败:', initResult) + return false + } + + this.initialized = true + return true + } catch (e) { + console.error('WCDB 初始化异常:', e) + return false + } + } + + /** + * 测试数据库连接 + */ + async testConnection(dbPath: string, hexKey: string, wxid: string): Promise<{ success: boolean; error?: string; sessionCount?: number }> { + try { + // 如果当前已经有相同参数的活动连接,直接返回成功 + if (this.handle !== null && + this.currentPath === dbPath && + this.currentKey === hexKey && + this.currentWxid === wxid) { + return { success: true, sessionCount: 0 } + } + + if (!this.initialized) { + const initOk = await this.initialize() + if (!initOk) { + return { success: false, error: 'WCDB 初始化失败' } + } + } + + // 构建 db_storage 目录路径 + const dbStoragePath = this.resolveDbStoragePath(dbPath, wxid) + this.writeLog(`testConnection dbPath=${dbPath} wxid=${wxid} dbStorage=${dbStoragePath || 'null'}`) + + if (!dbStoragePath || !existsSync(dbStoragePath)) { + return { success: false, error: `数据库目录不存在: ${dbPath}` } + } + + // 递归查找 session.db + const sessionDbPath = this.findSessionDb(dbStoragePath) + this.writeLog(`testConnection sessionDb=${sessionDbPath || 'null'}`) + + if (!sessionDbPath) { + return { success: false, error: `未找到 session.db 文件` } + } + + // 分配输出参数内存 + const handleOut = [0] + const result = this.wcdbOpenAccount(sessionDbPath, hexKey, handleOut) + + if (result !== 0) { + await this.printLogs() + let errorMsg = '数据库打开失败' + if (result === -1) errorMsg = '参数错误' + else if (result === -2) errorMsg = '密钥错误' + else if (result === -3) errorMsg = '数据库打开失败' + this.writeLog(`testConnection openAccount failed code=${result}`) + return { success: false, error: `${errorMsg} (错误码: ${result})` } + } + + const tempHandle = handleOut[0] + if (tempHandle <= 0) { + return { success: false, error: '无效的数据库句柄' } + } + + // 测试成功,使用 shutdown 清理所有资源(包括测试句柄) + // 这会中断当前活动连接,但 testConnection 本应该是独立测试 + try { + this.wcdbShutdown() + this.handle = null + this.currentPath = null + this.currentKey = null + this.currentWxid = null + this.initialized = false + } catch (closeErr) { + console.error('关闭测试数据库时出错:', closeErr) + } + + return { success: true, sessionCount: 0 } + } catch (e) { + console.error('测试连接异常:', e) + this.writeLog(`testConnection exception: ${String(e)}`) + return { success: false, error: String(e) } + } + } + + /** + * 打印 DLL 内部日志(仅在出错时调用) + */ + private async printLogs(force = false): Promise { + try { + if (!this.wcdbGetLogs) return + const outPtr = [null as any] + const result = this.wcdbGetLogs(outPtr) + if (result === 0 && outPtr[0]) { + try { + const jsonStr = this.koffi.decode(outPtr[0], 'char', -1) + this.writeLog(`wcdb_logs: ${jsonStr}`, force) + this.wcdbFreeString(outPtr[0]) + } catch (e) { + // ignore + } + } + } catch (e) { + console.error('获取日志失败:', e) + this.writeLog(`wcdb_logs failed: ${String(e)}`, force) + } + } + + private startLogPolling(): void { + if (this.logTimer || !this.isLogEnabled()) return + this.logTimer = setInterval(() => { + void this.pollLogs() + }, 2000) + } + + private stopLogPolling(): void { + if (this.logTimer) { + clearInterval(this.logTimer) + this.logTimer = null + } + this.lastLogTail = null + } + + private async pollLogs(): Promise { + try { + if (!this.wcdbGetLogs || !this.isLogEnabled()) return + const outPtr = [null as any] + const result = this.wcdbGetLogs(outPtr) + if (result !== 0 || !outPtr[0]) return + let jsonStr = '' + try { + jsonStr = this.koffi.decode(outPtr[0], 'char', -1) + } finally { + try { this.wcdbFreeString(outPtr[0]) } catch { } + } + const logs = JSON.parse(jsonStr) as string[] + if (!Array.isArray(logs) || logs.length === 0) return + let startIdx = 0 + if (this.lastLogTail) { + const idx = logs.lastIndexOf(this.lastLogTail) + if (idx >= 0) startIdx = idx + 1 + } + for (let i = startIdx; i < logs.length; i += 1) { + this.writeLog(`wcdb: ${logs[i]}`) + } + this.lastLogTail = logs[logs.length - 1] + } catch (e) { + // ignore polling errors + } + } + + private decodeJsonPtr(outPtr: any): string | null { + if (!outPtr) return null + try { + const jsonStr = this.koffi.decode(outPtr, 'char', -1) + this.wcdbFreeString(outPtr) + return jsonStr + } catch (e) { + try { this.wcdbFreeString(outPtr) } catch { } + return null + } + } + + private ensureReady(): boolean { + return this.initialized && this.handle !== null + } + + private normalizeTimestamp(input: number): number { + if (!input || input <= 0) return 0 + const asNumber = Number(input) + if (!Number.isFinite(asNumber)) return 0 + // Treat >1e12 as milliseconds. + const seconds = asNumber > 1e12 ? Math.floor(asNumber / 1000) : Math.floor(asNumber) + const maxInt32 = 2147483647 + return Math.min(Math.max(seconds, 0), maxInt32) + } + + private normalizeRange(beginTimestamp: number, endTimestamp: number): { begin: number; end: number } { + const normalizedBegin = this.normalizeTimestamp(beginTimestamp) + let normalizedEnd = this.normalizeTimestamp(endTimestamp) + if (normalizedEnd <= 0) { + normalizedEnd = this.normalizeTimestamp(Date.now()) + } + if (normalizedBegin > 0 && normalizedEnd < normalizedBegin) { + normalizedEnd = normalizedBegin + } + return { begin: normalizedBegin, end: normalizedEnd } + } + + isReady(): boolean { + return this.ensureReady() + } + + /** + * 打开数据库 + */ + async open(dbPath: string, hexKey: string, wxid: string): Promise { + try { + if (!this.initialized) { + const initOk = await this.initialize() + if (!initOk) return false + } + + // 检查是否已经是当前连接的参数,如果是则直接返回成功,实现"始终保持链接" + if (this.handle !== null && + this.currentPath === dbPath && + this.currentKey === hexKey && + this.currentWxid === wxid) { + return true + } + + // 如果参数不同,则先关闭原来的连接 + if (this.handle !== null) { + this.close() + // 重新初始化,因为 close 呼叫了 shutdown + const initOk = await this.initialize() + if (!initOk) return false + } + + const dbStoragePath = this.resolveDbStoragePath(dbPath, wxid) + this.writeLog(`open dbPath=${dbPath} wxid=${wxid} dbStorage=${dbStoragePath || 'null'}`) + + if (!dbStoragePath || !existsSync(dbStoragePath)) { + console.error('数据库目录不存在:', dbPath) + this.writeLog(`open failed: dbStorage not found for ${dbPath}`) + return false + } + + const sessionDbPath = this.findSessionDb(dbStoragePath) + this.writeLog(`open sessionDb=${sessionDbPath || 'null'}`) + if (!sessionDbPath) { + console.error('未找到 session.db 文件') + this.writeLog('open failed: session.db not found') + return false + } + + const handleOut = [0] + const result = this.wcdbOpenAccount(sessionDbPath, hexKey, handleOut) + + if (result !== 0) { + console.error('打开数据库失败:', result) + await this.printLogs() + this.writeLog(`open failed: openAccount code=${result}`) + return false + } + + const handle = handleOut[0] + if (handle <= 0) { + return false + } + + this.handle = handle + this.currentPath = dbPath + this.currentKey = hexKey + this.currentWxid = wxid + this.initialized = true + if (this.wcdbSetMyWxid && wxid) { + try { + this.wcdbSetMyWxid(this.handle, wxid) + } catch (e) { + console.warn('设置 wxid 失败:', e) + } + } + if (this.isLogEnabled()) { + this.startLogPolling() + } + this.writeLog(`open ok handle=${handle}`) + return true + } catch (e) { + console.error('打开数据库异常:', e) + this.writeLog(`open exception: ${String(e)}`) + return false + } + } + + /** + * 关闭数据库 + * 注意:wcdb_close_account 可能导致崩溃,使用 shutdown 代替 + */ + close(): void { + if (this.handle !== null || this.initialized) { + try { + // 不调用 closeAccount,直接 shutdown + this.wcdbShutdown() + } catch (e) { + console.error('WCDB shutdown 出错:', e) + } + this.handle = null + this.currentPath = null + this.currentKey = null + this.currentWxid = null + this.initialized = false + this.stopLogPolling() + } + } + + /** + * 关闭服务(与 close 相同) + */ + shutdown(): void { + this.close() + } + + /** + * 检查是否已连接 + */ + isConnected(): boolean { + return this.initialized && this.handle !== null + } + + async getSessions(): Promise<{ success: boolean; sessions?: any[]; error?: string }> { + if (!this.ensureReady()) { + this.writeLog('getSessions skipped: not connected') + return { success: false, error: 'WCDB 未连接' } + } + try { + // 使用 setImmediate 让事件循环有机会处理其他任务,避免长时间阻塞 + await new Promise(resolve => setImmediate(resolve)) + + const outPtr = [null as any] + const result = this.wcdbGetSessions(this.handle, outPtr) + + // DLL 调用后再次让出控制权 + await new Promise(resolve => setImmediate(resolve)) + + if (result !== 0 || !outPtr[0]) { + this.writeLog(`getSessions failed: code=${result}`) + return { success: false, error: `获取会话失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析会话失败' } + this.writeLog(`getSessions ok size=${jsonStr.length}`) + const sessions = JSON.parse(jsonStr) + return { success: true, sessions } + } catch (e) { + this.writeLog(`getSessions exception: ${String(e)}`) + return { success: false, error: String(e) } + } + } + + async getMessages(sessionId: string, limit: number, offset: number): Promise<{ success: boolean; messages?: any[]; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outPtr = [null as any] + const result = this.wcdbGetMessages(this.handle, sessionId, limit, offset, outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取消息失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析消息失败' } + const messages = JSON.parse(jsonStr) + return { success: true, messages } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getMessageCount(sessionId: string): Promise<{ success: boolean; count?: number; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outCount = [0] + const result = this.wcdbGetMessageCount(this.handle, sessionId, outCount) + if (result !== 0) { + return { success: false, error: `获取消息总数失败: ${result}` } + } + return { success: true, count: outCount[0] } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getDisplayNames(usernames: string[]): Promise<{ success: boolean; map?: Record; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + if (usernames.length === 0) return { success: true, map: {} } + try { + // 让出控制权,避免阻塞事件循环 + await new Promise(resolve => setImmediate(resolve)) + + const outPtr = [null as any] + const result = this.wcdbGetDisplayNames(this.handle, JSON.stringify(usernames), outPtr) + + // DLL 调用后再次让出控制权 + await new Promise(resolve => setImmediate(resolve)) + + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取昵称失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析昵称失败' } + const map = JSON.parse(jsonStr) + return { success: true, map } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getAvatarUrls(usernames: string[]): Promise<{ success: boolean; map?: Record; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + if (usernames.length === 0) return { success: true, map: {} } + try { + const now = Date.now() + const resultMap: Record = {} + const toFetch: string[] = [] + const seen = new Set() + + for (const username of usernames) { + if (!username || seen.has(username)) continue + seen.add(username) + const cached = this.avatarUrlCache.get(username) + // 只使用有效的缓存(URL不为空) + if (cached && cached.url && cached.url.trim() && now - cached.updatedAt < this.avatarCacheTtlMs) { + resultMap[username] = cached.url + continue + } + toFetch.push(username) + } + + if (toFetch.length === 0) { + return { success: true, map: resultMap } + } + + // 让出控制权,避免阻塞事件循环 + await new Promise(resolve => setImmediate(resolve)) + + const outPtr = [null as any] + const result = this.wcdbGetAvatarUrls(this.handle, JSON.stringify(toFetch), outPtr) + + // DLL 调用后再次让出控制权 + await new Promise(resolve => setImmediate(resolve)) + + if (result !== 0 || !outPtr[0]) { + console.warn(`[wcdbCore] getAvatarUrls DLL调用失败: result=${result}, usernames=${toFetch.length}`) + if (Object.keys(resultMap).length > 0) { + return { success: true, map: resultMap, error: `获取头像失败: ${result}` } + } + return { success: false, error: `获取头像失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) { + console.error('[wcdbCore] getAvatarUrls 解析JSON失败') + return { success: false, error: '解析头像失败' } + } + const map = JSON.parse(jsonStr) as Record + let successCount = 0 + let emptyCount = 0 + for (const username of toFetch) { + const url = map[username] + if (url && url.trim()) { + resultMap[username] = url + // 只缓存有效的URL + this.avatarUrlCache.set(username, { url, updatedAt: now }) + successCount++ + } else { + emptyCount++ + // 不缓存空URL,下次可以重新尝试 + } + } + console.log(`[wcdbCore] getAvatarUrls 成功: ${successCount}个, 空结果: ${emptyCount}个, 总请求: ${toFetch.length}`) + return { success: true, map: resultMap } + } catch (e) { + console.error('[wcdbCore] getAvatarUrls 异常:', e) + return { success: false, error: String(e) } + } + } + + async getGroupMemberCount(chatroomId: string): Promise<{ success: boolean; count?: number; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outCount = [0] + const result = this.wcdbGetGroupMemberCount(this.handle, chatroomId, outCount) + if (result !== 0) { + return { success: false, error: `获取群成员数量失败: ${result}` } + } + return { success: true, count: outCount[0] } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getGroupMemberCounts(chatroomIds: string[]): Promise<{ success: boolean; map?: Record; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + if (chatroomIds.length === 0) return { success: true, map: {} } + if (!this.wcdbGetGroupMemberCounts) { + const map: Record = {} + for (const chatroomId of chatroomIds) { + const result = await this.getGroupMemberCount(chatroomId) + if (result.success && typeof result.count === 'number') { + map[chatroomId] = result.count + } + } + return { success: true, map } + } + try { + const outPtr = [null as any] + const result = this.wcdbGetGroupMemberCounts(this.handle, JSON.stringify(chatroomIds), outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取群成员数量失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析群成员数量失败' } + const map = JSON.parse(jsonStr) + return { success: true, map } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getGroupMembers(chatroomId: string): Promise<{ success: boolean; members?: any[]; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outPtr = [null as any] + const result = this.wcdbGetGroupMembers(this.handle, chatroomId, outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取群成员失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析群成员失败' } + const members = JSON.parse(jsonStr) + return { success: true, members } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getMessageTables(sessionId: string): Promise<{ success: boolean; tables?: any[]; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outPtr = [null as any] + const result = this.wcdbGetMessageTables(this.handle, sessionId, outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取消息表失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析消息表失败' } + const tables = JSON.parse(jsonStr) + return { success: true, tables } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getMessageTableStats(sessionId: string): Promise<{ success: boolean; tables?: any[]; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outPtr = [null as any] + const result = this.wcdbGetMessageTableStats(this.handle, sessionId, outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取表统计失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析表统计失败' } + const tables = JSON.parse(jsonStr) + return { success: true, tables } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getMessageMeta(dbPath: string, tableName: string, limit: number, offset: number): Promise<{ success: boolean; rows?: any[]; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outPtr = [null as any] + const result = this.wcdbGetMessageMeta(this.handle, dbPath, tableName, limit, offset, outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取消息元数据失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析消息元数据失败' } + const rows = JSON.parse(jsonStr) + return { success: true, rows } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getContact(username: string): Promise<{ success: boolean; contact?: any; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outPtr = [null as any] + const result = this.wcdbGetContact(this.handle, username, outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取联系人失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析联系人失败' } + const contact = JSON.parse(jsonStr) + return { success: true, contact } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getAggregateStats(sessionIds: string[], beginTimestamp: number = 0, endTimestamp: number = 0): Promise<{ success: boolean; data?: any; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const normalizedBegin = this.normalizeTimestamp(beginTimestamp) + let normalizedEnd = this.normalizeTimestamp(endTimestamp) + if (normalizedEnd <= 0) { + normalizedEnd = this.normalizeTimestamp(Date.now()) + } + if (normalizedBegin > 0 && normalizedEnd < normalizedBegin) { + normalizedEnd = normalizedBegin + } + + const callAggregate = (ids: string[]) => { + const idsAreNumeric = ids.length > 0 && ids.every((id) => /^\d+$/.test(id)) + const payloadIds = idsAreNumeric ? ids.map((id) => Number(id)) : ids + + const outPtr = [null as any] + const result = this.wcdbGetAggregateStats(this.handle, JSON.stringify(payloadIds), normalizedBegin, normalizedEnd, outPtr) + + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取聚合统计失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) { + return { success: false, error: '解析聚合统计失败' } + } + + const data = JSON.parse(jsonStr) + return { success: true, data } + } + + let result = callAggregate(sessionIds) + if (result.success && result.data && result.data.total === 0 && result.data.idMap) { + const idMap = result.data.idMap as Record + const reverseMap: Record = {} + for (const [id, name] of Object.entries(idMap)) { + if (!name) continue + reverseMap[name] = id + } + const numericIds = sessionIds + .map((id) => reverseMap[id]) + .filter((id) => typeof id === 'string' && /^\d+$/.test(id)) + if (numericIds.length > 0) { + const retry = callAggregate(numericIds) + if (retry.success && retry.data) { + result = retry + } + } + } + + return result + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getAvailableYears(sessionIds: string[]): Promise<{ success: boolean; data?: number[]; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + if (!this.wcdbGetAvailableYears) { + return { success: false, error: '未支持获取年度列表' } + } + if (sessionIds.length === 0) return { success: true, data: [] } + try { + const outPtr = [null as any] + const result = this.wcdbGetAvailableYears(this.handle, JSON.stringify(sessionIds), outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取年度列表失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析年度列表失败' } + const data = JSON.parse(jsonStr) + return { success: true, data } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getAnnualReportStats(sessionIds: string[], beginTimestamp: number = 0, endTimestamp: number = 0): Promise<{ success: boolean; data?: any; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + if (!this.wcdbGetAnnualReportStats) { + return this.getAggregateStats(sessionIds, beginTimestamp, endTimestamp) + } + try { + const { begin, end } = this.normalizeRange(beginTimestamp, endTimestamp) + const outPtr = [null as any] + const result = this.wcdbGetAnnualReportStats(this.handle, JSON.stringify(sessionIds), begin, end, outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取年度统计失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析年度统计失败' } + const data = JSON.parse(jsonStr) + return { success: true, data } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getAnnualReportExtras( + sessionIds: string[], + beginTimestamp: number = 0, + endTimestamp: number = 0, + peakDayBegin: number = 0, + peakDayEnd: number = 0 + ): Promise<{ success: boolean; data?: any; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + if (!this.wcdbGetAnnualReportExtras) { + return { success: false, error: '未支持年度扩展统计' } + } + if (sessionIds.length === 0) return { success: true, data: {} } + try { + const { begin, end } = this.normalizeRange(beginTimestamp, endTimestamp) + const outPtr = [null as any] + const result = this.wcdbGetAnnualReportExtras( + this.handle, + JSON.stringify(sessionIds), + begin, + end, + this.normalizeTimestamp(peakDayBegin), + this.normalizeTimestamp(peakDayEnd), + outPtr + ) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取年度扩展统计失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析年度扩展统计失败' } + const data = JSON.parse(jsonStr) + return { success: true, data } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getGroupStats(chatroomId: string, beginTimestamp: number = 0, endTimestamp: number = 0): Promise<{ success: boolean; data?: any; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + if (!this.wcdbGetGroupStats) { + return this.getAggregateStats([chatroomId], beginTimestamp, endTimestamp) + } + try { + const { begin, end } = this.normalizeRange(beginTimestamp, endTimestamp) + const outPtr = [null as any] + const result = this.wcdbGetGroupStats(this.handle, chatroomId, begin, end, outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取群聊统计失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析群聊统计失败' } + const data = JSON.parse(jsonStr) + return { success: true, data } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async openMessageCursor(sessionId: string, batchSize: number, ascending: boolean, beginTimestamp: number, endTimestamp: number): Promise<{ success: boolean; cursor?: number; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outCursor = [0] + const result = this.wcdbOpenMessageCursor( + this.handle, + sessionId, + batchSize, + ascending ? 1 : 0, + beginTimestamp, + endTimestamp, + outCursor + ) + if (result !== 0 || outCursor[0] <= 0) { + await this.printLogs(true) + this.writeLog( + `openMessageCursor failed: sessionId=${sessionId} batchSize=${batchSize} ascending=${ascending ? 1 : 0} begin=${beginTimestamp} end=${endTimestamp} result=${result} cursor=${outCursor[0]}`, + true + ) + return { success: false, error: `创建游标失败: ${result},请查看日志` } + } + return { success: true, cursor: outCursor[0] } + } catch (e) { + await this.printLogs(true) + this.writeLog(`openMessageCursor exception: ${String(e)}`, true) + return { success: false, error: '创建游标异常,请查看日志' } + } + } + + async openMessageCursorLite(sessionId: string, batchSize: number, ascending: boolean, beginTimestamp: number, endTimestamp: number): Promise<{ success: boolean; cursor?: number; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + if (!this.wcdbOpenMessageCursorLite) { + return this.openMessageCursor(sessionId, batchSize, ascending, beginTimestamp, endTimestamp) + } + try { + const outCursor = [0] + const result = this.wcdbOpenMessageCursorLite( + this.handle, + sessionId, + batchSize, + ascending ? 1 : 0, + beginTimestamp, + endTimestamp, + outCursor + ) + if (result !== 0 || outCursor[0] <= 0) { + await this.printLogs(true) + this.writeLog( + `openMessageCursorLite failed: sessionId=${sessionId} batchSize=${batchSize} ascending=${ascending ? 1 : 0} begin=${beginTimestamp} end=${endTimestamp} result=${result} cursor=${outCursor[0]}`, + true + ) + return { success: false, error: `创建游标失败: ${result},请查看日志` } + } + return { success: true, cursor: outCursor[0] } + } catch (e) { + await this.printLogs(true) + this.writeLog(`openMessageCursorLite exception: ${String(e)}`, true) + return { success: false, error: '创建游标异常,请查看日志' } + } + } + + async fetchMessageBatch(cursor: number): Promise<{ success: boolean; rows?: any[]; hasMore?: boolean; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outPtr = [null as any] + const outHasMore = [0] + const result = this.wcdbFetchMessageBatch(this.handle, cursor, outPtr, outHasMore) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取批次失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析批次失败' } + const rows = JSON.parse(jsonStr) + return { success: true, rows, hasMore: outHasMore[0] === 1 } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async closeMessageCursor(cursor: number): Promise<{ success: boolean; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const result = this.wcdbCloseMessageCursor(this.handle, cursor) + if (result !== 0) { + return { success: false, error: `关闭游标失败: ${result}` } + } + return { success: true } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async execQuery(kind: string, path: string | null, sql: string): Promise<{ success: boolean; rows?: any[]; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outPtr = [null as any] + const result = this.wcdbExecQuery(this.handle, kind, path, sql, outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `执行查询失败: ${result}` } + } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析查询结果失败' } + const rows = JSON.parse(jsonStr) + return { success: true, rows } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getEmoticonCdnUrl(dbPath: string, md5: string): Promise<{ success: boolean; url?: string; error?: string }> { + if (!this.ensureReady()) { + return { success: false, error: 'WCDB 未连接' } + } + try { + const outPtr = [null as any] + const result = this.wcdbGetEmoticonCdnUrl(this.handle, dbPath, md5, outPtr) + if (result !== 0 || !outPtr[0]) { + return { success: false, error: `获取表情 URL 失败: ${result}` } + } + const urlStr = this.decodeJsonPtr(outPtr[0]) + if (urlStr === null) return { success: false, error: '解析表情 URL 失败' } + return { success: true, url: urlStr || undefined } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async listMessageDbs(): Promise<{ success: boolean; data?: string[]; error?: string }> { + if (!this.ensureReady()) return { success: false, error: 'WCDB 未连接' } + try { + const outPtr = [null as any] + const result = this.wcdbListMessageDbs(this.handle, outPtr) + if (result !== 0 || !outPtr[0]) return { success: false, error: `获取消息库列表失败: ${result}` } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析消息库列表失败' } + const data = JSON.parse(jsonStr) + return { success: true, data } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async listMediaDbs(): Promise<{ success: boolean; data?: string[]; error?: string }> { + if (!this.ensureReady()) return { success: false, error: 'WCDB 未连接' } + try { + const outPtr = [null as any] + const result = this.wcdbListMediaDbs(this.handle, outPtr) + if (result !== 0 || !outPtr[0]) return { success: false, error: `获取媒体库列表失败: ${result}` } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析媒体库列表失败' } + const data = JSON.parse(jsonStr) + return { success: true, data } + } catch (e) { + return { success: false, error: String(e) } + } + } + + async getMessageById(sessionId: string, localId: number): Promise<{ success: boolean; message?: any; error?: string }> { + if (!this.ensureReady()) return { success: false, error: 'WCDB 未连接' } + try { + const outPtr = [null as any] + const result = this.wcdbGetMessageById(this.handle, sessionId, localId, outPtr) + if (result !== 0 || !outPtr[0]) return { success: false, error: `查询消息失败: ${result}` } + const jsonStr = this.decodeJsonPtr(outPtr[0]) + if (!jsonStr) return { success: false, error: '解析消息失败' } + const message = JSON.parse(jsonStr) + // 处理 wcdb_get_message_by_id 返回空对象的情况 + if (Object.keys(message).length === 0) return { success: false, error: '未找到消息' } + return { success: true, message } + } catch (e) { + return { success: false, error: String(e) } + } + } +} + diff --git a/electron/services/wcdbService.ts b/electron/services/wcdbService.ts index 7e9a5d5..eacd90b 100644 --- a/electron/services/wcdbService.ts +++ b/electron/services/wcdbService.ts @@ -1,1285 +1,346 @@ -import { join, dirname, basename } from 'path' -import { appendFileSync, existsSync, mkdirSync, readdirSync, statSync, readFileSync } from 'fs' +import { Worker } from 'worker_threads' +import { join } from 'path' +import { existsSync } from 'fs' +/** + * Worker 消息接口 + */ +interface WorkerMessage { + id: number + result?: any + error?: string +} + +/** + * WCDB 服务 (客户端代理) + * 负责与后台 Worker 线程通信,执行数据库操作 + * 避免主进程阻塞 + */ export class WcdbService { + private worker: Worker | null = null + private messageId = 0 + private pending = new Map void; reject: (err: any) => void }>() private resourcesPath: string | null = null private userDataPath: string | null = null private logEnabled = false - private lib: any = null - private koffi: any = null - private initialized = false - private handle: number | null = null - private currentPath: string | null = null - private currentKey: string | null = null - private currentWxid: string | null = null - // 函数引用 - private wcdbInit: any = null - private wcdbShutdown: any = null - private wcdbOpenAccount: any = null - private wcdbCloseAccount: any = null - private wcdbSetMyWxid: any = null - private wcdbFreeString: any = null - private wcdbGetSessions: any = null - private wcdbGetMessages: any = null - private wcdbGetMessageCount: any = null - private wcdbGetDisplayNames: any = null - private wcdbGetAvatarUrls: any = null - private wcdbGetGroupMemberCount: any = null - private wcdbGetGroupMemberCounts: any = null - private wcdbGetGroupMembers: any = null - private wcdbGetMessageTables: any = null - private wcdbGetMessageMeta: any = null - private wcdbGetContact: any = null - private wcdbGetMessageTableStats: any = null - private wcdbGetAggregateStats: any = null - private wcdbGetAvailableYears: any = null - private wcdbGetAnnualReportStats: any = null - private wcdbGetAnnualReportExtras: any = null - private wcdbGetGroupStats: any = null - private wcdbOpenMessageCursor: any = null - private wcdbOpenMessageCursorLite: any = null - private wcdbFetchMessageBatch: any = null - private wcdbCloseMessageCursor: any = null - private wcdbGetLogs: any = null - private wcdbExecQuery: any = null - private wcdbListMessageDbs: any = null - private wcdbListMediaDbs: any = null - private wcdbGetMessageById: any = null - private wcdbGetEmoticonCdnUrl: any = null - private avatarUrlCache: Map = new Map() - private readonly avatarCacheTtlMs = 10 * 60 * 1000 - private logTimer: NodeJS.Timeout | null = null - private lastLogTail: string | null = null + constructor() { + this.initWorker() + } + /** + * 初始化 Worker 线程 + */ + private initWorker() { + if (this.worker) return + + const isDev = process.env.NODE_ENV === 'development' + const workerPath = isDev + ? join(__dirname, '../dist-electron/wcdbWorker.js') + : join(__dirname, 'wcdbWorker.js') + + let finalPath = workerPath + if (isDev && !existsSync(finalPath)) { + finalPath = join(__dirname, 'wcdbWorker.js') + } + + try { + this.worker = new Worker(finalPath) + + this.worker.on('message', (msg: WorkerMessage) => { + const { id, result, error } = msg + const p = this.pending.get(id) + if (p) { + this.pending.delete(id) + if (error) p.reject(new Error(error)) + else p.resolve(result) + } + }) + + this.worker.on('error', (err) => { + console.error('WCDB Worker 错误:', err) + }) + + this.worker.on('exit', (code) => { + if (code !== 0) console.error(`WCDB Worker 异常退出,退出码: ${code}`) + this.worker = null + }) + + // 如果已有路径配置,重新发送给新的 worker + if (this.resourcesPath && this.userDataPath) { + this.setPaths(this.resourcesPath, this.userDataPath) + } + this.setLogEnabled(this.logEnabled) + + } catch (e) { + console.error('创建 WCDB Worker 失败:', e) + } + } + + /** + * 发送消息到 Worker 并等待响应 + */ + private callWorker(type: string, payload: any = {}): Promise { + if (!this.worker) this.initWorker() + if (!this.worker) return Promise.reject(new Error('WCDB Worker 不可用')) + + return new Promise((resolve, reject) => { + const id = ++this.messageId + this.pending.set(id, { resolve, reject }) + this.worker!.postMessage({ id, type, payload }) + }) + } + + /** + * 设置资源路径 + */ setPaths(resourcesPath: string, userDataPath: string): void { this.resourcesPath = resourcesPath this.userDataPath = userDataPath + this.callWorker('setPaths', { resourcesPath, userDataPath }).catch(console.error) } + /** + * 启用/禁用日志 + */ setLogEnabled(enabled: boolean): void { this.logEnabled = enabled - if (this.isLogEnabled() && this.initialized) { - this.startLogPolling() - } else { - this.stopLogPolling() - } + this.callWorker('setLogEnabled', { enabled }).catch(console.error) } /** - * 获取 DLL 路径 + * 检查服务是否就绪 */ - private getDllPath(): string { - const envDllPath = process.env.WCDB_DLL_PATH - if (envDllPath && envDllPath.length > 0) { - return envDllPath - } - - const envResourcesPath = process.env.WCDB_RESOURCES_PATH - if (envResourcesPath && envResourcesPath.length > 0) { - return join(envResourcesPath, 'wcdb_api.dll') - } - - if (this.resourcesPath && this.resourcesPath.length > 0) { - return join(this.resourcesPath, 'wcdb_api.dll') - } - - const fallbackBase = process.resourcesPath || join(process.cwd(), 'resources') - return join(fallbackBase, 'wcdb_api.dll') + isReady(): boolean { + return !!this.worker } - private isLogEnabled(): boolean { - if (process.env.WEFLOW_WORKER === '1') return false - if (process.env.WCDB_LOG_ENABLED === '1') return true - return this.logEnabled - } - - private writeLog(message: string, force = false): void { - if (!force && !this.isLogEnabled()) return - try { - const base = this.userDataPath || process.env.WCDB_LOG_DIR || process.cwd() - const dir = join(base, 'logs') - if (!existsSync(dir)) mkdirSync(dir, { recursive: true }) - const line = `[${new Date().toISOString()}] ${message}\n` - appendFileSync(join(dir, 'wcdb.log'), line, { encoding: 'utf8' }) - } catch { } - } - - /** - * 递归查找 session.db 文件 - */ - private findSessionDb(dir: string, depth = 0): string | null { - if (depth > 5) return null - - try { - const entries = readdirSync(dir) - - for (const entry of entries) { - if (entry.toLowerCase() === 'session.db') { - const fullPath = join(dir, entry) - if (statSync(fullPath).isFile()) { - return fullPath - } - } - } - - for (const entry of entries) { - const fullPath = join(dir, entry) - try { - if (statSync(fullPath).isDirectory()) { - const found = this.findSessionDb(fullPath, depth + 1) - if (found) return found - } - } catch { } - } - } catch (e) { - console.error('查找 session.db 失败:', e) - } - - return null - } - - private resolveDbStoragePath(basePath: string, wxid: string): string | null { - if (!basePath) return null - const normalized = basePath.replace(/[\\\\/]+$/, '') - if (normalized.toLowerCase().endsWith('db_storage') && existsSync(normalized)) { - return normalized - } - const direct = join(normalized, 'db_storage') - if (existsSync(direct)) { - return direct - } - if (wxid) { - const viaWxid = join(normalized, wxid, 'db_storage') - if (existsSync(viaWxid)) { - return viaWxid - } - // 兼容目录名包含额外后缀(如 wxid_xxx_1234) - try { - const entries = readdirSync(normalized) - const lowerWxid = wxid.toLowerCase() - const candidates = entries.filter((entry) => { - const entryPath = join(normalized, entry) - try { - if (!statSync(entryPath).isDirectory()) return false - } catch { - return false - } - const lowerEntry = entry.toLowerCase() - return lowerEntry === lowerWxid || lowerEntry.startsWith(`${lowerWxid}_`) - }) - for (const entry of candidates) { - const candidate = join(normalized, entry, 'db_storage') - if (existsSync(candidate)) { - return candidate - } - } - } catch { } - } - return null - } - - /** - * 初始化 WCDB - */ - async initialize(): Promise { - if (this.initialized) return true - - try { - this.koffi = require('koffi') - const dllPath = this.getDllPath() - - if (!existsSync(dllPath)) { - console.error('WCDB DLL 不存在:', dllPath) - return false - } - - this.lib = this.koffi.load(dllPath) - - // 定义类型 - // wcdb_status wcdb_init() - this.wcdbInit = this.lib.func('int32 wcdb_init()') - - // wcdb_status wcdb_shutdown() - this.wcdbShutdown = this.lib.func('int32 wcdb_shutdown()') - - // wcdb_status wcdb_open_account(const char* session_db_path, const char* hex_key, wcdb_handle* out_handle) - // wcdb_handle 是 int64_t - this.wcdbOpenAccount = this.lib.func('int32 wcdb_open_account(const char* path, const char* key, _Out_ int64* handle)') - - // wcdb_status wcdb_close_account(wcdb_handle handle) - // C 接口是 int64, koffi 返回 handle 是 number 类型 - this.wcdbCloseAccount = this.lib.func('int32 wcdb_close_account(int64 handle)') - - // wcdb_status wcdb_set_my_wxid(wcdb_handle handle, const char* wxid) - try { - this.wcdbSetMyWxid = this.lib.func('int32 wcdb_set_my_wxid(int64 handle, const char* wxid)') - } catch { - this.wcdbSetMyWxid = null - } - - // void wcdb_free_string(char* ptr) - this.wcdbFreeString = this.lib.func('void wcdb_free_string(void* ptr)') - - // wcdb_status wcdb_get_sessions(wcdb_handle handle, char** out_json) - this.wcdbGetSessions = this.lib.func('int32 wcdb_get_sessions(int64 handle, _Out_ void** outJson)') - - // wcdb_status wcdb_get_messages(wcdb_handle handle, const char* username, int32_t limit, int32_t offset, char** out_json) - this.wcdbGetMessages = this.lib.func('int32 wcdb_get_messages(int64 handle, const char* username, int32 limit, int32 offset, _Out_ void** outJson)') - - // wcdb_status wcdb_get_message_count(wcdb_handle handle, const char* username, int32_t* out_count) - this.wcdbGetMessageCount = this.lib.func('int32 wcdb_get_message_count(int64 handle, const char* username, _Out_ int32* outCount)') - - // wcdb_status wcdb_get_display_names(wcdb_handle handle, const char* usernames_json, char** out_json) - this.wcdbGetDisplayNames = this.lib.func('int32 wcdb_get_display_names(int64 handle, const char* usernamesJson, _Out_ void** outJson)') - - // wcdb_status wcdb_get_avatar_urls(wcdb_handle handle, const char* usernames_json, char** out_json) - this.wcdbGetAvatarUrls = this.lib.func('int32 wcdb_get_avatar_urls(int64 handle, const char* usernamesJson, _Out_ void** outJson)') - - // wcdb_status wcdb_get_group_member_count(wcdb_handle handle, const char* chatroom_id, int32_t* out_count) - this.wcdbGetGroupMemberCount = this.lib.func('int32 wcdb_get_group_member_count(int64 handle, const char* chatroomId, _Out_ int32* outCount)') - - // wcdb_status wcdb_get_group_member_counts(wcdb_handle handle, const char* chatroom_ids_json, char** out_json) - try { - this.wcdbGetGroupMemberCounts = this.lib.func('int32 wcdb_get_group_member_counts(int64 handle, const char* chatroomIdsJson, _Out_ void** outJson)') - } catch { - this.wcdbGetGroupMemberCounts = null - } - - // wcdb_status wcdb_get_group_members(wcdb_handle handle, const char* chatroom_id, char** out_json) - this.wcdbGetGroupMembers = this.lib.func('int32 wcdb_get_group_members(int64 handle, const char* chatroomId, _Out_ void** outJson)') - - // wcdb_status wcdb_get_message_tables(wcdb_handle handle, const char* session_id, char** out_json) - this.wcdbGetMessageTables = this.lib.func('int32 wcdb_get_message_tables(int64 handle, const char* sessionId, _Out_ void** outJson)') - - // wcdb_status wcdb_get_message_meta(wcdb_handle handle, const char* db_path, const char* table_name, int32_t limit, int32_t offset, char** out_json) - this.wcdbGetMessageMeta = this.lib.func('int32 wcdb_get_message_meta(int64 handle, const char* dbPath, const char* tableName, int32 limit, int32 offset, _Out_ void** outJson)') - - // wcdb_status wcdb_get_contact(wcdb_handle handle, const char* username, char** out_json) - this.wcdbGetContact = this.lib.func('int32 wcdb_get_contact(int64 handle, const char* username, _Out_ void** outJson)') - - // wcdb_status wcdb_get_message_table_stats(wcdb_handle handle, const char* session_id, char** out_json) - this.wcdbGetMessageTableStats = this.lib.func('int32 wcdb_get_message_table_stats(int64 handle, const char* sessionId, _Out_ void** outJson)') - - // wcdb_status wcdb_get_aggregate_stats(wcdb_handle handle, const char* session_ids_json, int32_t begin_timestamp, int32_t end_timestamp, char** out_json) - this.wcdbGetAggregateStats = this.lib.func('int32 wcdb_get_aggregate_stats(int64 handle, const char* sessionIdsJson, int32 begin, int32 end, _Out_ void** outJson)') - - // wcdb_status wcdb_get_available_years(wcdb_handle handle, const char* session_ids_json, char** out_json) - try { - this.wcdbGetAvailableYears = this.lib.func('int32 wcdb_get_available_years(int64 handle, const char* sessionIdsJson, _Out_ void** outJson)') - } catch { - this.wcdbGetAvailableYears = null - } - - // wcdb_status wcdb_get_annual_report_stats(wcdb_handle handle, const char* session_ids_json, int32_t begin_timestamp, int32_t end_timestamp, char** out_json) - try { - this.wcdbGetAnnualReportStats = this.lib.func('int32 wcdb_get_annual_report_stats(int64 handle, const char* sessionIdsJson, int32 begin, int32 end, _Out_ void** outJson)') - } catch { - this.wcdbGetAnnualReportStats = null - } - - // wcdb_status wcdb_get_annual_report_extras(wcdb_handle handle, const char* session_ids_json, int32_t begin_timestamp, int32_t end_timestamp, int32_t peak_day_begin, int32_t peak_day_end, char** out_json) - try { - this.wcdbGetAnnualReportExtras = this.lib.func('int32 wcdb_get_annual_report_extras(int64 handle, const char* sessionIdsJson, int32 begin, int32 end, int32 peakBegin, int32 peakEnd, _Out_ void** outJson)') - } catch { - this.wcdbGetAnnualReportExtras = null - } - - // wcdb_status wcdb_get_group_stats(wcdb_handle handle, const char* chatroom_id, int32_t begin_timestamp, int32_t end_timestamp, char** out_json) - try { - this.wcdbGetGroupStats = this.lib.func('int32 wcdb_get_group_stats(int64 handle, const char* chatroomId, int32 begin, int32 end, _Out_ void** outJson)') - } catch { - this.wcdbGetGroupStats = null - } - - // wcdb_status wcdb_open_message_cursor(wcdb_handle handle, const char* session_id, int32_t batch_size, int32_t ascending, int32_t begin_timestamp, int32_t end_timestamp, wcdb_cursor* out_cursor) - this.wcdbOpenMessageCursor = this.lib.func('int32 wcdb_open_message_cursor(int64 handle, const char* sessionId, int32 batchSize, int32 ascending, int32 beginTimestamp, int32 endTimestamp, _Out_ int64* outCursor)') - - // wcdb_status wcdb_open_message_cursor_lite(wcdb_handle handle, const char* session_id, int32_t batch_size, int32_t ascending, int32_t begin_timestamp, int32_t end_timestamp, wcdb_cursor* out_cursor) - try { - this.wcdbOpenMessageCursorLite = this.lib.func('int32 wcdb_open_message_cursor_lite(int64 handle, const char* sessionId, int32 batchSize, int32 ascending, int32 beginTimestamp, int32 endTimestamp, _Out_ int64* outCursor)') - } catch { - this.wcdbOpenMessageCursorLite = null - } - - // wcdb_status wcdb_fetch_message_batch(wcdb_handle handle, wcdb_cursor cursor, char** out_json, int32_t* out_has_more) - this.wcdbFetchMessageBatch = this.lib.func('int32 wcdb_fetch_message_batch(int64 handle, int64 cursor, _Out_ void** outJson, _Out_ int32* outHasMore)') - - // wcdb_status wcdb_close_message_cursor(wcdb_handle handle, wcdb_cursor cursor) - this.wcdbCloseMessageCursor = this.lib.func('int32 wcdb_close_message_cursor(int64 handle, int64 cursor)') - - // wcdb_status wcdb_get_logs(char** out_json) - this.wcdbGetLogs = this.lib.func('int32 wcdb_get_logs(_Out_ void** outJson)') - - // wcdb_status wcdb_exec_query(wcdb_handle handle, const char* db_kind, const char* db_path, const char* sql, char** out_json) - this.wcdbExecQuery = this.lib.func('int32 wcdb_exec_query(int64 handle, const char* kind, const char* path, const char* sql, _Out_ void** outJson)') - - // wcdb_status wcdb_get_emoticon_cdn_url(wcdb_handle handle, const char* db_path, const char* md5, char** out_url) - this.wcdbGetEmoticonCdnUrl = this.lib.func('int32 wcdb_get_emoticon_cdn_url(int64 handle, const char* dbPath, const char* md5, _Out_ void** outUrl)') - - // wcdb_status wcdb_list_message_dbs(wcdb_handle handle, char** out_json) - this.wcdbListMessageDbs = this.lib.func('int32 wcdb_list_message_dbs(int64 handle, _Out_ void** outJson)') - - // wcdb_status wcdb_list_media_dbs(wcdb_handle handle, char** out_json) - this.wcdbListMediaDbs = this.lib.func('int32 wcdb_list_media_dbs(int64 handle, _Out_ void** outJson)') - - // wcdb_status wcdb_get_message_by_id(wcdb_handle handle, const char* session_id, int32 local_id, char** out_json) - this.wcdbGetMessageById = this.lib.func('int32 wcdb_get_message_by_id(int64 handle, const char* sessionId, int32 localId, _Out_ void** outJson)') - - // 初始化 - const initResult = this.wcdbInit() - if (initResult !== 0) { - console.error('WCDB 初始化失败:', initResult) - return false - } - - this.initialized = true - return true - } catch (e) { - console.error('WCDB 初始化异常:', e) - return false - } - } + // ========================================== + // 代理方法 (Proxy Methods) + // ========================================== /** * 测试数据库连接 */ async testConnection(dbPath: string, hexKey: string, wxid: string): Promise<{ success: boolean; error?: string; sessionCount?: number }> { - try { - // 如果当前已经有相同参数的活动连接,直接返回成功 - if (this.handle !== null && - this.currentPath === dbPath && - this.currentKey === hexKey && - this.currentWxid === wxid) { - return { success: true, sessionCount: 0 } - } - - if (!this.initialized) { - const initOk = await this.initialize() - if (!initOk) { - return { success: false, error: 'WCDB 初始化失败' } - } - } - - // 构建 db_storage 目录路径 - const dbStoragePath = this.resolveDbStoragePath(dbPath, wxid) - this.writeLog(`testConnection dbPath=${dbPath} wxid=${wxid} dbStorage=${dbStoragePath || 'null'}`) - - if (!dbStoragePath || !existsSync(dbStoragePath)) { - return { success: false, error: `数据库目录不存在: ${dbPath}` } - } - - // 递归查找 session.db - const sessionDbPath = this.findSessionDb(dbStoragePath) - this.writeLog(`testConnection sessionDb=${sessionDbPath || 'null'}`) - - if (!sessionDbPath) { - return { success: false, error: `未找到 session.db 文件` } - } - - // 分配输出参数内存 - const handleOut = [0] - const result = this.wcdbOpenAccount(sessionDbPath, hexKey, handleOut) - - if (result !== 0) { - await this.printLogs() - let errorMsg = '数据库打开失败' - if (result === -1) errorMsg = '参数错误' - else if (result === -2) errorMsg = '密钥错误' - else if (result === -3) errorMsg = '数据库打开失败' - this.writeLog(`testConnection openAccount failed code=${result}`) - return { success: false, error: `${errorMsg} (错误码: ${result})` } - } - - const tempHandle = handleOut[0] - if (tempHandle <= 0) { - return { success: false, error: '无效的数据库句柄' } - } - - // 测试成功,使用 shutdown 清理所有资源(包括测试句柄) - // 这会中断当前活动连接,但 testConnection 本应该是独立测试 - try { - this.wcdbShutdown() - this.handle = null - this.currentPath = null - this.currentKey = null - this.currentWxid = null - this.initialized = false - } catch (closeErr) { - console.error('关闭测试数据库时出错:', closeErr) - } - - return { success: true, sessionCount: 0 } - } catch (e) { - console.error('测试连接异常:', e) - this.writeLog(`testConnection exception: ${String(e)}`) - return { success: false, error: String(e) } - } - } - - /** - * 打印 DLL 内部日志(仅在出错时调用) - */ - private async printLogs(force = false): Promise { - try { - if (!this.wcdbGetLogs) return - const outPtr = [null as any] - const result = this.wcdbGetLogs(outPtr) - if (result === 0 && outPtr[0]) { - try { - const jsonStr = this.koffi.decode(outPtr[0], 'char', -1) - this.writeLog(`wcdb_logs: ${jsonStr}`, force) - this.wcdbFreeString(outPtr[0]) - } catch (e) { - // ignore - } - } - } catch (e) { - console.error('获取日志失败:', e) - this.writeLog(`wcdb_logs failed: ${String(e)}`, force) - } - } - - private startLogPolling(): void { - if (this.logTimer || !this.isLogEnabled()) return - this.logTimer = setInterval(() => { - void this.pollLogs() - }, 2000) - } - - private stopLogPolling(): void { - if (this.logTimer) { - clearInterval(this.logTimer) - this.logTimer = null - } - this.lastLogTail = null - } - - private async pollLogs(): Promise { - try { - if (!this.wcdbGetLogs || !this.isLogEnabled()) return - const outPtr = [null as any] - const result = this.wcdbGetLogs(outPtr) - if (result !== 0 || !outPtr[0]) return - let jsonStr = '' - try { - jsonStr = this.koffi.decode(outPtr[0], 'char', -1) - } finally { - try { this.wcdbFreeString(outPtr[0]) } catch { } - } - const logs = JSON.parse(jsonStr) as string[] - if (!Array.isArray(logs) || logs.length === 0) return - let startIdx = 0 - if (this.lastLogTail) { - const idx = logs.lastIndexOf(this.lastLogTail) - if (idx >= 0) startIdx = idx + 1 - } - for (let i = startIdx; i < logs.length; i += 1) { - this.writeLog(`wcdb: ${logs[i]}`) - } - this.lastLogTail = logs[logs.length - 1] - } catch (e) { - // ignore polling errors - } - } - - private decodeJsonPtr(outPtr: any): string | null { - if (!outPtr) return null - try { - const jsonStr = this.koffi.decode(outPtr, 'char', -1) - this.wcdbFreeString(outPtr) - return jsonStr - } catch (e) { - try { this.wcdbFreeString(outPtr) } catch { } - return null - } - } - - private ensureReady(): boolean { - return this.initialized && this.handle !== null - } - - private normalizeTimestamp(input: number): number { - if (!input || input <= 0) return 0 - const asNumber = Number(input) - if (!Number.isFinite(asNumber)) return 0 - // Treat >1e12 as milliseconds. - const seconds = asNumber > 1e12 ? Math.floor(asNumber / 1000) : Math.floor(asNumber) - const maxInt32 = 2147483647 - return Math.min(Math.max(seconds, 0), maxInt32) - } - - private normalizeRange(beginTimestamp: number, endTimestamp: number): { begin: number; end: number } { - const normalizedBegin = this.normalizeTimestamp(beginTimestamp) - let normalizedEnd = this.normalizeTimestamp(endTimestamp) - if (normalizedEnd <= 0) { - normalizedEnd = this.normalizeTimestamp(Date.now()) - } - if (normalizedBegin > 0 && normalizedEnd < normalizedBegin) { - normalizedEnd = normalizedBegin - } - return { begin: normalizedBegin, end: normalizedEnd } - } - - isReady(): boolean { - return this.ensureReady() + return this.callWorker('testConnection', { dbPath, hexKey, wxid }) } /** * 打开数据库 */ async open(dbPath: string, hexKey: string, wxid: string): Promise { - try { - if (!this.initialized) { - const initOk = await this.initialize() - if (!initOk) return false - } - - // 检查是否已经是当前连接的参数,如果是则直接返回成功,实现"始终保持链接" - if (this.handle !== null && - this.currentPath === dbPath && - this.currentKey === hexKey && - this.currentWxid === wxid) { - return true - } - - // 如果参数不同,则先关闭原来的连接 - if (this.handle !== null) { - this.close() - // 重新初始化,因为 close 呼叫了 shutdown - const initOk = await this.initialize() - if (!initOk) return false - } - - const dbStoragePath = this.resolveDbStoragePath(dbPath, wxid) - this.writeLog(`open dbPath=${dbPath} wxid=${wxid} dbStorage=${dbStoragePath || 'null'}`) - - if (!dbStoragePath || !existsSync(dbStoragePath)) { - console.error('数据库目录不存在:', dbPath) - this.writeLog(`open failed: dbStorage not found for ${dbPath}`) - return false - } - - const sessionDbPath = this.findSessionDb(dbStoragePath) - this.writeLog(`open sessionDb=${sessionDbPath || 'null'}`) - if (!sessionDbPath) { - console.error('未找到 session.db 文件') - this.writeLog('open failed: session.db not found') - return false - } - - const handleOut = [0] - const result = this.wcdbOpenAccount(sessionDbPath, hexKey, handleOut) - - if (result !== 0) { - console.error('打开数据库失败:', result) - await this.printLogs() - this.writeLog(`open failed: openAccount code=${result}`) - return false - } - - const handle = handleOut[0] - if (handle <= 0) { - return false - } - - this.handle = handle - this.currentPath = dbPath - this.currentKey = hexKey - this.currentWxid = wxid - this.initialized = true - if (this.wcdbSetMyWxid && wxid) { - try { - this.wcdbSetMyWxid(this.handle, wxid) - } catch (e) { - console.warn('设置 wxid 失败:', e) - } - } - if (this.isLogEnabled()) { - this.startLogPolling() - } - this.writeLog(`open ok handle=${handle}`) - return true - } catch (e) { - console.error('打开数据库异常:', e) - this.writeLog(`open exception: ${String(e)}`) - return false - } + return this.callWorker('open', { dbPath, hexKey, wxid }) } /** - * 关闭数据库 - * 注意:wcdb_close_account 可能导致崩溃,使用 shutdown 代替 + * 关闭数据库连接 */ - close(): void { - if (this.handle !== null || this.initialized) { - try { - // 不调用 closeAccount,直接 shutdown - this.wcdbShutdown() - } catch (e) { - console.error('WCDB shutdown 出错:', e) - } - this.handle = null - this.currentPath = null - this.currentKey = null - this.currentWxid = null - this.initialized = false - this.stopLogPolling() - } + async close(): Promise { + return this.callWorker('close') } /** - * 关闭服务(与 close 相同) + * 关闭服务 */ shutdown(): void { this.close() + if (this.worker) { + this.worker.terminate() + this.worker = null + } } /** - * 检查是否已连接 + * 获取数据库连接状态 + * 注意:此方法现在是异步的 */ - isConnected(): boolean { - return this.initialized && this.handle !== null + async isConnected(): Promise { + return this.callWorker('isConnected') } + /** + * 获取会话列表 + */ async getSessions(): Promise<{ success: boolean; sessions?: any[]; error?: string }> { - if (!this.ensureReady()) { - this.writeLog('getSessions skipped: not connected') - return { success: false, error: 'WCDB 未连接' } - } - try { - // 使用 setImmediate 让事件循环有机会处理其他任务,避免长时间阻塞 - await new Promise(resolve => setImmediate(resolve)) - - const outPtr = [null as any] - const result = this.wcdbGetSessions(this.handle, outPtr) - - // DLL 调用后再次让出控制权 - await new Promise(resolve => setImmediate(resolve)) - - if (result !== 0 || !outPtr[0]) { - this.writeLog(`getSessions failed: code=${result}`) - return { success: false, error: `获取会话失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析会话失败' } - this.writeLog(`getSessions ok size=${jsonStr.length}`) - const sessions = JSON.parse(jsonStr) - return { success: true, sessions } - } catch (e) { - this.writeLog(`getSessions exception: ${String(e)}`) - return { success: false, error: String(e) } - } + return this.callWorker('getSessions') } + /** + * 获取消息列表 + */ async getMessages(sessionId: string, limit: number, offset: number): Promise<{ success: boolean; messages?: any[]; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outPtr = [null as any] - const result = this.wcdbGetMessages(this.handle, sessionId, limit, offset, outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取消息失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析消息失败' } - const messages = JSON.parse(jsonStr) - return { success: true, messages } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getMessages', { sessionId, limit, offset }) } + /** + * 获取消息总数 + */ async getMessageCount(sessionId: string): Promise<{ success: boolean; count?: number; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outCount = [0] - const result = this.wcdbGetMessageCount(this.handle, sessionId, outCount) - if (result !== 0) { - return { success: false, error: `获取消息总数失败: ${result}` } - } - return { success: true, count: outCount[0] } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getMessageCount', { sessionId }) } + /** + * 获取联系人昵称 + */ async getDisplayNames(usernames: string[]): Promise<{ success: boolean; map?: Record; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - if (usernames.length === 0) return { success: true, map: {} } - try { - // 让出控制权,避免阻塞事件循环 - await new Promise(resolve => setImmediate(resolve)) - - const outPtr = [null as any] - const result = this.wcdbGetDisplayNames(this.handle, JSON.stringify(usernames), outPtr) - - // DLL 调用后再次让出控制权 - await new Promise(resolve => setImmediate(resolve)) - - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取昵称失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析昵称失败' } - const map = JSON.parse(jsonStr) - return { success: true, map } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getDisplayNames', { usernames }) } + /** + * 获取头像 URL + */ async getAvatarUrls(usernames: string[]): Promise<{ success: boolean; map?: Record; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - if (usernames.length === 0) return { success: true, map: {} } - try { - const now = Date.now() - const resultMap: Record = {} - const toFetch: string[] = [] - const seen = new Set() - - for (const username of usernames) { - if (!username || seen.has(username)) continue - seen.add(username) - const cached = this.avatarUrlCache.get(username) - if (cached && cached.url && now - cached.updatedAt < this.avatarCacheTtlMs) { - resultMap[username] = cached.url - continue - } - toFetch.push(username) - } - - if (toFetch.length === 0) { - return { success: true, map: resultMap } - } - - // 让出控制权,避免阻塞事件循环 - await new Promise(resolve => setImmediate(resolve)) - - const outPtr = [null as any] - const result = this.wcdbGetAvatarUrls(this.handle, JSON.stringify(toFetch), outPtr) - - // DLL 调用后再次让出控制权 - await new Promise(resolve => setImmediate(resolve)) - - if (result !== 0 || !outPtr[0]) { - if (Object.keys(resultMap).length > 0) { - return { success: true, map: resultMap, error: `获取头像失败: ${result}` } - } - return { success: false, error: `获取头像失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析头像失败' } - const map = JSON.parse(jsonStr) as Record - for (const username of toFetch) { - const url = map[username] - if (url) { - resultMap[username] = url - this.avatarUrlCache.set(username, { url, updatedAt: now }) - } - } - return { success: true, map: resultMap } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getAvatarUrls', { usernames }) } + /** + * 获取群成员数量 + */ async getGroupMemberCount(chatroomId: string): Promise<{ success: boolean; count?: number; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outCount = [0] - const result = this.wcdbGetGroupMemberCount(this.handle, chatroomId, outCount) - if (result !== 0) { - return { success: false, error: `获取群成员数量失败: ${result}` } - } - return { success: true, count: outCount[0] } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getGroupMemberCount', { chatroomId }) } + /** + * 批量获取群成员数量 + */ async getGroupMemberCounts(chatroomIds: string[]): Promise<{ success: boolean; map?: Record; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - if (chatroomIds.length === 0) return { success: true, map: {} } - if (!this.wcdbGetGroupMemberCounts) { - const map: Record = {} - for (const chatroomId of chatroomIds) { - const result = await this.getGroupMemberCount(chatroomId) - if (result.success && typeof result.count === 'number') { - map[chatroomId] = result.count - } - } - return { success: true, map } - } - try { - const outPtr = [null as any] - const result = this.wcdbGetGroupMemberCounts(this.handle, JSON.stringify(chatroomIds), outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取群成员数量失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析群成员数量失败' } - const map = JSON.parse(jsonStr) - return { success: true, map } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getGroupMemberCounts', { chatroomIds }) } + /** + * 获取群成员列表 + */ async getGroupMembers(chatroomId: string): Promise<{ success: boolean; members?: any[]; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outPtr = [null as any] - const result = this.wcdbGetGroupMembers(this.handle, chatroomId, outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取群成员失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析群成员失败' } - const members = JSON.parse(jsonStr) - return { success: true, members } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getGroupMembers', { chatroomId }) } + /** + * 获取消息表列表 + */ async getMessageTables(sessionId: string): Promise<{ success: boolean; tables?: any[]; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outPtr = [null as any] - const result = this.wcdbGetMessageTables(this.handle, sessionId, outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取消息表失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析消息表失败' } - const tables = JSON.parse(jsonStr) - return { success: true, tables } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getMessageTables', { sessionId }) } + /** + * 获取消息表统计 + */ async getMessageTableStats(sessionId: string): Promise<{ success: boolean; tables?: any[]; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outPtr = [null as any] - const result = this.wcdbGetMessageTableStats(this.handle, sessionId, outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取表统计失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析表统计失败' } - const tables = JSON.parse(jsonStr) - return { success: true, tables } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getMessageTableStats', { sessionId }) } + /** + * 获取消息元数据 + */ async getMessageMeta(dbPath: string, tableName: string, limit: number, offset: number): Promise<{ success: boolean; rows?: any[]; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outPtr = [null as any] - const result = this.wcdbGetMessageMeta(this.handle, dbPath, tableName, limit, offset, outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取消息元数据失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析消息元数据失败' } - const rows = JSON.parse(jsonStr) - return { success: true, rows } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getMessageMeta', { dbPath, tableName, limit, offset }) } + /** + * 获取联系人详情 + */ async getContact(username: string): Promise<{ success: boolean; contact?: any; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outPtr = [null as any] - const result = this.wcdbGetContact(this.handle, username, outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取联系人失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析联系人失败' } - const contact = JSON.parse(jsonStr) - return { success: true, contact } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getContact', { username }) } + /** + * 获取聚合统计数据 + */ async getAggregateStats(sessionIds: string[], beginTimestamp: number = 0, endTimestamp: number = 0): Promise<{ success: boolean; data?: any; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const normalizedBegin = this.normalizeTimestamp(beginTimestamp) - let normalizedEnd = this.normalizeTimestamp(endTimestamp) - if (normalizedEnd <= 0) { - normalizedEnd = this.normalizeTimestamp(Date.now()) - } - if (normalizedBegin > 0 && normalizedEnd < normalizedBegin) { - normalizedEnd = normalizedBegin - } - - const callAggregate = (ids: string[]) => { - const idsAreNumeric = ids.length > 0 && ids.every((id) => /^\d+$/.test(id)) - const payloadIds = idsAreNumeric ? ids.map((id) => Number(id)) : ids - - const outPtr = [null as any] - const result = this.wcdbGetAggregateStats(this.handle, JSON.stringify(payloadIds), normalizedBegin, normalizedEnd, outPtr) - - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取聚合统计失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) { - return { success: false, error: '解析聚合统计失败' } - } - - const data = JSON.parse(jsonStr) - return { success: true, data } - } - - let result = callAggregate(sessionIds) - if (result.success && result.data && result.data.total === 0 && result.data.idMap) { - const idMap = result.data.idMap as Record - const reverseMap: Record = {} - for (const [id, name] of Object.entries(idMap)) { - if (!name) continue - reverseMap[name] = id - } - const numericIds = sessionIds - .map((id) => reverseMap[id]) - .filter((id) => typeof id === 'string' && /^\d+$/.test(id)) - if (numericIds.length > 0) { - const retry = callAggregate(numericIds) - if (retry.success && retry.data) { - result = retry - } - } - } - - return result - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getAggregateStats', { sessionIds, beginTimestamp, endTimestamp }) } + /** + * 获取可用年份 + */ async getAvailableYears(sessionIds: string[]): Promise<{ success: boolean; data?: number[]; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - if (!this.wcdbGetAvailableYears) { - return { success: false, error: '未支持获取年度列表' } - } - if (sessionIds.length === 0) return { success: true, data: [] } - try { - const outPtr = [null as any] - const result = this.wcdbGetAvailableYears(this.handle, JSON.stringify(sessionIds), outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取年度列表失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析年度列表失败' } - const data = JSON.parse(jsonStr) - return { success: true, data } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getAvailableYears', { sessionIds }) } + /** + * 获取年度报告统计 + */ async getAnnualReportStats(sessionIds: string[], beginTimestamp: number = 0, endTimestamp: number = 0): Promise<{ success: boolean; data?: any; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - if (!this.wcdbGetAnnualReportStats) { - return this.getAggregateStats(sessionIds, beginTimestamp, endTimestamp) - } - try { - const { begin, end } = this.normalizeRange(beginTimestamp, endTimestamp) - const outPtr = [null as any] - const result = this.wcdbGetAnnualReportStats(this.handle, JSON.stringify(sessionIds), begin, end, outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取年度统计失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析年度统计失败' } - const data = JSON.parse(jsonStr) - return { success: true, data } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getAnnualReportStats', { sessionIds, beginTimestamp, endTimestamp }) } - async getAnnualReportExtras( - sessionIds: string[], - beginTimestamp: number = 0, - endTimestamp: number = 0, - peakDayBegin: number = 0, - peakDayEnd: number = 0 - ): Promise<{ success: boolean; data?: any; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - if (!this.wcdbGetAnnualReportExtras) { - return { success: false, error: '未支持年度扩展统计' } - } - if (sessionIds.length === 0) return { success: true, data: {} } - try { - const { begin, end } = this.normalizeRange(beginTimestamp, endTimestamp) - const outPtr = [null as any] - const result = this.wcdbGetAnnualReportExtras( - this.handle, - JSON.stringify(sessionIds), - begin, - end, - this.normalizeTimestamp(peakDayBegin), - this.normalizeTimestamp(peakDayEnd), - outPtr - ) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取年度扩展统计失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析年度扩展统计失败' } - const data = JSON.parse(jsonStr) - return { success: true, data } - } catch (e) { - return { success: false, error: String(e) } - } + /** + * 获取年度报告扩展数据 + */ + async getAnnualReportExtras(sessionIds: string[], beginTimestamp: number, endTimestamp: number, peakDayBegin: number, peakDayEnd: number): Promise<{ success: boolean; data?: any; error?: string }> { + return this.callWorker('getAnnualReportExtras', { sessionIds, beginTimestamp, endTimestamp, peakDayBegin, peakDayEnd }) } + /** + * 获取群聊统计 + */ async getGroupStats(chatroomId: string, beginTimestamp: number = 0, endTimestamp: number = 0): Promise<{ success: boolean; data?: any; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - if (!this.wcdbGetGroupStats) { - return this.getAggregateStats([chatroomId], beginTimestamp, endTimestamp) - } - try { - const { begin, end } = this.normalizeRange(beginTimestamp, endTimestamp) - const outPtr = [null as any] - const result = this.wcdbGetGroupStats(this.handle, chatroomId, begin, end, outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取群聊统计失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析群聊统计失败' } - const data = JSON.parse(jsonStr) - return { success: true, data } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getGroupStats', { chatroomId, beginTimestamp, endTimestamp }) } + /** + * 打开消息游标 + */ async openMessageCursor(sessionId: string, batchSize: number, ascending: boolean, beginTimestamp: number, endTimestamp: number): Promise<{ success: boolean; cursor?: number; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outCursor = [0] - const result = this.wcdbOpenMessageCursor( - this.handle, - sessionId, - batchSize, - ascending ? 1 : 0, - beginTimestamp, - endTimestamp, - outCursor - ) - if (result !== 0 || outCursor[0] <= 0) { - await this.printLogs(true) - this.writeLog( - `openMessageCursor failed: sessionId=${sessionId} batchSize=${batchSize} ascending=${ascending ? 1 : 0} begin=${beginTimestamp} end=${endTimestamp} result=${result} cursor=${outCursor[0]}`, - true - ) - return { success: false, error: `创建游标失败: ${result},请查看日志` } - } - return { success: true, cursor: outCursor[0] } - } catch (e) { - await this.printLogs(true) - this.writeLog(`openMessageCursor exception: ${String(e)}`, true) - return { success: false, error: '创建游标异常,请查看日志' } - } + return this.callWorker('openMessageCursor', { sessionId, batchSize, ascending, beginTimestamp, endTimestamp }) } + /** + * 打开轻量级消息游标 + */ async openMessageCursorLite(sessionId: string, batchSize: number, ascending: boolean, beginTimestamp: number, endTimestamp: number): Promise<{ success: boolean; cursor?: number; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - if (!this.wcdbOpenMessageCursorLite) { - return this.openMessageCursor(sessionId, batchSize, ascending, beginTimestamp, endTimestamp) - } - try { - const outCursor = [0] - const result = this.wcdbOpenMessageCursorLite( - this.handle, - sessionId, - batchSize, - ascending ? 1 : 0, - beginTimestamp, - endTimestamp, - outCursor - ) - if (result !== 0 || outCursor[0] <= 0) { - await this.printLogs(true) - this.writeLog( - `openMessageCursorLite failed: sessionId=${sessionId} batchSize=${batchSize} ascending=${ascending ? 1 : 0} begin=${beginTimestamp} end=${endTimestamp} result=${result} cursor=${outCursor[0]}`, - true - ) - return { success: false, error: `创建游标失败: ${result},请查看日志` } - } - return { success: true, cursor: outCursor[0] } - } catch (e) { - await this.printLogs(true) - this.writeLog(`openMessageCursorLite exception: ${String(e)}`, true) - return { success: false, error: '创建游标异常,请查看日志' } - } + return this.callWorker('openMessageCursorLite', { sessionId, batchSize, ascending, beginTimestamp, endTimestamp }) } + /** + * 获取下一批消息 + */ async fetchMessageBatch(cursor: number): Promise<{ success: boolean; rows?: any[]; hasMore?: boolean; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outPtr = [null as any] - const outHasMore = [0] - const result = this.wcdbFetchMessageBatch(this.handle, cursor, outPtr, outHasMore) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取批次失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析批次失败' } - const rows = JSON.parse(jsonStr) - return { success: true, rows, hasMore: outHasMore[0] === 1 } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('fetchMessageBatch', { cursor }) } + /** + * 关闭消息游标 + */ async closeMessageCursor(cursor: number): Promise<{ success: boolean; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const result = this.wcdbCloseMessageCursor(this.handle, cursor) - if (result !== 0) { - return { success: false, error: `关闭游标失败: ${result}` } - } - return { success: true } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('closeMessageCursor', { cursor }) } + /** + * 执行 SQL 查询 + */ async execQuery(kind: string, path: string | null, sql: string): Promise<{ success: boolean; rows?: any[]; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outPtr = [null as any] - const result = this.wcdbExecQuery(this.handle, kind, path, sql, outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `执行查询失败: ${result}` } - } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析查询结果失败' } - const rows = JSON.parse(jsonStr) - return { success: true, rows } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('execQuery', { kind, path, sql }) } + /** + * 获取表情包 CDN URL + */ async getEmoticonCdnUrl(dbPath: string, md5: string): Promise<{ success: boolean; url?: string; error?: string }> { - if (!this.ensureReady()) { - return { success: false, error: 'WCDB 未连接' } - } - try { - const outPtr = [null as any] - const result = this.wcdbGetEmoticonCdnUrl(this.handle, dbPath, md5, outPtr) - if (result !== 0 || !outPtr[0]) { - return { success: false, error: `获取表情 URL 失败: ${result}` } - } - const urlStr = this.decodeJsonPtr(outPtr[0]) - if (urlStr === null) return { success: false, error: '解析表情 URL 失败' } - return { success: true, url: urlStr || undefined } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getEmoticonCdnUrl', { dbPath, md5 }) } + /** + * 列出消息数据库 + */ async listMessageDbs(): Promise<{ success: boolean; data?: string[]; error?: string }> { - if (!this.ensureReady()) return { success: false, error: 'WCDB 未连接' } - try { - const outPtr = [null as any] - const result = this.wcdbListMessageDbs(this.handle, outPtr) - if (result !== 0 || !outPtr[0]) return { success: false, error: `获取消息库列表失败: ${result}` } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析消息库列表失败' } - const data = JSON.parse(jsonStr) - return { success: true, data } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('listMessageDbs') } + /** + * 列出媒体数据库 + */ async listMediaDbs(): Promise<{ success: boolean; data?: string[]; error?: string }> { - if (!this.ensureReady()) return { success: false, error: 'WCDB 未连接' } - try { - const outPtr = [null as any] - const result = this.wcdbListMediaDbs(this.handle, outPtr) - if (result !== 0 || !outPtr[0]) return { success: false, error: `获取媒体库列表失败: ${result}` } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析媒体库列表失败' } - const data = JSON.parse(jsonStr) - return { success: true, data } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('listMediaDbs') } + /** + * 根据 ID 获取消息 + */ async getMessageById(sessionId: string, localId: number): Promise<{ success: boolean; message?: any; error?: string }> { - if (!this.ensureReady()) return { success: false, error: 'WCDB 未连接' } - try { - const outPtr = [null as any] - const result = this.wcdbGetMessageById(this.handle, sessionId, localId, outPtr) - if (result !== 0 || !outPtr[0]) return { success: false, error: `查询消息失败: ${result}` } - const jsonStr = this.decodeJsonPtr(outPtr[0]) - if (!jsonStr) return { success: false, error: '解析消息失败' } - const message = JSON.parse(jsonStr) - // 处理 wcdb_get_message_by_id 返回空对象的情况 - if (Object.keys(message).length === 0) return { success: false, error: '未找到消息' } - return { success: true, message } - } catch (e) { - return { success: false, error: String(e) } - } + return this.callWorker('getMessageById', { sessionId, localId }) } + } export const wcdbService = new WcdbService() diff --git a/electron/wcdbWorker.ts b/electron/wcdbWorker.ts new file mode 100644 index 0000000..4958419 --- /dev/null +++ b/electron/wcdbWorker.ts @@ -0,0 +1,122 @@ +import { parentPort, workerData } from 'worker_threads' +import { WcdbCore } from './services/wcdbCore' + +const core = new WcdbCore() + +if (parentPort) { + parentPort.on('message', async (msg) => { + const { id, type, payload } = msg + + try { + let result: any + + switch (type) { + case 'setPaths': + core.setPaths(payload.resourcesPath, payload.userDataPath) + result = { success: true } + break + case 'setLogEnabled': + core.setLogEnabled(payload.enabled) + result = { success: true } + break + case 'testConnection': + result = await core.testConnection(payload.dbPath, payload.hexKey, payload.wxid) + break + case 'open': + result = await core.open(payload.dbPath, payload.hexKey, payload.wxid) + break + case 'close': + core.close() + result = { success: true } + break + case 'isConnected': + result = core.isConnected() + break + case 'getSessions': + result = await core.getSessions() + break + case 'getMessages': + result = await core.getMessages(payload.sessionId, payload.limit, payload.offset) + break + case 'getMessageCount': + result = await core.getMessageCount(payload.sessionId) + break + case 'getDisplayNames': + result = await core.getDisplayNames(payload.usernames) + break + case 'getAvatarUrls': + result = await core.getAvatarUrls(payload.usernames) + break + case 'getGroupMemberCount': + result = await core.getGroupMemberCount(payload.chatroomId) + break + case 'getGroupMemberCounts': + result = await core.getGroupMemberCounts(payload.chatroomIds) + break + case 'getGroupMembers': + result = await core.getGroupMembers(payload.chatroomId) + break + case 'getMessageTables': + result = await core.getMessageTables(payload.sessionId) + break + case 'getMessageTableStats': + result = await core.getMessageTableStats(payload.sessionId) + break + case 'getMessageMeta': + result = await core.getMessageMeta(payload.dbPath, payload.tableName, payload.limit, payload.offset) + break + case 'getContact': + result = await core.getContact(payload.username) + break + case 'getAggregateStats': + result = await core.getAggregateStats(payload.sessionIds, payload.beginTimestamp, payload.endTimestamp) + break + case 'getAvailableYears': + result = await core.getAvailableYears(payload.sessionIds) + break + case 'getAnnualReportStats': + result = await core.getAnnualReportStats(payload.sessionIds, payload.beginTimestamp, payload.endTimestamp) + break + case 'getAnnualReportExtras': + result = await core.getAnnualReportExtras(payload.sessionIds, payload.beginTimestamp, payload.endTimestamp, payload.peakDayBegin, payload.peakDayEnd) + break + case 'getGroupStats': + result = await core.getGroupStats(payload.chatroomId, payload.beginTimestamp, payload.endTimestamp) + break + case 'openMessageCursor': + result = await core.openMessageCursor(payload.sessionId, payload.batchSize, payload.ascending, payload.beginTimestamp, payload.endTimestamp) + break + case 'openMessageCursorLite': + result = await core.openMessageCursorLite(payload.sessionId, payload.batchSize, payload.ascending, payload.beginTimestamp, payload.endTimestamp) + break + case 'fetchMessageBatch': + result = await core.fetchMessageBatch(payload.cursor) + break + case 'closeMessageCursor': + result = await core.closeMessageCursor(payload.cursor) + break + case 'execQuery': + result = await core.execQuery(payload.kind, payload.path, payload.sql) + break + case 'getEmoticonCdnUrl': + result = await core.getEmoticonCdnUrl(payload.dbPath, payload.md5) + break + case 'listMessageDbs': + result = await core.listMessageDbs() + break + case 'listMediaDbs': + result = await core.listMediaDbs() + break + case 'getMessageById': + result = await core.getMessageById(payload.sessionId, payload.localId) + break + default: + result = { success: false, error: `Unknown method: ${type}` } + } + + parentPort!.postMessage({ id, result }) + } catch (e) { + parentPort!.postMessage({ id, error: String(e) }) + } + }) +} diff --git a/package-lock.json b/package-lock.json index 9099308..6c3e749 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,6 @@ "version": "1.0.4", "hasInstallScript": true, "dependencies": { - "@lancedb/lancedb": "^0.23.1-beta.1", - "@lancedb/lancedb-win32-x64-msvc": "^0.22.3", - "@xenova/transformers": "^2.17.2", "better-sqlite3": "^12.5.0", "echarts": "^5.5.1", "echarts-for-react": "^3.0.2", @@ -23,8 +20,6 @@ "jszip": "^3.10.1", "koffi": "^2.9.0", "lucide-react": "^0.562.0", - "node-llama-cpp": "^3.1.0", - "onnxruntime-node": "^1.16.1", "react": "^19.2.3", "react-dom": "^19.2.3", "react-router-dom": "^7.1.1", @@ -1136,15 +1131,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@huggingface/jinja": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/@huggingface/jinja/-/jinja-0.2.2.tgz", - "integrity": "sha512-/KPde26khDUIPkTGU82jdtTW9UAuvUTumCAbFs/7giR0SxsvZC4hru51PBvpijH6BVkHcROcvZM/lpy5h1jRRA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/@img/colour": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/@img/colour/-/colour-1.0.0.tgz", @@ -1824,94 +1810,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@kwsites/file-exists": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz", - "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.1" - } - }, - "node_modules/@kwsites/promise-deferred": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", - "license": "MIT" - }, - "node_modules/@lancedb/lancedb": { - "version": "0.23.1-beta.1", - "resolved": "https://registry.npmmirror.com/@lancedb/lancedb/-/lancedb-0.23.1-beta.1.tgz", - "integrity": "sha512-l7mLIA6qGlQHM6YfjUxvph8XzTvkBF8nqGT47nI7xVl1SuJiqv70J+cSHwkj0TXex716UCyiLM94EO3c33m98A==", - "cpu": [ - "x64", - "arm64" - ], - "license": "Apache-2.0", - "os": [ - "darwin", - "linux", - "win32" - ], - "dependencies": { - "reflect-metadata": "^0.2.2" - }, - "engines": { - "node": ">= 18" - }, - "optionalDependencies": { - "@lancedb/lancedb-darwin-arm64": "0.23.1-beta.1", - "@lancedb/lancedb-darwin-x64": "0.23.1-beta.1", - "@lancedb/lancedb-linux-arm64-gnu": "0.23.1-beta.1", - "@lancedb/lancedb-linux-arm64-musl": "0.23.1-beta.1", - "@lancedb/lancedb-linux-x64-gnu": "0.23.1-beta.1", - "@lancedb/lancedb-linux-x64-musl": "0.23.1-beta.1", - "@lancedb/lancedb-win32-arm64-msvc": "0.23.1-beta.1", - "@lancedb/lancedb-win32-x64-msvc": "0.23.1-beta.1" - }, - "peerDependencies": { - "apache-arrow": ">=15.0.0 <=18.1.0" - } - }, - "node_modules/@lancedb/lancedb-win32-x64-msvc": { - "version": "0.22.3", - "resolved": "https://registry.npmmirror.com/@lancedb/lancedb-win32-x64-msvc/-/lancedb-win32-x64-msvc-0.22.3.tgz", - "integrity": "sha512-/1feFnjz5MIhzXOEU4+1OeGwpAFYczGfefuOGZRsmGWDdt4V6/fza7Hkkxyb2OnTzqpBfy6BdW2+iBguE1JMyQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "os": [ - "win32" - ], - "engines": { - "node": ">= 18" - } - }, - "node_modules/@lancedb/lancedb/node_modules/@lancedb/lancedb-darwin-arm64": { - "optional": true - }, - "node_modules/@lancedb/lancedb/node_modules/@lancedb/lancedb-darwin-x64": { - "optional": true - }, - "node_modules/@lancedb/lancedb/node_modules/@lancedb/lancedb-linux-arm64-gnu": { - "optional": true - }, - "node_modules/@lancedb/lancedb/node_modules/@lancedb/lancedb-linux-arm64-musl": { - "optional": true - }, - "node_modules/@lancedb/lancedb/node_modules/@lancedb/lancedb-linux-x64-gnu": { - "optional": true - }, - "node_modules/@lancedb/lancedb/node_modules/@lancedb/lancedb-linux-x64-musl": { - "optional": true - }, - "node_modules/@lancedb/lancedb/node_modules/@lancedb/lancedb-win32-arm64-msvc": { - "optional": true - }, - "node_modules/@lancedb/lancedb/node_modules/@lancedb/lancedb-win32-x64-msvc": { - "optional": true - }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -1990,138 +1888,6 @@ "node": ">= 10.0.0" } }, - "node_modules/@node-llama-cpp/linux-arm64": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@node-llama-cpp/linux-arm64/-/linux-arm64-3.15.0.tgz", - "integrity": "sha512-IaHIllWlj6tGjhhCtyp1w6xA7AHaGJiVaXAZ+78hDs8X1SL9ySBN2Qceju8AQJALePtynbAfjgjTqjQ7Hyk+IQ==", - "cpu": [ - "arm64", - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@node-llama-cpp/linux-armv7l": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@node-llama-cpp/linux-armv7l/-/linux-armv7l-3.15.0.tgz", - "integrity": "sha512-ZuZ3q6mejQnEP4o22la7zBv7jNR+IZfgItDm3KjAl04HUXTKJ43HpNwjnf9GyYYd+dEgtoX0MESvWz4RnGH8Jw==", - "cpu": [ - "arm", - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@node-llama-cpp/linux-x64": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@node-llama-cpp/linux-x64/-/linux-x64-3.15.0.tgz", - "integrity": "sha512-etUuTqSyNefRObqc5+JZviNTkuef2XEtHcQLaamEIWwjI1dj7nTD2YMZPBP7H3M3E55HSIY82vqCQ1bp6ZILiA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@node-llama-cpp/linux-x64-vulkan": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@node-llama-cpp/linux-x64-vulkan/-/linux-x64-vulkan-3.15.0.tgz", - "integrity": "sha512-htVIthQKq/rr8v5e7NiVtcHsstqTBAAC50kUymmDMbrzAu6d/EHacCJpNbU57b1UUa1nKN5cBqr6Jr+QqEalMA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@node-llama-cpp/mac-arm64-metal": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@node-llama-cpp/mac-arm64-metal/-/mac-arm64-metal-3.15.0.tgz", - "integrity": "sha512-3Vkq6bpyQZaIzoaLLP7H2Tt8ty5BS0zxUY2pX0ox2S9P4fp8Au0CCJuUJF4V+EKi+/PTn70A6R1QCkRMfMQJig==", - "cpu": [ - "arm64", - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@node-llama-cpp/mac-x64": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@node-llama-cpp/mac-x64/-/mac-x64-3.15.0.tgz", - "integrity": "sha512-BUrmLu0ySveEYv2YzFIjqnWWAqjTZfRHuzoFLaZwqIJ86Jzycm9tzxJub4wfJCj6ixeuWyI1sUdNGIw4/2E03Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@node-llama-cpp/win-arm64": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@node-llama-cpp/win-arm64/-/win-arm64-3.15.0.tgz", - "integrity": "sha512-GwhqaPNpbtGDmw0Ex13hwq4jqzSZr7hw5QpRWhSKB1dHiYj6C1NLM1Vz5xiDZX+69WI/ndb+FEqGiIYFQpfmiQ==", - "cpu": [ - "arm64", - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@node-llama-cpp/win-x64": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/@node-llama-cpp/win-x64/-/win-x64-3.15.0.tgz", - "integrity": "sha512-gWhtc8l3HOry5guO46YfFohLQnF0NfL4On0GAO8E27JiYYxHO9nHSCfFif4+U03+FfHquZXL0znJ1qPVOiwOPw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/@npmcli/agent": { "version": "3.0.0", "resolved": "https://registry.npmmirror.com/@npmcli/agent/-/agent-3.0.0.tgz", @@ -2174,347 +1940,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/@octokit/app": { - "version": "16.1.2", - "resolved": "https://registry.npmmirror.com/@octokit/app/-/app-16.1.2.tgz", - "integrity": "sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ==", - "license": "MIT", - "dependencies": { - "@octokit/auth-app": "^8.1.2", - "@octokit/auth-unauthenticated": "^7.0.3", - "@octokit/core": "^7.0.6", - "@octokit/oauth-app": "^8.0.3", - "@octokit/plugin-paginate-rest": "^14.0.0", - "@octokit/types": "^16.0.0", - "@octokit/webhooks": "^14.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/auth-app": { - "version": "8.1.2", - "resolved": "https://registry.npmmirror.com/@octokit/auth-app/-/auth-app-8.1.2.tgz", - "integrity": "sha512-db8VO0PqXxfzI6GdjtgEFHY9tzqUql5xMFXYA12juq8TeTgPAuiiP3zid4h50lwlIP457p5+56PnJOgd2GGBuw==", - "license": "MIT", - "dependencies": { - "@octokit/auth-oauth-app": "^9.0.3", - "@octokit/auth-oauth-user": "^6.0.2", - "@octokit/request": "^10.0.6", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "toad-cache": "^3.7.0", - "universal-github-app-jwt": "^2.2.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/auth-oauth-app": { - "version": "9.0.3", - "resolved": "https://registry.npmmirror.com/@octokit/auth-oauth-app/-/auth-oauth-app-9.0.3.tgz", - "integrity": "sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==", - "license": "MIT", - "dependencies": { - "@octokit/auth-oauth-device": "^8.0.3", - "@octokit/auth-oauth-user": "^6.0.2", - "@octokit/request": "^10.0.6", - "@octokit/types": "^16.0.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/auth-oauth-device": { - "version": "8.0.3", - "resolved": "https://registry.npmmirror.com/@octokit/auth-oauth-device/-/auth-oauth-device-8.0.3.tgz", - "integrity": "sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==", - "license": "MIT", - "dependencies": { - "@octokit/oauth-methods": "^6.0.2", - "@octokit/request": "^10.0.6", - "@octokit/types": "^16.0.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/auth-oauth-user": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/@octokit/auth-oauth-user/-/auth-oauth-user-6.0.2.tgz", - "integrity": "sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==", - "license": "MIT", - "dependencies": { - "@octokit/auth-oauth-device": "^8.0.3", - "@octokit/oauth-methods": "^6.0.2", - "@octokit/request": "^10.0.6", - "@octokit/types": "^16.0.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/auth-token": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/@octokit/auth-token/-/auth-token-6.0.0.tgz", - "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/auth-unauthenticated": { - "version": "7.0.3", - "resolved": "https://registry.npmmirror.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-7.0.3.tgz", - "integrity": "sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g==", - "license": "MIT", - "dependencies": { - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/core": { - "version": "7.0.6", - "resolved": "https://registry.npmmirror.com/@octokit/core/-/core-7.0.6.tgz", - "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", - "license": "MIT", - "dependencies": { - "@octokit/auth-token": "^6.0.0", - "@octokit/graphql": "^9.0.3", - "@octokit/request": "^10.0.6", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "before-after-hook": "^4.0.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/endpoint": { - "version": "11.0.2", - "resolved": "https://registry.npmmirror.com/@octokit/endpoint/-/endpoint-11.0.2.tgz", - "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0", - "universal-user-agent": "^7.0.2" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/graphql": { - "version": "9.0.3", - "resolved": "https://registry.npmmirror.com/@octokit/graphql/-/graphql-9.0.3.tgz", - "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", - "license": "MIT", - "dependencies": { - "@octokit/request": "^10.0.6", - "@octokit/types": "^16.0.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/oauth-app": { - "version": "8.0.3", - "resolved": "https://registry.npmmirror.com/@octokit/oauth-app/-/oauth-app-8.0.3.tgz", - "integrity": "sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg==", - "license": "MIT", - "dependencies": { - "@octokit/auth-oauth-app": "^9.0.2", - "@octokit/auth-oauth-user": "^6.0.1", - "@octokit/auth-unauthenticated": "^7.0.2", - "@octokit/core": "^7.0.5", - "@octokit/oauth-authorization-url": "^8.0.0", - "@octokit/oauth-methods": "^6.0.1", - "@types/aws-lambda": "^8.10.83", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/oauth-authorization-url": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-8.0.0.tgz", - "integrity": "sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==", - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/oauth-methods": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/@octokit/oauth-methods/-/oauth-methods-6.0.2.tgz", - "integrity": "sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==", - "license": "MIT", - "dependencies": { - "@octokit/oauth-authorization-url": "^8.0.0", - "@octokit/request": "^10.0.6", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "27.0.0", - "resolved": "https://registry.npmmirror.com/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", - "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", - "license": "MIT" - }, - "node_modules/@octokit/openapi-webhooks-types": { - "version": "12.1.0", - "resolved": "https://registry.npmmirror.com/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-12.1.0.tgz", - "integrity": "sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==", - "license": "MIT" - }, - "node_modules/@octokit/plugin-paginate-graphql": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-6.0.0.tgz", - "integrity": "sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==", - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@octokit/core": ">=6" - } - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "14.0.0", - "resolved": "https://registry.npmmirror.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", - "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@octokit/core": ">=6" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "17.0.0", - "resolved": "https://registry.npmmirror.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", - "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@octokit/core": ">=6" - } - }, - "node_modules/@octokit/plugin-retry": { - "version": "8.0.3", - "resolved": "https://registry.npmmirror.com/@octokit/plugin-retry/-/plugin-retry-8.0.3.tgz", - "integrity": "sha512-vKGx1i3MC0za53IzYBSBXcrhmd+daQDzuZfYDd52X5S0M2otf3kVZTVP8bLA3EkU0lTvd1WEC2OlNNa4G+dohA==", - "license": "MIT", - "dependencies": { - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "bottleneck": "^2.15.3" - }, - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@octokit/core": ">=7" - } - }, - "node_modules/@octokit/plugin-throttling": { - "version": "11.0.3", - "resolved": "https://registry.npmmirror.com/@octokit/plugin-throttling/-/plugin-throttling-11.0.3.tgz", - "integrity": "sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==", - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0", - "bottleneck": "^2.15.3" - }, - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@octokit/core": "^7.0.0" - } - }, - "node_modules/@octokit/request": { - "version": "10.0.7", - "resolved": "https://registry.npmmirror.com/@octokit/request/-/request-10.0.7.tgz", - "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", - "license": "MIT", - "dependencies": { - "@octokit/endpoint": "^11.0.2", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "fast-content-type-parse": "^3.0.0", - "universal-user-agent": "^7.0.2" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/request-error": { - "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/@octokit/request-error/-/request-error-7.1.0.tgz", - "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/types": { - "version": "16.0.0", - "resolved": "https://registry.npmmirror.com/@octokit/types/-/types-16.0.0.tgz", - "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", - "license": "MIT", - "dependencies": { - "@octokit/openapi-types": "^27.0.0" - } - }, - "node_modules/@octokit/webhooks": { - "version": "14.2.0", - "resolved": "https://registry.npmmirror.com/@octokit/webhooks/-/webhooks-14.2.0.tgz", - "integrity": "sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==", - "license": "MIT", - "dependencies": { - "@octokit/openapi-webhooks-types": "12.1.0", - "@octokit/request-error": "^7.0.0", - "@octokit/webhooks-methods": "^6.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/webhooks-methods": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/@octokit/webhooks-methods/-/webhooks-methods-6.0.0.tgz", - "integrity": "sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==", - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, "node_modules/@parcel/watcher": { "version": "2.5.1", "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.1.tgz", @@ -2858,218 +2283,6 @@ "node": ">=14" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" - }, - "node_modules/@reflink/reflink": { - "version": "0.1.19", - "resolved": "https://registry.npmmirror.com/@reflink/reflink/-/reflink-0.1.19.tgz", - "integrity": "sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@reflink/reflink-darwin-arm64": "0.1.19", - "@reflink/reflink-darwin-x64": "0.1.19", - "@reflink/reflink-linux-arm64-gnu": "0.1.19", - "@reflink/reflink-linux-arm64-musl": "0.1.19", - "@reflink/reflink-linux-x64-gnu": "0.1.19", - "@reflink/reflink-linux-x64-musl": "0.1.19", - "@reflink/reflink-win32-arm64-msvc": "0.1.19", - "@reflink/reflink-win32-x64-msvc": "0.1.19" - } - }, - "node_modules/@reflink/reflink-darwin-arm64": { - "version": "0.1.19", - "resolved": "https://registry.npmmirror.com/@reflink/reflink-darwin-arm64/-/reflink-darwin-arm64-0.1.19.tgz", - "integrity": "sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-darwin-x64": { - "version": "0.1.19", - "resolved": "https://registry.npmmirror.com/@reflink/reflink-darwin-x64/-/reflink-darwin-x64-0.1.19.tgz", - "integrity": "sha512-By85MSWrMZa+c26TcnAy8SDk0sTUkYlNnwknSchkhHpGXOtjNDUOxJE9oByBnGbeuIE1PiQsxDG3Ud+IVV9yuA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-linux-arm64-gnu": { - "version": "0.1.19", - "resolved": "https://registry.npmmirror.com/@reflink/reflink-linux-arm64-gnu/-/reflink-linux-arm64-gnu-0.1.19.tgz", - "integrity": "sha512-7P+er8+rP9iNeN+bfmccM4hTAaLP6PQJPKWSA4iSk2bNvo6KU6RyPgYeHxXmzNKzPVRcypZQTpFgstHam6maVg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-linux-arm64-musl": { - "version": "0.1.19", - "resolved": "https://registry.npmmirror.com/@reflink/reflink-linux-arm64-musl/-/reflink-linux-arm64-musl-0.1.19.tgz", - "integrity": "sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-linux-x64-gnu": { - "version": "0.1.19", - "resolved": "https://registry.npmmirror.com/@reflink/reflink-linux-x64-gnu/-/reflink-linux-x64-gnu-0.1.19.tgz", - "integrity": "sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-linux-x64-musl": { - "version": "0.1.19", - "resolved": "https://registry.npmmirror.com/@reflink/reflink-linux-x64-musl/-/reflink-linux-x64-musl-0.1.19.tgz", - "integrity": "sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-win32-arm64-msvc": { - "version": "0.1.19", - "resolved": "https://registry.npmmirror.com/@reflink/reflink-win32-arm64-msvc/-/reflink-win32-arm64-msvc-0.1.19.tgz", - "integrity": "sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@reflink/reflink-win32-x64-msvc": { - "version": "0.1.19", - "resolved": "https://registry.npmmirror.com/@reflink/reflink-win32-x64-msvc/-/reflink-win32-x64-msvc-0.1.19.tgz", - "integrity": "sha512-E//yT4ni2SyhwP8JRjVGWr3cbnhWDiPLgnQ66qqaanjjnMiu3O/2tjCPQXlcGc/DEYofpDc9fvhv6tALQsMV9w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", @@ -3440,23 +2653,6 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@swc/helpers": { - "version": "0.5.18", - "resolved": "https://registry.npmmirror.com/@swc/helpers/-/helpers-0.5.18.tgz", - "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@swc/helpers/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true - }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -3470,19 +2666,6 @@ "node": ">=10" } }, - "node_modules/@tinyhttp/content-disposition": { - "version": "2.2.2", - "resolved": "https://registry.npmmirror.com/@tinyhttp/content-disposition/-/content-disposition-2.2.2.tgz", - "integrity": "sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g==", - "license": "MIT", - "engines": { - "node": ">=12.20.0" - }, - "funding": { - "type": "individual", - "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" - } - }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz", @@ -3493,12 +2676,6 @@ "node": ">= 10" } }, - "node_modules/@types/aws-lambda": { - "version": "8.10.159", - "resolved": "https://registry.npmmirror.com/@types/aws-lambda/-/aws-lambda-8.10.159.tgz", - "integrity": "sha512-SAP22WSGNN12OQ8PlCzGzRCZ7QDCwI85dQZbmpz7+mAk+L7j+wI7qnvmdKh+o7A5LaOp6QnOZ2NJphAZQTTHQg==", - "license": "MIT" - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3567,20 +2744,6 @@ "@types/responselike": "^1.0.0" } }, - "node_modules/@types/command-line-args": { - "version": "5.2.3", - "resolved": "https://registry.npmmirror.com/@types/command-line-args/-/command-line-args-5.2.3.tgz", - "integrity": "sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==", - "license": "MIT", - "peer": true - }, - "node_modules/@types/command-line-usage": { - "version": "5.0.4", - "resolved": "https://registry.npmmirror.com/@types/command-line-usage/-/command-line-usage-5.0.4.tgz", - "integrity": "sha512-BwR5KP3Es/CSht0xqBcUXS3qCAUVXwpRKsV2+arxeb65atasuXG9LykC9Ab10Cw3s2raH92ZqOeILaQbsB2ACg==", - "license": "MIT", - "peer": true - }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.12.tgz", @@ -3625,12 +2788,6 @@ "@types/node": "*" } }, - "node_modules/@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", - "license": "MIT" - }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz", @@ -3642,6 +2799,7 @@ "version": "25.0.3", "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.0.3.tgz", "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==", + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -3729,89 +2887,6 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, - "node_modules/@xenova/transformers": { - "version": "2.17.2", - "resolved": "https://registry.npmmirror.com/@xenova/transformers/-/transformers-2.17.2.tgz", - "integrity": "sha512-lZmHqzrVIkSvZdKZEx7IYY51TK0WDrC8eR0c5IMnBsO8di8are1zzw8BlLhyO2TklZKLN5UffNGs1IJwT6oOqQ==", - "license": "Apache-2.0", - "dependencies": { - "@huggingface/jinja": "^0.2.2", - "onnxruntime-web": "1.14.0", - "sharp": "^0.32.0" - }, - "optionalDependencies": { - "onnxruntime-node": "1.14.0" - } - }, - "node_modules/@xenova/transformers/node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", - "license": "MIT" - }, - "node_modules/@xenova/transformers/node_modules/onnxruntime-node": { - "version": "1.14.0", - "resolved": "https://registry.npmmirror.com/onnxruntime-node/-/onnxruntime-node-1.14.0.tgz", - "integrity": "sha512-5ba7TWomIV/9b6NH/1x/8QEeowsb+jBEvFzU6z0T4mNsFwdPqXeFUM7uxC6QeSRkEbWu3qEB0VMjrvzN/0S9+w==", - "license": "MIT", - "optional": true, - "os": [ - "win32", - "darwin", - "linux" - ], - "dependencies": { - "onnxruntime-common": "~1.14.0" - } - }, - "node_modules/@xenova/transformers/node_modules/sharp": { - "version": "0.32.6", - "resolved": "https://registry.npmmirror.com/sharp/-/sharp-0.32.6.tgz", - "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.2", - "node-addon-api": "^6.1.0", - "prebuild-install": "^7.1.1", - "semver": "^7.5.4", - "simple-get": "^4.0.1", - "tar-fs": "^3.0.4", - "tunnel-agent": "^0.6.0" - }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@xenova/transformers/node_modules/tar-fs": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-3.1.1.tgz", - "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, - "node_modules/@xenova/transformers/node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/@xmldom/xmldom": { "version": "0.8.11", "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.11.tgz", @@ -3839,15 +2914,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/adm-zip": { - "version": "0.5.16", - "resolved": "https://registry.npmmirror.com/adm-zip/-/adm-zip-0.5.16.tgz", - "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", - "license": "MIT", - "engines": { - "node": ">=12.0" - } - }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz", @@ -3951,22 +3017,11 @@ "ajv": "^6.9.1" } }, - "node_modules/ansi-escapes": { - "version": "6.2.1", - "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-6.2.1.tgz", - "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3976,6 +3031,7 @@ "version": "4.3.0", "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -3987,51 +3043,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/apache-arrow": { - "version": "18.1.0", - "resolved": "https://registry.npmmirror.com/apache-arrow/-/apache-arrow-18.1.0.tgz", - "integrity": "sha512-v/ShMp57iBnBp4lDgV8Jx3d3Q5/Hac25FWmQ98eMahUiHPXcvwIMKJD0hBIgclm/FCG+LwPkAKtkRO1O/W0YGg==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@swc/helpers": "^0.5.11", - "@types/command-line-args": "^5.2.3", - "@types/command-line-usage": "^5.0.4", - "@types/node": "^20.13.0", - "command-line-args": "^5.2.1", - "command-line-usage": "^7.0.1", - "flatbuffers": "^24.3.25", - "json-bignum": "^0.0.3", - "tslib": "^2.6.2" - }, - "bin": { - "arrow2csv": "bin/arrow2csv.js" - } - }, - "node_modules/apache-arrow/node_modules/@types/node": { - "version": "20.19.28", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.28.tgz", - "integrity": "sha512-VyKBr25BuFDzBFCK5sUM6ZXiWfqgCTwTAOK8qzGV/m9FCirXYDlmczJ+d5dXBAQALGCdRRdbteKYfJ84NGEusw==", - "license": "MIT", - "peer": true, - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/apache-arrow/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true - }, - "node_modules/apache-arrow/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT", - "peer": true - }, "node_modules/app-builder-bin": { "version": "5.0.0-alpha.10", "resolved": "https://registry.npmmirror.com/app-builder-bin/-/app-builder-bin-5.0.0-alpha.10.tgz", @@ -4546,6 +3557,7 @@ "version": "2.1.0", "resolved": "https://registry.npmmirror.com/aproba/-/aproba-2.1.0.tgz", "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "dev": true, "license": "ISC" }, "node_modules/archiver": { @@ -4632,6 +3644,7 @@ "resolved": "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", "deprecated": "This package is no longer supported.", + "dev": true, "license": "ISC", "dependencies": { "delegates": "^1.0.0", @@ -4647,16 +3660,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, - "node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz", @@ -4696,28 +3699,11 @@ "node": ">=0.12.0" } }, - "node_modules/async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "license": "MIT", - "dependencies": { - "retry": "0.13.1" - } - }, - "node_modules/async-retry/node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, "license": "MIT" }, "node_modules/at-least-node": { @@ -4740,31 +3726,6 @@ "when-exit": "^2.1.4" } }, - "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/b4a": { - "version": "1.7.3", - "resolved": "https://registry.npmmirror.com/b4a/-/b4a-1.7.3.tgz", - "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", - "license": "Apache-2.0", - "peerDependencies": { - "react-native-b4a": "*" - }, - "peerDependenciesMeta": { - "react-native-b4a": { - "optional": true - } - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4772,97 +3733,6 @@ "dev": true, "license": "MIT" }, - "node_modules/bare-events": { - "version": "2.8.2", - "resolved": "https://registry.npmmirror.com/bare-events/-/bare-events-2.8.2.tgz", - "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", - "license": "Apache-2.0", - "peerDependencies": { - "bare-abort-controller": "*" - }, - "peerDependenciesMeta": { - "bare-abort-controller": { - "optional": true - } - } - }, - "node_modules/bare-fs": { - "version": "4.5.2", - "resolved": "https://registry.npmmirror.com/bare-fs/-/bare-fs-4.5.2.tgz", - "integrity": "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-events": "^2.5.4", - "bare-path": "^3.0.0", - "bare-stream": "^2.6.4", - "bare-url": "^2.2.2", - "fast-fifo": "^1.3.2" - }, - "engines": { - "bare": ">=1.16.0" - }, - "peerDependencies": { - "bare-buffer": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - } - } - }, - "node_modules/bare-os": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/bare-os/-/bare-os-3.6.2.tgz", - "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "bare": ">=1.14.0" - } - }, - "node_modules/bare-path": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-os": "^3.0.1" - } - }, - "node_modules/bare-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmmirror.com/bare-stream/-/bare-stream-2.7.0.tgz", - "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "streamx": "^2.21.0" - }, - "peerDependencies": { - "bare-buffer": "*", - "bare-events": "*" - }, - "peerDependenciesMeta": { - "bare-buffer": { - "optional": true - }, - "bare-events": { - "optional": true - } - } - }, - "node_modules/bare-url": { - "version": "2.3.2", - "resolved": "https://registry.npmmirror.com/bare-url/-/bare-url-2.3.2.tgz", - "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-path": "^3.0.0" - } - }, "node_modules/base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", @@ -4902,12 +3772,6 @@ "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/before-after-hook": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/before-after-hook/-/before-after-hook-4.0.0.tgz", - "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", - "license": "Apache-2.0" - }, "node_modules/better-sqlite3": { "version": "12.5.0", "resolved": "https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-12.5.0.tgz", @@ -4964,13 +3828,9 @@ "resolved": "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz", "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "license": "MIT" - }, - "node_modules/bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmmirror.com/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true }, "node_modules/brace-expansion": { "version": "1.1.12", @@ -5149,15 +4009,6 @@ "node": ">= 10.0.0" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/cacache": { "version": "19.0.1", "resolved": "https://registry.npmmirror.com/cacache/-/cacache-19.0.1.tgz", @@ -5306,6 +4157,7 @@ "version": "1.0.2", "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5340,6 +4192,7 @@ "version": "4.1.2", "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -5352,28 +4205,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk-template": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/chalk-template/-/chalk-template-0.4.0.tgz", - "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", - "license": "MIT", - "peer": true, - "dependencies": { - "chalk": "^4.1.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/chalk-template?sponsor=1" - } - }, - "node_modules/chmodrp": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/chmodrp/-/chmodrp-1.0.2.tgz", - "integrity": "sha512-TdngOlFV1FLTzU0o1w8MB6/BFywhtLC0SzRTGJU7T9lmdjlCWeMRt1iVo0Ki+ldwNk0BqNiKoc8xpLZEQ8mY1w==", - "license": "MIT" - }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz", @@ -5394,6 +4225,7 @@ "version": "2.0.0", "resolved": "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -5449,6 +4281,7 @@ "version": "2.9.2", "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5479,6 +4312,7 @@ "version": "8.0.1", "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -5512,84 +4346,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cmake-js": { - "version": "7.4.0", - "resolved": "https://registry.npmmirror.com/cmake-js/-/cmake-js-7.4.0.tgz", - "integrity": "sha512-Lw0JxEHrmk+qNj1n9W9d4IvkDdYTBn7l2BW6XmtLj7WPpIo2shvxUy+YokfjMxAAOELNonQwX3stkPhM5xSC2Q==", - "license": "MIT", - "dependencies": { - "axios": "^1.6.5", - "debug": "^4", - "fs-extra": "^11.2.0", - "memory-stream": "^1.0.0", - "node-api-headers": "^1.1.0", - "npmlog": "^6.0.2", - "rc": "^1.2.7", - "semver": "^7.5.4", - "tar": "^6.2.0", - "url-join": "^4.0.1", - "which": "^2.0.2", - "yargs": "^17.7.2" - }, - "bin": { - "cmake-js": "bin/cmake-js" - }, - "engines": { - "node": ">= 14.15.0" - } - }, - "node_modules/cmake-js/node_modules/fs-extra": { - "version": "11.3.3", - "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.3.tgz", - "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/cmake-js/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/cmake-js/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/color": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -5602,22 +4363,14 @@ "version": "1.1.4", "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, - "node_modules/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/color-support": { "version": "1.1.3", "resolved": "https://registry.npmmirror.com/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, "license": "ISC", "bin": { "color-support": "bin.js" @@ -5627,6 +4380,7 @@ "version": "1.0.8", "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -5635,58 +4389,6 @@ "node": ">= 0.8" } }, - "node_modules/command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", - "license": "MIT", - "peer": true, - "dependencies": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", - "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-usage": { - "version": "7.0.3", - "resolved": "https://registry.npmmirror.com/command-line-usage/-/command-line-usage-7.0.3.tgz", - "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", - "license": "MIT", - "peer": true, - "dependencies": { - "array-back": "^6.2.2", - "chalk-template": "^0.4.0", - "table-layout": "^4.1.0", - "typical": "^7.1.1" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/command-line-usage/node_modules/array-back": { - "version": "6.2.2", - "resolved": "https://registry.npmmirror.com/array-back/-/array-back-6.2.2.tgz", - "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12.17" - } - }, - "node_modules/command-line-usage/node_modules/typical": { - "version": "7.3.0", - "resolved": "https://registry.npmmirror.com/typical/-/typical-7.3.0.tgz", - "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12.17" - } - }, "node_modules/commander": { "version": "5.1.0", "resolved": "https://registry.npmmirror.com/commander/-/commander-5.1.0.tgz", @@ -5850,6 +4552,7 @@ "version": "1.1.0", "resolved": "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, "license": "ISC" }, "node_modules/convert-source-map": { @@ -5922,6 +4625,7 @@ "version": "7.0.6", "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -6043,7 +4747,9 @@ "version": "1.1.4", "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -6060,7 +4766,9 @@ "version": "1.2.1", "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -6077,6 +4785,7 @@ "version": "1.0.0", "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -6086,6 +4795,7 @@ "version": "1.0.0", "resolved": "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -6101,7 +4811,9 @@ "version": "2.1.0", "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true }, "node_modules/dir-compare": { "version": "4.2.0", @@ -6258,6 +4970,7 @@ "version": "1.0.1", "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -6615,6 +5328,7 @@ "version": "8.0.0", "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/encoding": { @@ -6647,15 +5361,6 @@ "node": ">=6" } }, - "node_modules/env-var": { - "version": "7.5.0", - "resolved": "https://registry.npmmirror.com/env-var/-/env-var-7.5.0.tgz", - "integrity": "sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz", @@ -6667,6 +5372,7 @@ "version": "1.0.1", "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6676,6 +5382,7 @@ "version": "1.3.0", "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6685,6 +5392,7 @@ "version": "1.1.1", "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -6697,6 +5405,7 @@ "version": "2.1.0", "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -6712,7 +5421,9 @@ "version": "4.1.1", "resolved": "https://registry.npmmirror.com/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true }, "node_modules/esbuild": { "version": "0.25.12", @@ -6760,6 +5471,7 @@ "version": "3.2.0", "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6769,7 +5481,9 @@ "version": "4.0.0", "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">=10" }, @@ -6777,21 +5491,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/events-universal": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/events-universal/-/events-universal-1.0.1.tgz", - "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", - "license": "Apache-2.0", - "dependencies": { - "bare-events": "^2.7.0" - } - }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz", @@ -6840,34 +5539,12 @@ "license": "MIT", "optional": true }, - "node_modules/fast-content-type-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", - "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmmirror.com/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT" - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -6940,33 +5617,6 @@ "node": ">=10" } }, - "node_modules/filename-reserved-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", - "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/filenamify": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/filenamify/-/filenamify-6.0.0.tgz", - "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==", - "license": "MIT", - "dependencies": { - "filename-reserved-regex": "^3.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", @@ -6981,46 +5631,6 @@ "node": ">=8" } }, - "node_modules/find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "array-back": "^3.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/flatbuffers": { - "version": "24.12.23", - "resolved": "https://registry.npmmirror.com/flatbuffers/-/flatbuffers-24.12.23.tgz", - "integrity": "sha512-dLVCAISd5mhls514keQzmEG6QHmUUsNuWsb4tFafIUwvvgDjXhtfAYSKOzt5SWOy+qByV5pbsDZ+Vb7HUOBEdA==", - "license": "Apache-2.0", - "peer": true - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", @@ -7055,6 +5665,7 @@ "version": "4.0.5", "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -7127,6 +5738,7 @@ "version": "1.1.2", "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7143,6 +5755,7 @@ "resolved": "https://registry.npmmirror.com/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "deprecated": "This package is no longer supported.", + "dev": true, "license": "ISC", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", @@ -7172,27 +5785,17 @@ "version": "2.0.5", "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-east-asian-width": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -7217,6 +5820,7 @@ "version": "1.0.1", "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -7287,7 +5891,9 @@ "version": "3.0.0", "resolved": "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz", "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, "license": "BSD-3-Clause", + "optional": true, "dependencies": { "boolean": "^3.0.1", "es6-error": "^4.1.1", @@ -7304,7 +5910,9 @@ "version": "1.0.4", "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -7320,6 +5928,7 @@ "version": "1.2.0", "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7360,16 +5969,11 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/guid-typescript": { - "version": "1.0.9", - "resolved": "https://registry.npmmirror.com/guid-typescript/-/guid-typescript-1.0.9.tgz", - "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", - "license": "ISC" - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7379,7 +5983,9 @@ "version": "1.0.2", "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -7391,6 +5997,7 @@ "version": "1.1.0", "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7403,6 +6010,7 @@ "version": "1.0.2", "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -7418,12 +6026,14 @@ "version": "2.0.1", "resolved": "https://registry.npmmirror.com/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, "license": "ISC" }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -7588,15 +6198,6 @@ ], "license": "BSD-3-Clause" }, - "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz", @@ -7671,238 +6272,6 @@ "node": ">= 12" } }, - "node_modules/ipull": { - "version": "3.9.3", - "resolved": "https://registry.npmmirror.com/ipull/-/ipull-3.9.3.tgz", - "integrity": "sha512-ZMkxaopfwKHwmEuGDYx7giNBdLxbHbRCWcQVA1D2eqE4crUguupfxej6s7UqbidYEwT69dkyumYkY8DPHIxF9g==", - "license": "MIT", - "dependencies": { - "@tinyhttp/content-disposition": "^2.2.0", - "async-retry": "^1.3.3", - "chalk": "^5.3.0", - "ci-info": "^4.0.0", - "cli-spinners": "^2.9.2", - "commander": "^10.0.0", - "eventemitter3": "^5.0.1", - "filenamify": "^6.0.0", - "fs-extra": "^11.1.1", - "is-unicode-supported": "^2.0.0", - "lifecycle-utils": "^2.0.1", - "lodash.debounce": "^4.0.8", - "lowdb": "^7.0.1", - "pretty-bytes": "^6.1.0", - "pretty-ms": "^8.0.0", - "sleep-promise": "^9.1.0", - "slice-ansi": "^7.1.0", - "stdout-update": "^4.0.1", - "strip-ansi": "^7.1.0" - }, - "bin": { - "ipull": "dist/cli/cli.js" - }, - "engines": { - "node": ">=18.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/ido-pluto/ipull?sponsor=1" - }, - "optionalDependencies": { - "@reflink/reflink": "^0.1.16" - } - }, - "node_modules/ipull/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ipull/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ipull/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ipull/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ipull/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmmirror.com/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/ipull/node_modules/fs-extra": { - "version": "11.3.3", - "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.3.tgz", - "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/ipull/node_modules/is-fullwidth-code-point": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ipull/node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ipull/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/ipull/node_modules/lifecycle-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/lifecycle-utils/-/lifecycle-utils-2.1.0.tgz", - "integrity": "sha512-AnrXnE2/OF9PHCyFg0RSqsnQTzV991XaZA/buhFDoc58xU7rhSCDgCz/09Lqpsn4MpoPHt7TRAXV1kWZypFVsA==", - "license": "MIT" - }, - "node_modules/ipull/node_modules/parse-ms": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/parse-ms/-/parse-ms-3.0.0.tgz", - "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ipull/node_modules/pretty-ms": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/pretty-ms/-/pretty-ms-8.0.0.tgz", - "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", - "license": "MIT", - "dependencies": { - "parse-ms": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ipull/node_modules/slice-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/ipull/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/ipull/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/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "license": "MIT" - }, "node_modules/is-ci": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-3.0.1.tgz", @@ -7931,6 +6300,7 @@ "version": "3.0.0", "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8014,6 +6384,7 @@ "version": "2.0.0", "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, "node_modules/jackspeak": { @@ -8088,15 +6459,6 @@ "node": ">=6" } }, - "node_modules/json-bignum": { - "version": "0.0.3", - "resolved": "https://registry.npmmirror.com/json-bignum/-/json-bignum-0.0.3.tgz", - "integrity": "sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==", - "peer": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", @@ -8121,7 +6483,9 @@ "version": "5.0.1", "resolved": "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "license": "ISC" + "dev": true, + "license": "ISC", + "optional": true }, "node_modules/json5": { "version": "2.2.3", @@ -8273,12 +6637,6 @@ "immediate": "~3.0.5" } }, - "node_modules/lifecycle-utils": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/lifecycle-utils/-/lifecycle-utils-3.0.1.tgz", - "integrity": "sha512-Qt/Jl5dsNIsyCAZsHB6x3mbwHFn0HJbdmvF49sVX/bHgX2cW7+G+U+I67Zw+TPM1Sr21Gb2nfJMd2g6iUcI1EQ==", - "license": "MIT" - }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", @@ -8286,19 +6644,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT", - "peer": true - }, - "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==", - "license": "MIT" - }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz", @@ -8369,27 +6714,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "license": "Apache-2.0" - }, - "node_modules/lowdb": { - "version": "7.0.1", - "resolved": "https://registry.npmmirror.com/lowdb/-/lowdb-7.0.1.tgz", - "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", - "license": "MIT", - "dependencies": { - "steno": "^4.0.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, "node_modules/lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -8446,7 +6770,9 @@ "version": "3.0.0", "resolved": "https://registry.npmmirror.com/matcher/-/matcher-3.0.0.tgz", "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { "escape-string-regexp": "^4.0.0" }, @@ -8458,20 +6784,12 @@ "version": "1.1.0", "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, - "node_modules/memory-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/memory-stream/-/memory-stream-1.0.0.tgz", - "integrity": "sha512-Wm13VcsPIMdG96dzILfij09PvuS3APtcKNh7M28FsCA/w6+1mjR7hhPmfFNoilX9xU7wTdhsH5lJAm6XNzdtww==", - "license": "MIT", - "dependencies": { - "readable-stream": "^3.4.0" - } - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", @@ -8504,6 +6822,7 @@ "version": "1.52.0", "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -8513,6 +6832,7 @@ "version": "2.1.35", "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -8735,6 +7055,7 @@ "version": "1.0.4", "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -8811,12 +7132,6 @@ "license": "MIT", "optional": true }, - "node_modules/node-api-headers": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/node-api-headers/-/node-api-headers-1.7.0.tgz", - "integrity": "sha512-uJMGdkhVwu9+I3UsVvI3KW6ICAy/yDfsu5Br9rSnTtY3WpoaComXvKloiV5wtx0Md2rn0B9n29Ys2WMNwWxj9A==", - "license": "MIT" - }, "node_modules/node-api-version": { "version": "0.2.1", "resolved": "https://registry.npmmirror.com/node-api-version/-/node-api-version-0.2.1.tgz", @@ -8915,442 +7230,6 @@ "node": ">=18" } }, - "node_modules/node-llama-cpp": { - "version": "3.15.0", - "resolved": "https://registry.npmmirror.com/node-llama-cpp/-/node-llama-cpp-3.15.0.tgz", - "integrity": "sha512-xQKl+MvKiA5QNi/CTwqLKMos7hefhRVyzJuNIAEwl7zvOoF+gNMOXEsR4Ojwl7qvgpcjsVeGKWSK3Rb6zoUP1w==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@huggingface/jinja": "^0.5.3", - "async-retry": "^1.3.3", - "bytes": "^3.1.2", - "chalk": "^5.4.1", - "chmodrp": "^1.0.2", - "cmake-js": "^7.4.0", - "cross-spawn": "^7.0.6", - "env-var": "^7.5.0", - "filenamify": "^6.0.0", - "fs-extra": "^11.3.0", - "ignore": "^7.0.4", - "ipull": "^3.9.2", - "is-unicode-supported": "^2.1.0", - "lifecycle-utils": "^3.0.1", - "log-symbols": "^7.0.0", - "nanoid": "^5.1.5", - "node-addon-api": "^8.3.1", - "octokit": "^5.0.3", - "ora": "^8.2.0", - "pretty-ms": "^9.2.0", - "proper-lockfile": "^4.1.2", - "semver": "^7.7.1", - "simple-git": "^3.27.0", - "slice-ansi": "^7.1.0", - "stdout-update": "^4.0.1", - "strip-ansi": "^7.1.0", - "validate-npm-package-name": "^6.0.0", - "which": "^5.0.0", - "yargs": "^17.7.2" - }, - "bin": { - "nlc": "dist/cli/cli.js", - "node-llama-cpp": "dist/cli/cli.js" - }, - "engines": { - "node": ">=20.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/giladgd" - }, - "optionalDependencies": { - "@node-llama-cpp/linux-arm64": "3.15.0", - "@node-llama-cpp/linux-armv7l": "3.15.0", - "@node-llama-cpp/linux-x64": "3.15.0", - "@node-llama-cpp/linux-x64-cuda": "3.15.0", - "@node-llama-cpp/linux-x64-cuda-ext": "3.15.0", - "@node-llama-cpp/linux-x64-vulkan": "3.15.0", - "@node-llama-cpp/mac-arm64-metal": "3.15.0", - "@node-llama-cpp/mac-x64": "3.15.0", - "@node-llama-cpp/win-arm64": "3.15.0", - "@node-llama-cpp/win-x64": "3.15.0", - "@node-llama-cpp/win-x64-cuda": "3.15.0", - "@node-llama-cpp/win-x64-cuda-ext": "3.15.0", - "@node-llama-cpp/win-x64-vulkan": "3.15.0" - }, - "peerDependencies": { - "typescript": ">=5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/node-llama-cpp/node_modules/@huggingface/jinja": { - "version": "0.5.3", - "resolved": "https://registry.npmmirror.com/@huggingface/jinja/-/jinja-0.5.3.tgz", - "integrity": "sha512-asqfZ4GQS0hD876Uw4qiUb7Tr/V5Q+JZuo2L+BtdrD4U40QU58nIRq3ZSgAzJgT874VLjhGVacaYfrdpXtEvtA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/node-llama-cpp/node_modules/@node-llama-cpp/linux-x64-cuda": { - "optional": true - }, - "node_modules/node-llama-cpp/node_modules/@node-llama-cpp/linux-x64-cuda-ext": { - "optional": true - }, - "node_modules/node-llama-cpp/node_modules/@node-llama-cpp/win-x64-cuda": { - "optional": true - }, - "node_modules/node-llama-cpp/node_modules/@node-llama-cpp/win-x64-cuda-ext": { - "optional": true - }, - "node_modules/node-llama-cpp/node_modules/@node-llama-cpp/win-x64-vulkan": { - "optional": true - }, - "node_modules/node-llama-cpp/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/node-llama-cpp/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/node-llama-cpp/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/node-llama-cpp/node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/node-llama-cpp/node_modules/fs-extra": { - "version": "11.3.3", - "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.3.3.tgz", - "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/node-llama-cpp/node_modules/is-fullwidth-code-point": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.3.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/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", - "engines": { - "node": ">=16" - } - }, - "node_modules/node-llama-cpp/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/node-llama-cpp/node_modules/log-symbols": { - "version": "7.0.1", - "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-7.0.1.tgz", - "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", - "license": "MIT", - "dependencies": { - "is-unicode-supported": "^2.0.0", - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/node_modules/nanoid": { - "version": "5.1.6", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-5.1.6.tgz", - "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.js" - }, - "engines": { - "node": "^18 || >=20" - } - }, - "node_modules/node-llama-cpp/node_modules/node-addon-api": { - "version": "8.5.0", - "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-8.5.0.tgz", - "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, - "node_modules/node-llama-cpp/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/node_modules/ora": { - "version": "8.2.0", - "resolved": "https://registry.npmmirror.com/ora/-/ora-8.2.0.tgz", - "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/node_modules/ora/node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-llama-cpp/node_modules/slice-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/node-llama-cpp/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-llama-cpp/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/node-llama-cpp/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/node-llama-cpp/node_modules/which": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz", @@ -9403,6 +7282,7 @@ "resolved": "https://registry.npmmirror.com/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "deprecated": "This package is no longer supported.", + "dev": true, "license": "ISC", "dependencies": { "are-we-there-yet": "^3.0.0", @@ -9418,33 +7298,13 @@ "version": "1.1.1", "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">= 0.4" } }, - "node_modules/octokit": { - "version": "5.0.5", - "resolved": "https://registry.npmmirror.com/octokit/-/octokit-5.0.5.tgz", - "integrity": "sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==", - "license": "MIT", - "dependencies": { - "@octokit/app": "^16.1.2", - "@octokit/core": "^7.0.6", - "@octokit/oauth-app": "^8.0.3", - "@octokit/plugin-paginate-graphql": "^6.0.0", - "@octokit/plugin-paginate-rest": "^14.0.0", - "@octokit/plugin-rest-endpoint-methods": "^17.0.0", - "@octokit/plugin-retry": "^8.0.3", - "@octokit/plugin-throttling": "^11.0.3", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "@octokit/webhooks": "^14.0.0" - }, - "engines": { - "node": ">= 20" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz", @@ -9470,64 +7330,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/onnx-proto": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/onnx-proto/-/onnx-proto-4.0.4.tgz", - "integrity": "sha512-aldMOB3HRoo6q/phyB6QRQxSt895HNNw82BNyZ2CMh4bjeKv7g/c+VpAFtJuEMVfYLMbRx61hbuqnKceLeDcDA==", - "license": "MIT", - "dependencies": { - "protobufjs": "^6.8.8" - } - }, - "node_modules/onnxruntime-common": { - "version": "1.14.0", - "resolved": "https://registry.npmmirror.com/onnxruntime-common/-/onnxruntime-common-1.14.0.tgz", - "integrity": "sha512-3LJpegM2iMNRX2wUmtYfeX/ytfOzNwAWKSq1HbRrKc9+uqG/FsEA0bbKZl1btQeZaXhC26l44NWpNUeXPII7Ew==", - "license": "MIT" - }, - "node_modules/onnxruntime-node": { - "version": "1.23.2", - "resolved": "https://registry.npmmirror.com/onnxruntime-node/-/onnxruntime-node-1.23.2.tgz", - "integrity": "sha512-OBTsG0W8ddBVOeVVVychpVBS87A9YV5sa2hJ6lc025T97Le+J4v++PwSC4XFs1C62SWyNdof0Mh4KvnZgtt4aw==", - "hasInstallScript": true, - "license": "MIT", - "os": [ - "win32", - "darwin", - "linux" - ], - "dependencies": { - "adm-zip": "^0.5.16", - "global-agent": "^3.0.0", - "onnxruntime-common": "1.23.2" - } - }, - "node_modules/onnxruntime-node/node_modules/onnxruntime-common": { - "version": "1.23.2", - "resolved": "https://registry.npmmirror.com/onnxruntime-common/-/onnxruntime-common-1.23.2.tgz", - "integrity": "sha512-5LFsC9Dukzp2WV6kNHYLNzp8sT6V02IubLCbzw2Xd6X5GOlr65gAX6xiJwyi2URJol/s71gaQLC5F2C25AAR2w==", - "license": "MIT" - }, - "node_modules/onnxruntime-web": { - "version": "1.14.0", - "resolved": "https://registry.npmmirror.com/onnxruntime-web/-/onnxruntime-web-1.14.0.tgz", - "integrity": "sha512-Kcqf43UMfW8mCydVGcX9OMXI2VN17c0p6XvR7IPSZzBf/6lteBzXHvcEVWDPmCKuGombl997HgLqj91F11DzXw==", - "license": "MIT", - "dependencies": { - "flatbuffers": "^1.12.0", - "guid-typescript": "^1.0.9", - "long": "^4.0.0", - "onnx-proto": "^4.0.4", - "onnxruntime-common": "~1.14.0", - "platform": "^1.3.6" - } - }, - "node_modules/onnxruntime-web/node_modules/flatbuffers": { - "version": "1.12.0", - "resolved": "https://registry.npmmirror.com/flatbuffers/-/flatbuffers-1.12.0.tgz", - "integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==", - "license": "SEE LICENSE IN LICENSE.txt" - }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz", @@ -9604,18 +7406,6 @@ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "license": "(MIT AND Zlib)" }, - "node_modules/parse-ms": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/parse-ms/-/parse-ms-4.0.0.tgz", - "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -9630,6 +7420,7 @@ "version": "3.1.1", "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -9702,12 +7493,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmmirror.com/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", - "license": "MIT" - }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/plist/-/plist-3.1.0.tgz", @@ -9790,33 +7575,6 @@ "node": ">=10" } }, - "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==", - "license": "MIT", - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-ms": { - "version": "9.3.0", - "resolved": "https://registry.npmmirror.com/pretty-ms/-/pretty-ms-9.3.0.tgz", - "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", - "license": "MIT", - "dependencies": { - "parse-ms": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/proc-log": { "version": "5.0.0", "resolved": "https://registry.npmmirror.com/proc-log/-/proc-log-5.0.0.tgz", @@ -9864,49 +7622,6 @@ "node": ">=10" } }, - "node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/protobufjs": { - "version": "6.11.4", - "resolved": "https://registry.npmmirror.com/protobufjs/-/protobufjs-6.11.4.tgz", - "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - }, - "bin": { - "pbjs": "bin/pbjs", - "pbts": "bin/pbts" - } - }, - "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", @@ -10101,16 +7816,11 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10181,6 +7891,7 @@ "version": "0.12.0", "resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -10207,7 +7918,9 @@ "version": "2.15.4", "resolved": "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz", "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, "license": "BSD-3-Clause", + "optional": true, "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", @@ -10351,13 +8064,17 @@ "version": "1.0.0", "resolved": "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz", "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "license": "MIT" + "dev": true, + "license": "MIT", + "optional": true }, "node_modules/serialize-error": { "version": "7.0.1", "resolved": "https://registry.npmmirror.com/serialize-error/-/serialize-error-7.0.1.tgz", "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, "license": "MIT", + "optional": true, "dependencies": { "type-fest": "^0.13.1" }, @@ -10372,7 +8089,9 @@ "version": "0.13.1", "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz", "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, "license": "(MIT OR CC0-1.0)", + "optional": true, "engines": { "node": ">=10" }, @@ -10384,6 +8103,7 @@ "version": "2.0.0", "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, "license": "ISC" }, "node_modules/set-cookie-parser": { @@ -10447,6 +8167,7 @@ "version": "2.0.0", "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -10459,6 +8180,7 @@ "version": "3.0.0", "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10468,6 +8190,7 @@ "version": "3.0.7", "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, "license": "ISC" }, "node_modules/simple-concat": { @@ -10515,30 +8238,6 @@ "simple-concat": "^1.0.0" } }, - "node_modules/simple-git": { - "version": "3.30.0", - "resolved": "https://registry.npmmirror.com/simple-git/-/simple-git-3.30.0.tgz", - "integrity": "sha512-q6lxyDsCmEal/MEGhP1aVyQ3oxnagGlBDOVSIB4XUVLl1iZh0Pah6ebC9V4xBap/RfgP2WlI8EKs0WS0rMEJHg==", - "license": "MIT", - "dependencies": { - "@kwsites/file-exists": "^1.1.1", - "@kwsites/promise-deferred": "^1.1.1", - "debug": "^4.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/steveukx/git-js?sponsor=1" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.4.tgz", - "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -10558,12 +8257,6 @@ "integrity": "sha512-2NCmWxY7A9pYKGXNBfteo4hy14gWu47rg5692peVMst6lQLPKrVjhY+UTEsPI5ceFRJSl3gVgMYaUi/hKuaiKw==", "license": "ISC" }, - "node_modules/sleep-promise": { - "version": "9.1.0", - "resolved": "https://registry.npmmirror.com/sleep-promise/-/sleep-promise-9.1.0.tgz", - "integrity": "sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==", - "license": "MIT" - }, "node_modules/slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-3.0.0.tgz", @@ -10656,7 +8349,9 @@ "version": "1.1.3", "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause" + "dev": true, + "license": "BSD-3-Clause", + "optional": true }, "node_modules/ssri": { "version": "12.0.0", @@ -10681,118 +8376,6 @@ "node": ">= 6" } }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stdout-update": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/stdout-update/-/stdout-update-4.0.1.tgz", - "integrity": "sha512-wiS21Jthlvl1to+oorePvcyrIkiG/6M3D3VTmDUlJm7Cy6SbFhKkAvX+YBuHLxck/tO3mrdpC/cNesigQc3+UQ==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^6.2.0", - "ansi-styles": "^6.2.1", - "string-width": "^7.1.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/stdout-update/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/stdout-update/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/stdout-update/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/stdout-update/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stdout-update/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/steno": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/steno/-/steno-4.0.2.tgz", - "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/streamx": { - "version": "2.23.0", - "resolved": "https://registry.npmmirror.com/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" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", @@ -10806,6 +8389,7 @@ "version": "4.2.3", "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -10836,6 +8420,7 @@ "version": "6.0.1", "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -10899,6 +8484,7 @@ "version": "7.2.0", "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -10907,34 +8493,11 @@ "node": ">=8" } }, - "node_modules/table-layout": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/table-layout/-/table-layout-4.1.1.tgz", - "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", - "license": "MIT", - "peer": true, - "dependencies": { - "array-back": "^6.2.2", - "wordwrapjs": "^5.1.0" - }, - "engines": { - "node": ">=12.17" - } - }, - "node_modules/table-layout/node_modules/array-back": { - "version": "6.2.2", - "resolved": "https://registry.npmmirror.com/array-back/-/array-back-6.2.2.tgz", - "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12.17" - } - }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmmirror.com/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -10986,6 +8549,7 @@ "version": "2.1.0", "resolved": "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -10998,6 +8562,7 @@ "version": "3.3.6", "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -11010,6 +8575,7 @@ "version": "5.0.0", "resolved": "https://registry.npmmirror.com/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -11019,6 +8585,7 @@ "version": "2.1.2", "resolved": "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -11032,6 +8599,7 @@ "version": "3.3.6", "resolved": "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -11044,6 +8612,7 @@ "version": "4.0.0", "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, "license": "ISC" }, "node_modules/temp-file": { @@ -11095,15 +8664,6 @@ "node": ">= 10.0.0" } }, - "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==", - "license": "Apache-2.0", - "dependencies": { - "b4a": "^1.6.4" - } - }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -11201,15 +8761,6 @@ "node": ">=8.0" } }, - "node_modules/toad-cache": { - "version": "3.7.0", - "resolved": "https://registry.npmmirror.com/toad-cache/-/toad-cache-3.7.0.tgz", - "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -11254,7 +8805,7 @@ "version": "5.9.3", "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -11264,16 +8815,6 @@ "node": ">=14.17" } }, - "node_modules/typical": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/uint8array-extras": { "version": "1.5.0", "resolved": "https://registry.npmmirror.com/uint8array-extras/-/uint8array-extras-1.5.0.tgz", @@ -11290,6 +8831,7 @@ "version": "7.16.0", "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, "license": "MIT" }, "node_modules/unique-filename": { @@ -11318,18 +8860,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/universal-github-app-jwt": { - "version": "2.2.2", - "resolved": "https://registry.npmmirror.com/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz", - "integrity": "sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==", - "license": "MIT" - }, - "node_modules/universal-user-agent": { - "version": "7.0.3", - "resolved": "https://registry.npmmirror.com/universal-user-agent/-/universal-user-agent-7.0.3.tgz", - "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", - "license": "ISC" - }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", @@ -11381,12 +8911,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "license": "MIT" - }, "node_modules/utf8-byte-length": { "version": "1.0.5", "resolved": "https://registry.npmmirror.com/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", @@ -11409,15 +8933,6 @@ "base64-arraybuffer": "^1.0.2" } }, - "node_modules/validate-npm-package-name": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", - "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/verror": { "version": "1.10.1", "resolved": "https://registry.npmmirror.com/verror/-/verror-1.10.1.tgz", @@ -11591,6 +9106,7 @@ "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -11606,25 +9122,17 @@ "version": "1.1.5", "resolved": "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/wordwrapjs": { - "version": "5.1.1", - "resolved": "https://registry.npmmirror.com/wordwrapjs/-/wordwrapjs-5.1.1.tgz", - "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12.17" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -11677,6 +9185,7 @@ "version": "5.0.8", "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -11693,6 +9202,7 @@ "version": "17.7.2", "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -11711,6 +9221,7 @@ "version": "21.1.1", "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -11740,18 +9251,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/zip-stream": { "version": "4.1.1", "resolved": "https://registry.npmmirror.com/zip-stream/-/zip-stream-4.1.1.tgz", diff --git a/package.json b/package.json index 48ae0a5..5b102ed 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "weflow", - "version": "1.0.4", - "description": "WeFlow - 微信聊天记录查看工具", + "version": "1.1.0", + "description": "WeFlow", "main": "dist-electron/main.js", "author": "cc", "scripts": { - "postinstall": "electron-rebuild -f -w @lancedb/lancedb,onnxruntime-node", - "rebuild": "electron-rebuild -f -w @lancedb/lancedb,onnxruntime-node", + "postinstall": "echo 'No native modules to rebuild'", + "rebuild": "echo 'No native modules to rebuild'", "dev": "vite", "build": "vue-tsc && vite build && electron-builder", "preview": "vite preview", @@ -14,8 +14,6 @@ "electron:build": "npm run build" }, "dependencies": { - "@lancedb/lancedb": "^0.23.1-beta.1", - "@lancedb/lancedb-win32-x64-msvc": "^0.22.3", "better-sqlite3": "^12.5.0", "echarts": "^5.5.1", "echarts-for-react": "^3.0.2", @@ -27,7 +25,6 @@ "jszip": "^3.10.1", "koffi": "^2.9.0", "lucide-react": "^0.562.0", - "onnxruntime-node": "^1.16.1", "react": "^19.2.3", "react-dom": "^19.2.3", "react-router-dom": "^7.1.1", @@ -51,10 +48,6 @@ }, "build": { "appId": "com.WeFlow.app", - "asarUnpack": [ - "**/node_modules/@lancedb/lancedb/**/*", - "**/node_modules/onnxruntime-node/**/*" - ], "publish": { "provider": "github", "releaseType": "release" diff --git a/resources/wcdb_api.dll b/resources/wcdb_api.dll index 70e4eb7..3428494 100644 Binary files a/resources/wcdb_api.dll and b/resources/wcdb_api.dll differ diff --git a/src/App.tsx b/src/App.tsx index 60c8b16..ed93702 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,6 +7,7 @@ import WelcomePage from './pages/WelcomePage' import HomePage from './pages/HomePage' import ChatPage from './pages/ChatPage' import AnalyticsPage from './pages/AnalyticsPage' +import AnalyticsWelcomePage from './pages/AnalyticsWelcomePage' import AnnualReportPage from './pages/AnnualReportPage' import AnnualReportWindow from './pages/AnnualReportWindow' import AgreementPage from './pages/AgreementPage' @@ -308,7 +309,8 @@ function App() { } /> } /> } /> - } /> + } /> + } /> } /> } /> } /> diff --git a/src/components/Avatar.scss b/src/components/Avatar.scss new file mode 100644 index 0000000..34b11b2 --- /dev/null +++ b/src/components/Avatar.scss @@ -0,0 +1,79 @@ +.avatar-component { + position: relative; + display: inline-block; + overflow: hidden; + background-color: var(--bg-tertiary, #f5f5f5); + flex-shrink: 0; + border-radius: 4px; + /* Default radius */ + + &.circle { + border-radius: 50%; + } + + &.rounded { + border-radius: 6px; + } + + /* Image styling */ + img.avatar-image { + width: 100%; + height: 100%; + object-fit: cover; + opacity: 0; + transition: opacity 0.3s ease-in-out; + border-radius: inherit; + + &.loaded { + opacity: 1; + } + + &.instant { + transition: none !important; + opacity: 1 !important; + } + } + + /* Placeholder/Letter styling */ + .avatar-placeholder { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 500; + color: var(--text-secondary, #666); + background-color: var(--bg-tertiary, #e0e0e0); + font-size: 1.2em; + text-transform: uppercase; + user-select: none; + border-radius: inherit; + } + + /* Loading Skeleton */ + .avatar-skeleton { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(90deg, + var(--bg-tertiary, #f0f0f0) 25%, + var(--bg-secondary, #e0e0e0) 50%, + var(--bg-tertiary, #f0f0f0) 75%); + background-size: 200% 100%; + animation: shimmer 1.5s infinite; + z-index: 1; + border-radius: inherit; + } + + @keyframes shimmer { + 0% { + background-position: 200% 0; + } + + 100% { + background-position: -200% 0; + } + } +} \ No newline at end of file diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx new file mode 100644 index 0000000..7406bd5 --- /dev/null +++ b/src/components/Avatar.tsx @@ -0,0 +1,129 @@ +import React, { useState, useEffect, useRef, useMemo } from 'react' +import { User } from 'lucide-react' +import { avatarLoadQueue } from '../utils/AvatarLoadQueue' +import './Avatar.scss' + +// 全局缓存已成功加载过的头像 URL,用于控制后续是否显示动画 +const loadedAvatarCache = new Set() + +interface AvatarProps { + src?: string + name?: string + size?: number | string + shape?: 'circle' | 'square' | 'rounded' + className?: string + lazy?: boolean + onClick?: () => void +} + +export const Avatar = React.memo(function Avatar({ + src, + name, + size = 48, + shape = 'rounded', + className = '', + lazy = true, + onClick +}: AvatarProps) { + // 如果 URL 已在缓存中,则直接标记为已加载,不显示骨架屏和淡入动画 + const isCached = useMemo(() => src ? loadedAvatarCache.has(src) : false, [src]) + const [imageLoaded, setImageLoaded] = useState(isCached) + const [imageError, setImageError] = useState(false) + const [shouldLoad, setShouldLoad] = useState(!lazy || isCached) + const [isInQueue, setIsInQueue] = useState(false) + const imgRef = useRef(null) + const containerRef = useRef(null) + + const getAvatarLetter = (): string => { + if (!name) return '?' + const chars = [...name] + return chars[0] || '?' + } + + // Intersection Observer for lazy loading + useEffect(() => { + if (!lazy || shouldLoad || isInQueue || !src || !containerRef.current || isCached) return + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting && !isInQueue) { + setIsInQueue(true) + avatarLoadQueue.enqueue(src).then(() => { + setShouldLoad(true) + }).catch(() => { + // 加载失败不要立刻显示错误,让浏览器渲染去报错 + setShouldLoad(true) + }).finally(() => { + setIsInQueue(false) + }) + observer.disconnect() + } + }) + }, + { rootMargin: '100px' } + ) + + observer.observe(containerRef.current) + + return () => observer.disconnect() + }, [src, lazy, shouldLoad, isInQueue, isCached]) + + // Reset state when src changes + useEffect(() => { + const cached = src ? loadedAvatarCache.has(src) : false + setImageLoaded(cached) + setImageError(false) + if (lazy && !cached) { + setShouldLoad(false) + setIsInQueue(false) + } else { + setShouldLoad(true) + } + }, [src, lazy]) + + // Check if image is already cached/loaded + useEffect(() => { + if (shouldLoad && imgRef.current?.complete && imgRef.current?.naturalWidth > 0) { + setImageLoaded(true) + } + }, [src, shouldLoad]) + + const style = { + width: typeof size === 'number' ? `${size}px` : size, + height: typeof size === 'number' ? `${size}px` : size, + } + + const hasValidUrl = !!src && !imageError && shouldLoad + + return ( +
+ {hasValidUrl ? ( + <> + {!imageLoaded &&
} + {name { + if (src) loadedAvatarCache.add(src) + setImageLoaded(true) + }} + onError={() => setImageError(true)} + loading={lazy ? "lazy" : "eager"} + /> + + ) : ( +
+ {name ? {getAvatarLetter()} : } +
+ )} +
+ ) +}) diff --git a/src/pages/AnalyticsPage.tsx b/src/pages/AnalyticsPage.tsx index 7c6cf45..2bf1868 100644 --- a/src/pages/AnalyticsPage.tsx +++ b/src/pages/AnalyticsPage.tsx @@ -1,10 +1,12 @@ import { useState, useEffect } from 'react' +import { useLocation } from 'react-router-dom' import { Users, Clock, MessageSquare, Send, Inbox, Calendar, Loader2, RefreshCw, User, Medal } from 'lucide-react' import ReactECharts from 'echarts-for-react' import { useAnalyticsStore } from '../stores/analyticsStore' import { useThemeStore } from '../stores/themeStore' import './AnalyticsPage.scss' import './DataManagementPage.scss' +import { Avatar } from '../components/Avatar' function AnalyticsPage() { const [isLoading, setIsLoading] = useState(false) @@ -28,7 +30,7 @@ function AnalyticsPage() { try { setLoadingStatus('正在统计消息数据...') - const statsResult = await window.electronAPI.analytics.getOverallStatistics() + const statsResult = await window.electronAPI.analytics.getOverallStatistics(forceRefresh) if (statsResult.success && statsResult.data) { setStatistics(statsResult.data) } else { @@ -55,7 +57,12 @@ function AnalyticsPage() { } } - useEffect(() => { loadData() }, []) + const location = useLocation() + + useEffect(() => { + const force = location.state?.forceRefresh === true + loadData(force) + }, [location.state]) const handleRefresh = () => loadData(true) @@ -289,7 +296,7 @@ function AnalyticsPage() {
{index + 1}
- {contact.avatarUrl ? :
} + {index < 3 &&
}
diff --git a/src/pages/AnalyticsWelcomePage.scss b/src/pages/AnalyticsWelcomePage.scss new file mode 100644 index 0000000..7a61efb --- /dev/null +++ b/src/pages/AnalyticsWelcomePage.scss @@ -0,0 +1,119 @@ +.analytics-welcome-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + padding: 40px; + background: var(--bg-primary); + color: var(--text-primary); + animation: fadeIn 0.4s ease-out; + + .welcome-content { + text-align: center; + max-width: 600px; + + .icon-wrapper { + width: 80px; + height: 80px; + margin: 0 auto 24px; + background: rgba(7, 193, 96, 0.1); + border-radius: 20px; + display: flex; + align-items: center; + justify-content: center; + color: #07c160; + + svg { + width: 40px; + height: 40px; + } + } + + h1 { + font-size: 28px; + margin-bottom: 12px; + font-weight: 600; + } + + p { + color: var(--text-secondary); + margin-bottom: 40px; + font-size: 16px; + line-height: 1.6; + } + + .action-cards { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-top: 20px; + + button { + display: flex; + flex-direction: column; + align-items: center; + padding: 30px 20px; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 12px; + cursor: pointer; + transition: all 0.2s ease; + text-align: center; + + &:hover:not(:disabled) { + transform: translateY(-2px); + border-color: #07c160; + box-shadow: 0 4px 12px rgba(7, 193, 96, 0.1); + + .card-icon { + color: #07c160; + background: rgba(7, 193, 96, 0.1); + } + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + filter: grayscale(100%); + } + + .card-icon { + width: 50px; + height: 50px; + border-radius: 12px; + background: var(--bg-tertiary); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 16px; + color: var(--text-secondary); + transition: all 0.2s ease; + } + + h3 { + font-size: 18px; + margin-bottom: 8px; + color: var(--text-primary); + } + + span { + font-size: 13px; + color: var(--text-tertiary); + } + } + } + } +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} \ No newline at end of file diff --git a/src/pages/AnalyticsWelcomePage.tsx b/src/pages/AnalyticsWelcomePage.tsx new file mode 100644 index 0000000..ea85aff --- /dev/null +++ b/src/pages/AnalyticsWelcomePage.tsx @@ -0,0 +1,63 @@ +import { useNavigate } from 'react-router-dom' +import { BarChart2, History, RefreshCcw } from 'lucide-react' +import { useAnalyticsStore } from '../stores/analyticsStore' +import './AnalyticsWelcomePage.scss' + +function AnalyticsWelcomePage() { + const navigate = useNavigate() + // 检查是否有任何缓存数据加载或基本的存储状态表明它已准备好。 + // 实际上,如果 store 没有持久化,`isLoaded` 可能会在应用刷新时重置。 + // 如果用户点击“加载缓存”但缓存为空,AnalyticsPage 的逻辑(loadData 不带 force)将尝试从后端缓存加载。 + // 如果后端缓存也为空,则会重新计算。 + + // 我们也可以检查 `lastLoadTime` 来显示“上次更新:xxx”(如果已持久化)。 + const { lastLoadTime } = useAnalyticsStore() + + const handleLoadCache = () => { + navigate('/analytics/view') + } + + const handleNewAnalysis = () => { + navigate('/analytics/view', { state: { forceRefresh: true } }) + } + + const formatLastTime = (ts: number | null) => { + if (!ts) return '无记录' + return new Date(ts).toLocaleString() + } + + return ( +
+
+
+ +
+

私聊数据分析

+

+ WeFlow 可以分析您的聊天记录,生成详细的统计报表。
+ 您可以选择加载上次的分析结果(速度快),或者开始新的分析(数据最新)。 +

+ +
+ + + +
+
+
+ ) +} + +export default AnalyticsWelcomePage diff --git a/src/pages/ChatPage.scss b/src/pages/ChatPage.scss index e53857c..2c7d462 100644 --- a/src/pages/ChatPage.scss +++ b/src/pages/ChatPage.scss @@ -1426,10 +1426,12 @@ height: 6px; opacity: 0.5; } + 50% { height: 16px; opacity: 1; } + 100% { height: 6px; opacity: 0.5; @@ -1875,4 +1877,4 @@ opacity: 1; transform: translateX(0); } -} +} \ No newline at end of file diff --git a/src/pages/ChatPage.tsx b/src/pages/ChatPage.tsx index 767cf6d..03f8c0a 100644 --- a/src/pages/ChatPage.tsx +++ b/src/pages/ChatPage.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react' import { Search, MessageSquare, AlertCircle, Loader2, RefreshCw, X, ChevronDown, Info, Calendar, Database, Hash, Play, Pause, Image as ImageIcon } from 'lucide-react' +import { createPortal } from 'react-dom' import { useChatStore } from '../stores/chatStore' import type { ChatSession, Message } from '../types/models' import { getEmojiPath } from 'wechat-emojis' @@ -23,65 +24,10 @@ interface SessionDetail { messageTables: { dbName: string; tableName: string; count: number }[] } -// 全局头像加载队列管理器(限制并发,避免卡顿) -class AvatarLoadQueue { - private queue: Array<{ url: string; resolve: () => void; reject: () => void }> = [] - private loading = new Set() - private readonly maxConcurrent = 1 // 一次只加载1个头像,避免卡顿 - private readonly delayBetweenBatches = 100 // 批次间延迟100ms,给UI喘息时间 - - async enqueue(url: string): Promise { - // 如果已经在加载中,直接返回 - if (this.loading.has(url)) { - return Promise.resolve() - } - - return new Promise((resolve, reject) => { - this.queue.push({ url, resolve, reject }) - this.processQueue() - }) - } - - private async processQueue() { - // 如果已达到最大并发数,等待 - if (this.loading.size >= this.maxConcurrent) { - return - } - - // 如果队列为空,返回 - if (this.queue.length === 0) { - return - } - - // 取出一个任务 - const task = this.queue.shift() - if (!task) return - - this.loading.add(task.url) - - // 加载图片 - const img = new Image() - img.onload = () => { - this.loading.delete(task.url) - task.resolve() - // 延迟一下再处理下一个,避免一次性加载太多 - setTimeout(() => this.processQueue(), this.delayBetweenBatches) - } - img.onerror = () => { - this.loading.delete(task.url) - task.reject() - setTimeout(() => this.processQueue(), this.delayBetweenBatches) - } - img.src = task.url - } - - clear() { - this.queue = [] - this.loading.clear() - } -} - -const avatarLoadQueue = new AvatarLoadQueue() +// 全局头像加载队列管理器已移至 src/utils/AvatarLoadQueue.ts +// 全局头像加载队列管理器已移至 src/utils/AvatarLoadQueue.ts +import { avatarLoadQueue } from '../utils/AvatarLoadQueue' +import { Avatar } from '../components/Avatar' // 头像组件 - 支持骨架屏加载和懒加载(优化:限制并发,使用 memo 避免不必要的重渲染) // 会话项组件(使用 memo 优化,避免不必要的重渲染) @@ -107,7 +53,12 @@ const SessionItem = React.memo(function SessionItem({ className={`session-item ${isActive ? 'active' : ''}`} onClick={() => onSelect(session)} > - +
{session.displayName || session.username} @@ -138,109 +89,7 @@ const SessionItem = React.memo(function SessionItem({ ) }) -const SessionAvatar = React.memo(function SessionAvatar({ session, size = 48 }: { session: ChatSession; size?: number }) { - const [imageLoaded, setImageLoaded] = useState(false) - const [imageError, setImageError] = useState(false) - const [shouldLoad, setShouldLoad] = useState(false) - const [isInQueue, setIsInQueue] = useState(false) - const imgRef = useRef(null) - const containerRef = useRef(null) - const isGroup = session.username.includes('@chatroom') - const getAvatarLetter = (): string => { - const name = session.displayName || session.username - if (!name) return '?' - const chars = [...name] - return chars[0] || '?' - } - - // 使用 Intersection Observer 实现懒加载(优化性能) - useEffect(() => { - if (!containerRef.current || shouldLoad || isInQueue) return - if (!session.avatarUrl) { - // 没有头像URL,不需要加载 - return - } - - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting && !isInQueue) { - // 加入加载队列,而不是立即加载 - setIsInQueue(true) - avatarLoadQueue.enqueue(session.avatarUrl!).then(() => { - setShouldLoad(true) - }).catch(() => { - setImageError(true) - }).finally(() => { - setIsInQueue(false) - }) - observer.disconnect() - } - }) - }, - { - rootMargin: '50px' // 减少预加载距离,只提前50px - } - ) - - observer.observe(containerRef.current) - - return () => { - observer.disconnect() - } - }, [session.avatarUrl, shouldLoad, isInQueue]) - - // 当 avatarUrl 变化时重置状态 - useEffect(() => { - setImageLoaded(false) - setImageError(false) - setShouldLoad(false) - setIsInQueue(false) - }, [session.avatarUrl]) - - // 检查图片是否已经从缓存加载完成 - useEffect(() => { - if (shouldLoad && imgRef.current?.complete && imgRef.current?.naturalWidth > 0) { - setImageLoaded(true) - } - }, [session.avatarUrl, shouldLoad]) - - const hasValidUrl = session.avatarUrl && !imageError && shouldLoad - - return ( -
- {hasValidUrl ? ( - <> - {!imageLoaded &&
} - setImageLoaded(true)} - onError={() => setImageError(true)} - loading="lazy" - /> - - ) : ( - {getAvatarLetter()} - )} -
- ) -}, (prevProps, nextProps) => { - // 自定义比较函数,只在关键属性变化时重渲染 - return ( - prevProps.session.username === nextProps.session.username && - prevProps.session.displayName === nextProps.session.displayName && - prevProps.session.avatarUrl === nextProps.session.avatarUrl && - prevProps.size === nextProps.size - ) -}) function ChatPage(_props: ChatPageProps) { const { @@ -380,10 +229,8 @@ function ChatPage(_props: ChatPageProps) { // 确保 nextSessions 也是数组 if (Array.isArray(nextSessions)) { setSessions(nextSessions) - // 延迟启动联系人信息加载,确保UI先渲染完成 - setTimeout(() => { - void enrichSessionsContactInfo(nextSessions) - }, 500) + // 立即启动联系人信息加载,不再延迟 500ms + void enrichSessionsContactInfo(nextSessions) } else { console.error('mergeSessions returned non-array:', nextSessions) setSessions(sessionsArray) @@ -420,8 +267,7 @@ function ChatPage(_props: ChatPageProps) { console.log(`[性能监控] 开始加载联系人信息,会话数: ${sessions.length}`) const totalStart = performance.now() - // 延迟启动,等待UI渲染完成 - await new Promise(resolve => setTimeout(resolve, 500)) + // 移除初始 500ms 延迟,让后台加载与 UI 渲染并行 // 检查是否被取消 if (enrichCancelledRef.current) { @@ -430,8 +276,8 @@ function ChatPage(_props: ChatPageProps) { } try { - // 找出需要加载联系人信息的会话(没有缓存的) - const needEnrich = sessions.filter(s => !s.avatarUrl && (!s.displayName || s.displayName === s.username)) + // 找出需要加载联系人信息的会话(没有头像或者没有显示名称的) + const needEnrich = sessions.filter(s => !s.avatarUrl || !s.displayName || s.displayName === s.username) if (needEnrich.length === 0) { console.log('[性能监控] 所有联系人信息已缓存,跳过加载') isEnrichingRef.current = false @@ -584,9 +430,15 @@ function ChatPage(_props: ChatPageProps) { } if (result.success && result.contacts) { - // 将更新加入队列,而不是立即更新 + // 将更新加入队列,用于侧边栏更新 for (const [username, contact] of Object.entries(result.contacts)) { contactUpdateQueueRef.current.set(username, contact) + + // 【核心优化】同步更新全局发送者头像缓存,供 MessageBubble 使用 + senderAvatarCache.set(username, { + avatarUrl: contact.avatarUrl, + displayName: contact.displayName + }) } // 触发批量更新 flushContactUpdates() @@ -660,6 +512,31 @@ function ChatPage(_props: ChatPageProps) { if (result.success && result.messages) { if (offset === 0) { setMessages(result.messages) + + // 预取发送者信息:在关闭加载遮罩前处理 + const unreadCount = session?.unreadCount ?? 0 + const isGroup = sessionId.includes('@chatroom') + if (isGroup && result.messages.length > 0) { + const unknownSenders = [...new Set(result.messages + .filter(m => m.isSend !== 1 && m.senderUsername && !senderAvatarCache.has(m.senderUsername)) + .map(m => m.senderUsername as string) + )] + if (unknownSenders.length > 0) { + console.log(`[性能监控] 预取消息发送者信息: ${unknownSenders.length} 个`) + // 在批量请求前,先将这些发送者标记为加载中,防止 MessageBubble 触发重复请求 + const batchPromise = loadContactInfoBatch(unknownSenders) + unknownSenders.forEach(username => { + if (!senderAvatarLoading.has(username)) { + senderAvatarLoading.set(username, batchPromise.then(() => senderAvatarCache.get(username) || null)) + } + }) + // 确保在请求完成后清理 loading 状态 + batchPromise.finally(() => { + unknownSenders.forEach(username => senderAvatarLoading.delete(username)) + }) + } + } + // 首次加载滚动到底部 requestAnimationFrame(() => { if (messageListRef.current) { @@ -668,6 +545,27 @@ function ChatPage(_props: ChatPageProps) { }) } else { appendMessages(result.messages, true) + + // 加载更多也同样处理发送者信息预取 + const isGroup = sessionId.includes('@chatroom') + if (isGroup) { + const unknownSenders = [...new Set(result.messages + .filter(m => m.isSend !== 1 && m.senderUsername && !senderAvatarCache.has(m.senderUsername)) + .map(m => m.senderUsername as string) + )] + if (unknownSenders.length > 0) { + const batchPromise = loadContactInfoBatch(unknownSenders) + unknownSenders.forEach(username => { + if (!senderAvatarLoading.has(username)) { + senderAvatarLoading.set(username, batchPromise.then(() => senderAvatarCache.get(username) || null)) + } + }) + batchPromise.finally(() => { + unknownSenders.forEach(username => senderAvatarLoading.delete(username)) + }) + } + } + // 加载更多后保持位置:让之前的第一条消息保持在原来的视觉位置 if (firstMsgEl && listEl) { requestAnimationFrame(() => { @@ -1101,8 +999,10 @@ function ChatPage(_props: ChatPageProps) {
)} + {/* ... (previous content) ... */} {isLoadingSessions ? (
+ {/* ... (skeleton items) ... */} {[1, 2, 3, 4, 5].map(i => (
@@ -1118,12 +1018,10 @@ function ChatPage(_props: ChatPageProps) { className="session-list" ref={sessionListRef} onScroll={() => { - // 标记正在滚动,暂停联系人信息加载 isScrollingRef.current = true if (sessionScrollTimeoutRef.current) { clearTimeout(sessionScrollTimeoutRef.current) } - // 滚动结束后200ms才认为滚动停止 sessionScrollTimeoutRef.current = window.setTimeout(() => { isScrollingRef.current = false sessionScrollTimeoutRef.current = null @@ -1147,6 +1045,8 @@ function ChatPage(_props: ChatPageProps) {

请先在数据管理页面解密数据库

)} + +
{/* 拖动调节条 */} @@ -1157,7 +1057,12 @@ function ChatPage(_props: ChatPageProps) { {currentSession ? ( <>
- +

{currentSession.displayName || currentSession.username}

{isGroupChat(currentSession.username) && ( @@ -1767,13 +1672,14 @@ function MessageBubble({ message, session, showTime, myAvatarUrl, isGroupChat }: )}
- {showImagePreview && ( + {showImagePreview && createPortal(
setShowImagePreview(false)}> 图片预览 e.stopPropagation()} /> -
+
, + document.body )} ) @@ -1920,11 +1826,15 @@ function MessageBubble({ message, session, showTime, myAvatarUrl, isGroupChat }: )}
- {avatarUrl ? ( - - ) : ( - {avatarLetter} - )} +
{/* 群聊中显示发送者名称 */} diff --git a/src/pages/GroupAnalyticsPage.tsx b/src/pages/GroupAnalyticsPage.tsx index 846b6e6..cd4d973 100644 --- a/src/pages/GroupAnalyticsPage.tsx +++ b/src/pages/GroupAnalyticsPage.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useRef } from 'react' import { Users, BarChart3, Clock, Image, Loader2, RefreshCw, User, Medal, Search, X, ChevronLeft, Copy, Check } from 'lucide-react' +import { Avatar } from '../components/Avatar' import ReactECharts from 'echarts-for-react' import DateRangePicker from '../components/DateRangePicker' import './GroupAnalyticsPage.scss' @@ -177,7 +178,7 @@ function GroupAnalyticsPage() { const getMediaOption = () => { if (!mediaStats || mediaStats.typeCounts.length === 0) return {} - + // 定义颜色映射 const colorMap: Record = { 1: '#3b82f6', // 文本 - 蓝色 @@ -188,13 +189,13 @@ function GroupAnalyticsPage() { 49: '#14b8a6', // 链接/文件 - 青色 [-1]: '#6b7280', // 其他 - 灰色 } - + const data = mediaStats.typeCounts.map(item => ({ name: item.name, value: item.count, itemStyle: { color: colorMap[item.type] || '#6b7280' } })) - + return { tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' }, series: [{ @@ -202,8 +203,8 @@ function GroupAnalyticsPage() { radius: ['40%', '70%'], center: ['50%', '50%'], itemStyle: { borderRadius: 8, borderColor: 'rgba(255,255,255,0.1)', borderWidth: 2 }, - label: { - show: true, + label: { + show: true, formatter: (params: { name: string; percent: number }) => { // 只显示占比大于3%的标签 return params.percent > 3 ? `${params.name}\n${params.percent.toFixed(1)}%` : '' @@ -256,11 +257,7 @@ function GroupAnalyticsPage() {
- {selectedMember.avatarUrl ? ( - - ) : ( -
- )} +

{selectedMember.displayName}

@@ -334,7 +331,7 @@ function GroupAnalyticsPage() { onClick={() => handleGroupSelect(group)} >
- {group.avatarUrl ? :
} +
{group.displayName} @@ -352,7 +349,7 @@ function GroupAnalyticsPage() {
- {selectedGroup?.avatarUrl ? :
} +

{selectedGroup?.displayName}

{selectedGroup?.memberCount} 位成员

@@ -424,7 +421,7 @@ function GroupAnalyticsPage() { {members.map(member => (
handleMemberClick(member)}>
- {member.avatarUrl ? :
} +
{member.displayName}
@@ -437,7 +434,7 @@ function GroupAnalyticsPage() {
{index + 1}
- {item.member.avatarUrl ? :
} + {index < 3 &&
}
diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx index 2f80c91..989522f 100644 --- a/src/pages/SettingsPage.tsx +++ b/src/pages/SettingsPage.tsx @@ -484,6 +484,7 @@ function SettingsPage() { {isFetchingImageKey ? '获取中...' : '自动获取图片密钥'} {imageKeyStatus &&
{imageKeyStatus}
} + {isFetchingImageKey &&
正在扫描内存,请稍候...
}
diff --git a/src/pages/WelcomePage.tsx b/src/pages/WelcomePage.tsx index c346569..f728565 100644 --- a/src/pages/WelcomePage.tsx +++ b/src/pages/WelcomePage.tsx @@ -506,7 +506,7 @@ function WelcomePage({ standalone = false }: WelcomePageProps) { {dbKeyStatus &&
{dbKeyStatus}
}
获取密钥会自动识别最近登录的账号
-
如果获取秘钥失败 请在微信打开后等待10秒后再登录
+
点击自动获取后微信将重新启动,当页面提示可以登录微信了再点击登录
)} @@ -533,6 +533,7 @@ function WelcomePage({ standalone = false }: WelcomePageProps) { {imageKeyStatus &&
{imageKeyStatus}
}
如获取失败,请先打开朋友圈图片再重试
+ {isFetchingImageKey &&
正在扫描内存,请稍候...
}
)} diff --git a/src/stores/analyticsStore.ts b/src/stores/analyticsStore.ts index 98ae4a1..7cc9559 100644 --- a/src/stores/analyticsStore.ts +++ b/src/stores/analyticsStore.ts @@ -1,4 +1,5 @@ import { create } from 'zustand' +import { persist } from 'zustand/middleware' interface ChatStatistics { totalMessages: number @@ -36,11 +37,11 @@ interface AnalyticsState { statistics: ChatStatistics | null rankings: ContactRanking[] timeDistribution: TimeDistribution | null - + // 状态 isLoaded: boolean lastLoadTime: number | null - + // Actions setStatistics: (data: ChatStatistics) => void setRankings: (data: ContactRanking[]) => void @@ -49,22 +50,29 @@ interface AnalyticsState { clearCache: () => void } -export const useAnalyticsStore = create((set) => ({ - statistics: null, - rankings: [], - timeDistribution: null, - isLoaded: false, - lastLoadTime: null, +export const useAnalyticsStore = create()( + persist( + (set) => ({ + statistics: null, + rankings: [], + timeDistribution: null, + isLoaded: false, + lastLoadTime: null, - setStatistics: (data) => set({ statistics: data }), - setRankings: (data) => set({ rankings: data }), - setTimeDistribution: (data) => set({ timeDistribution: data }), - markLoaded: () => set({ isLoaded: true, lastLoadTime: Date.now() }), - clearCache: () => set({ - statistics: null, - rankings: [], - timeDistribution: null, - isLoaded: false, - lastLoadTime: null - }), -})) + setStatistics: (data) => set({ statistics: data }), + setRankings: (data) => set({ rankings: data }), + setTimeDistribution: (data) => set({ timeDistribution: data }), + markLoaded: () => set({ isLoaded: true, lastLoadTime: Date.now() }), + clearCache: () => set({ + statistics: null, + rankings: [], + timeDistribution: null, + isLoaded: false, + lastLoadTime: null + }), + }), + { + name: 'analytics-storage', + } + ) +) diff --git a/src/types/electron.d.ts b/src/types/electron.d.ts index 11c2a41..d50e4b5 100644 --- a/src/types/electron.d.ts +++ b/src/types/electron.d.ts @@ -45,6 +45,7 @@ export interface ElectronAPI { testConnection: (dbPath: string, hexKey: string, wxid: string) => Promise<{ success: boolean; error?: string; sessionCount?: number }> open: (dbPath: string, hexKey: string, wxid: string) => Promise close: () => Promise + } key: { autoGetDbKey: () => Promise<{ success: boolean; key?: string; error?: string; logs?: string[] }> @@ -104,7 +105,7 @@ export interface ElectronAPI { onCacheResolved: (callback: (payload: { cacheKey: string; imageMd5?: string; imageDatName?: string; localPath: string }) => void) => () => void } analytics: { - getOverallStatistics: () => Promise<{ + getOverallStatistics: (force?: boolean) => Promise<{ success: boolean data?: { totalMessages: number diff --git a/src/utils/AvatarLoadQueue.ts b/src/utils/AvatarLoadQueue.ts new file mode 100644 index 0000000..85e297b --- /dev/null +++ b/src/utils/AvatarLoadQueue.ts @@ -0,0 +1,74 @@ + +// 全局头像加载队列管理器(限制并发,避免卡顿) +export class AvatarLoadQueue { + private queue: Array<{ url: string; resolve: () => void; reject: (error: Error) => void }> = [] + private loading = new Map>() + private activeCount = 0 + private readonly maxConcurrent = 3 + private readonly delayBetweenBatches = 10 + + private static instance: AvatarLoadQueue + + public static getInstance(): AvatarLoadQueue { + if (!AvatarLoadQueue.instance) { + AvatarLoadQueue.instance = new AvatarLoadQueue() + } + return AvatarLoadQueue.instance + } + + async enqueue(url: string): Promise { + if (!url) return Promise.resolve() + + // 核心修复:防止重复并发请求同一个 URL + const existingPromise = this.loading.get(url) + if (existingPromise) { + return existingPromise + } + + const loadPromise = new Promise((resolve, reject) => { + this.queue.push({ url, resolve, reject }) + this.processQueue() + }) + + this.loading.set(url, loadPromise) + loadPromise.finally(() => { + this.loading.delete(url) + }) + + return loadPromise + } + + private async processQueue() { + if (this.activeCount >= this.maxConcurrent || this.queue.length === 0) { + return + } + + const task = this.queue.shift() + if (!task) return + + this.activeCount++ + + const img = new Image() + img.onload = () => { + this.activeCount-- + task.resolve() + setTimeout(() => this.processQueue(), this.delayBetweenBatches) + } + img.onerror = () => { + this.activeCount-- + task.reject(new Error(`Failed: ${task.url}`)) + setTimeout(() => this.processQueue(), this.delayBetweenBatches) + } + img.src = task.url + + this.processQueue() + } + + clear() { + this.queue = [] + this.loading.clear() + this.activeCount = 0 + } +} + +export const avatarLoadQueue = AvatarLoadQueue.getInstance() diff --git a/vite.config.ts b/vite.config.ts index 640fd47..52a386c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -16,14 +16,7 @@ export default defineConfig({ } }, optimizeDeps: { - exclude: [ - '@lancedb/lancedb', - '@lancedb/lancedb-win32-x64-msvc', - 'node-llama-cpp', - 'onnxruntime-node', - '@xenova/transformers', - '@huggingface/transformers' - ] + exclude: [] }, plugins: [ react(), @@ -33,16 +26,10 @@ export default defineConfig({ vite: { build: { outDir: 'dist-electron', - rollupOptions: { + rollupOptions: { external: [ 'better-sqlite3', 'koffi', - 'node-llama-cpp', - '@lancedb/lancedb', - '@lancedb/lancedb-win32-x64-msvc', - 'onnxruntime-node', - '@xenova/transformers', - '@huggingface/transformers', 'fsevents' ] } @@ -57,12 +44,6 @@ export default defineConfig({ rollupOptions: { external: [ 'koffi', - 'node-llama-cpp', - '@lancedb/lancedb', - '@lancedb/lancedb-win32-x64-msvc', - 'onnxruntime-node', - '@xenova/transformers', - '@huggingface/transformers', 'fsevents' ], output: { @@ -73,30 +54,6 @@ export default defineConfig({ } } }, - { - entry: 'electron/cloneEmbeddingWorker.ts', - vite: { - build: { - outDir: 'dist-electron', - rollupOptions: { - external: [ - 'koffi', - 'node-llama-cpp', - '@lancedb/lancedb', - '@lancedb/lancedb-win32-x64-msvc', - 'onnxruntime-node', - '@xenova/transformers', - '@huggingface/transformers', - 'fsevents' - ], - output: { - entryFileNames: 'cloneEmbeddingWorker.js', - inlineDynamicImports: true - } - } - } - } - }, { entry: 'electron/imageSearchWorker.ts', vite: { @@ -111,6 +68,25 @@ export default defineConfig({ } } }, + { + entry: 'electron/wcdbWorker.ts', + vite: { + build: { + outDir: 'dist-electron', + rollupOptions: { + external: [ + 'better-sqlite3', + 'koffi', + 'fsevents' + ], + output: { + entryFileNames: 'wcdbWorker.js', + inlineDynamicImports: true + } + } + } + } + }, { entry: 'electron/preload.ts', onstart(options) { diff --git a/wx_key b/wx_key new file mode 160000 index 0000000..40be59d --- /dev/null +++ b/wx_key @@ -0,0 +1 @@ +Subproject commit 40be59dc472ac283b3de0e3a0ad87e029610082a