mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-06 20:42:57 +08:00
✨ Feature(custom): add vitest and optimize performance of aeshelper
This commit is contained in:
@@ -1,62 +1,63 @@
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
||||
export default defineConfig({
|
||||
main: {
|
||||
// Main process configuration
|
||||
plugins: [
|
||||
externalizeDepsPlugin()
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('src/renderer'),
|
||||
'~': resolve('src/main'),
|
||||
root: resolve('./'),
|
||||
'#': resolve('src/universal'),
|
||||
apis: resolve('src/main/apis'),
|
||||
'@core': resolve('src/main/apis/core')
|
||||
}
|
||||
}
|
||||
},
|
||||
preload: {
|
||||
// Preload scripts configuration
|
||||
plugins: [externalizeDepsPlugin(),
|
||||
VueI18nPlugin({
|
||||
/* options */
|
||||
// locale messages resource pre-compile option
|
||||
include: resolve(dirname(fileURLToPath(import.meta.url)), './src/renderer/i18n/locales/**')
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('src/renderer'),
|
||||
'~': resolve('src/main'),
|
||||
root: resolve('./'),
|
||||
'#': resolve('src/universal')
|
||||
}
|
||||
}
|
||||
},
|
||||
renderer: {
|
||||
// Renderer process configuration
|
||||
root: resolve('src/renderer'),
|
||||
publicDir: resolve('./public'),
|
||||
build: {
|
||||
outDir: resolve('dist/renderer')
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('src/renderer'),
|
||||
'~': resolve('src/main'),
|
||||
root: resolve('./'),
|
||||
'#': resolve('src/universal')
|
||||
}
|
||||
},
|
||||
plugins: [vue()],
|
||||
server: {
|
||||
port: 3000
|
||||
}
|
||||
}
|
||||
})
|
||||
/// <reference types="vitest/config" />
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
||||
export default defineConfig({
|
||||
main: {
|
||||
// Main process configuration
|
||||
plugins: [
|
||||
externalizeDepsPlugin()
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('src/renderer'),
|
||||
'~': resolve('src/main'),
|
||||
root: resolve('./'),
|
||||
'#': resolve('src/universal'),
|
||||
apis: resolve('src/main/apis'),
|
||||
'@core': resolve('src/main/apis/core')
|
||||
}
|
||||
}
|
||||
},
|
||||
preload: {
|
||||
// Preload scripts configuration
|
||||
plugins: [externalizeDepsPlugin(),
|
||||
VueI18nPlugin({
|
||||
/* options */
|
||||
// locale messages resource pre-compile option
|
||||
include: resolve(dirname(fileURLToPath(import.meta.url)), './src/renderer/i18n/locales/**')
|
||||
})
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('src/renderer'),
|
||||
'~': resolve('src/main'),
|
||||
root: resolve('./'),
|
||||
'#': resolve('src/universal')
|
||||
}
|
||||
}
|
||||
},
|
||||
renderer: {
|
||||
// Renderer process configuration
|
||||
root: resolve('src/renderer'),
|
||||
publicDir: resolve('./public'),
|
||||
build: {
|
||||
outDir: resolve('dist/renderer')
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('src/renderer'),
|
||||
'~': resolve('src/main'),
|
||||
root: resolve('./'),
|
||||
'#': resolve('src/universal')
|
||||
}
|
||||
},
|
||||
plugins: [vue()],
|
||||
server: {
|
||||
port: 3000
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"preview": "electron-vite preview",
|
||||
"release": "electron-vite build && electron-builder --publish always",
|
||||
"sha256": "node ./scripts/gen-sha256.js",
|
||||
"test": "vitest",
|
||||
"upload-beta": "node ./scripts/upload-beta.cjs",
|
||||
"upload-dist": "node ./scripts/upload-dist-to-r2.cjs"
|
||||
},
|
||||
@@ -133,6 +134,7 @@
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.39.0",
|
||||
"vite": "^7.1.0",
|
||||
"vitest": "^3.2.4",
|
||||
"vue-eslint-parser": "^10.2.0",
|
||||
"vue-tsc": "^3.0.5"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { bootstrap } from '~/lifeCycle'
|
||||
|
||||
bootstrap.launchApp()
|
||||
import { lifeCycle } from '~/lifeCycle'
|
||||
|
||||
lifeCycle.launchApp()
|
||||
|
||||
@@ -1,350 +1,350 @@
|
||||
import '~/lifeCycle/errorHandler'
|
||||
|
||||
import path from 'node:path'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import db from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
import { remoteNoticeHandler } from 'apis/app/remoteNotice'
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import { createTray, setDockMenu } from 'apis/app/system'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import axios from 'axios'
|
||||
import { app, dialog, globalShortcut, Notification, protocol, screen, shell } from 'electron'
|
||||
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
|
||||
import updater from 'electron-updater'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import busEventList from '~/events/busEventList'
|
||||
import { rpcServer } from '~/events/rpc'
|
||||
import { startFileServer, stopFileServer } from '~/fileServer'
|
||||
import { T as $t } from '~/i18n'
|
||||
import fixPath from '~/lifeCycle/fixPath'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import getManageApi from '~/manage/Main'
|
||||
import { clearTempFolder } from '~/manage/utils/common'
|
||||
import server from '~/server/index'
|
||||
import webServer from '~/server/webServer'
|
||||
import beforeOpen from '~/utils/beforeOpen'
|
||||
import clipboardPoll from '~/utils/clipboardPoll'
|
||||
import { configPaths } from '~/utils/configPaths'
|
||||
import { II18nLanguage, IRemoteNoticeTriggerHook, ISartMode, IWindowList } from '~/utils/enum'
|
||||
import { getUploadFiles } from '~/utils/handleArgv'
|
||||
import { initI18n } from '~/utils/handleI18n'
|
||||
import { notificationList } from '~/utils/notification'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '~/utils/static'
|
||||
import updateChecker from '~/utils/updateChecker'
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
const handleStartUpFiles = (argv: string[], cwd: string) => {
|
||||
const files = getUploadFiles(argv, cwd, logger)
|
||||
|
||||
if (files === null) {
|
||||
logger.info('cli -> uploading file from clipboard')
|
||||
uploadClipboardFiles()
|
||||
return true
|
||||
}
|
||||
|
||||
if (files.length > 0) {
|
||||
logger.info('cli -> uploading files from cli', ...files.map(file => file.path))
|
||||
const win = windowManager.getAvailableWindow()
|
||||
uploadChoosedFiles(win.webContents, files)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
updater.autoUpdater.setFeedURL({
|
||||
provider: 'generic',
|
||||
url: 'https://release.piclist.cn/latest',
|
||||
channel: 'latest'
|
||||
})
|
||||
|
||||
updater.autoUpdater.autoDownload = false
|
||||
|
||||
updater.autoUpdater.on('update-available', async (info: updater.UpdateInfo) => {
|
||||
const lang = db.get(configPaths.settings.language) || II18nLanguage.ZH_CN
|
||||
let updateLog = ''
|
||||
try {
|
||||
const url =
|
||||
lang === II18nLanguage.ZH_CN
|
||||
? 'https://release.piclist.cn/currentVersion.md'
|
||||
: 'https://release.piclist.cn/currentVersion_en.md'
|
||||
const res = await axios.get(url)
|
||||
updateLog = res.data
|
||||
} catch (e: any) {
|
||||
logger.error(e)
|
||||
}
|
||||
|
||||
const maxLogLength = 800
|
||||
let displayLog = updateLog
|
||||
let truncatedNote = ''
|
||||
|
||||
if (updateLog.length > maxLogLength) {
|
||||
const truncatePoint = updateLog.lastIndexOf('\n', maxLogLength)
|
||||
displayLog = updateLog.substring(0, truncatePoint > 0 ? truncatePoint : maxLogLength)
|
||||
truncatedNote =
|
||||
lang === II18nLanguage.ZH_CN
|
||||
? '\n\n... (更多详情请查看完整更新日志)'
|
||||
: '\n\n... (See full changelog for more details)'
|
||||
}
|
||||
|
||||
dialog
|
||||
.showMessageBox({
|
||||
type: 'info',
|
||||
title: $t('FIND_NEW_VERSION'),
|
||||
buttons: ['Yes', 'Go to download page'],
|
||||
message:
|
||||
$t('TIPS_FIND_NEW_VERSION', {
|
||||
v: info.version
|
||||
}) +
|
||||
'\n\n' +
|
||||
displayLog +
|
||||
truncatedNote,
|
||||
checkboxLabel: $t('NO_MORE_NOTICE'),
|
||||
checkboxChecked: false
|
||||
})
|
||||
.then(result => {
|
||||
if (result.response === 0) {
|
||||
updater.autoUpdater.downloadUpdate()
|
||||
} else {
|
||||
shell.openExternal('https://github.com/Kuingsmile/PicList/releases/latest')
|
||||
}
|
||||
db.set(configPaths.settings.showUpdateTip, !result.checkboxChecked)
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err)
|
||||
})
|
||||
})
|
||||
|
||||
updater.autoUpdater.on('download-progress', progressObj => {
|
||||
const percent = {
|
||||
progress: progressObj.percent
|
||||
}
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send('updateProgress', percent)
|
||||
})
|
||||
|
||||
updater.autoUpdater.on('update-downloaded', () => {
|
||||
dialog
|
||||
.showMessageBox({
|
||||
type: 'info',
|
||||
title: $t('UPDATE_DOWNLOADED'),
|
||||
buttons: ['Yes', 'No'],
|
||||
message: $t('TIPS_UPDATE_DOWNLOADED')
|
||||
})
|
||||
.then(result => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send('updateProgress', { progress: 100 })
|
||||
if (result.response === 0) {
|
||||
updater.autoUpdater.quitAndInstall()
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err)
|
||||
})
|
||||
})
|
||||
|
||||
updater.autoUpdater.on('error', err => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
class LifeCycle {
|
||||
async #beforeReady () {
|
||||
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
|
||||
// fix the $PATH in macOS & linux
|
||||
fixPath()
|
||||
beforeOpen()
|
||||
getManageApi()
|
||||
UpDownTaskQueue.getInstance()
|
||||
initI18n()
|
||||
rpcServer.start()
|
||||
busEventList.listen()
|
||||
}
|
||||
|
||||
#onReady () {
|
||||
const readyFunction = async () => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
installExtension(VUEJS_DEVTOOLS).then(name => {
|
||||
console.log(`Added Extension: ${JSON.stringify(name)}`)
|
||||
}).catch(err => {
|
||||
console.log('An error occurred: ', err)
|
||||
})
|
||||
}
|
||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||
windowManager.create(IWindowList.SETTING_WINDOW)
|
||||
const isAutoListenClipboard = db.get(configPaths.settings.isAutoListenClipboard) || false
|
||||
const ClipboardWatcher = clipboardPoll
|
||||
if (isAutoListenClipboard) {
|
||||
db.set(configPaths.settings.isListeningClipboard, true)
|
||||
ClipboardWatcher.startListening()
|
||||
ClipboardWatcher.on('change', () => {
|
||||
picgo.log.info('clipboard changed')
|
||||
uploadClipboardFiles()
|
||||
})
|
||||
} else {
|
||||
db.set(configPaths.settings.isListeningClipboard, false)
|
||||
}
|
||||
const isHideDock = db.get(configPaths.settings.isHideDock) || false
|
||||
let startMode = db.get(configPaths.settings.startMode) || ISartMode.QUIET
|
||||
if (process.platform === 'darwin' && startMode === ISartMode.MINI) {
|
||||
startMode = ISartMode.QUIET
|
||||
}
|
||||
const currentPicBed = db.get(configPaths.picBed.uploader) || db.get(configPaths.picBed.current) || 'smms'
|
||||
const currentPicBedConfig = db.get(`picBed.${currentPicBed}`)?._configName || 'Default'
|
||||
const tooltip = `${currentPicBed} ${currentPicBedConfig}`
|
||||
if (process.platform === 'darwin') {
|
||||
isHideDock ? app.dock?.hide() : setDockMenu()
|
||||
startMode !== ISartMode.NO_TRAY && createTray(tooltip)
|
||||
} else {
|
||||
createTray(tooltip)
|
||||
}
|
||||
db.set(configPaths.needReload, false)
|
||||
updateChecker()
|
||||
// 不需要阻塞
|
||||
process.nextTick(() => {
|
||||
shortKeyHandler.init()
|
||||
})
|
||||
server.startup()
|
||||
webServer.start()
|
||||
startFileServer()
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
handleStartUpFiles(process.argv, process.cwd())
|
||||
}
|
||||
|
||||
if (notificationList && notificationList.length > 0) {
|
||||
while (notificationList.length) {
|
||||
const option = notificationList.pop()
|
||||
const notice = new Notification(option!)
|
||||
notice.show()
|
||||
}
|
||||
}
|
||||
await remoteNoticeHandler.init()
|
||||
remoteNoticeHandler.triggerHook(IRemoteNoticeTriggerHook.APP_START)
|
||||
if (startMode === ISartMode.MINI && process.platform !== 'darwin') {
|
||||
windowManager.create(IWindowList.MINI_WINDOW)
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
miniWindow.removeAllListeners()
|
||||
if (db.get(configPaths.settings.miniWindowOntop)) {
|
||||
miniWindow.setAlwaysOnTop(true)
|
||||
}
|
||||
const { width, height } = screen.getPrimaryDisplay().workAreaSize
|
||||
const lastPosition = db.get(configPaths.settings.miniWindowPosition)
|
||||
if (lastPosition) {
|
||||
if (lastPosition[0] < 0 || lastPosition[0] > width || lastPosition[1] < 0 || lastPosition[1] > height) {
|
||||
miniWindow.setPosition(width - 100, height - 100)
|
||||
db.set(configPaths.settings.miniWindowPosition, [width - 100, height - 100])
|
||||
} else if (lastPosition[0] + miniWindow.getSize()[0] > width || lastPosition[1] + miniWindow.getSize()[1] > height) {
|
||||
miniWindow.setPosition(width - miniWindow.getSize()[0], height - miniWindow.getSize()[1])
|
||||
db.set(configPaths.settings.miniWindowPosition, [width - miniWindow.getSize()[0], height - miniWindow.getSize()[1]])
|
||||
} else {
|
||||
miniWindow.setPosition(lastPosition[0], lastPosition[1])
|
||||
}
|
||||
} else {
|
||||
miniWindow.setPosition(width - 100, height - 100)
|
||||
}
|
||||
const setPositionFunc = () => {
|
||||
const position = miniWindow.getPosition()
|
||||
db.set(configPaths.settings.miniWindowPosition, position)
|
||||
}
|
||||
miniWindow.on('close', setPositionFunc)
|
||||
miniWindow.on('move', setPositionFunc)
|
||||
miniWindow.show()
|
||||
miniWindow.focus()
|
||||
} else if (startMode === ISartMode.MAIN) {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
settingWindow.show()
|
||||
settingWindow.focus()
|
||||
}
|
||||
const clipboardDir = path.join(picgo.baseDir, CLIPBOARD_IMAGE_FOLDER)
|
||||
fs.emptyDir(clipboardDir)
|
||||
}
|
||||
app.whenReady().then(readyFunction)
|
||||
}
|
||||
|
||||
#onRunning () {
|
||||
app.on('second-instance', (_, commandLine, workingDirectory) => {
|
||||
logger.info('detect second instance')
|
||||
const result = handleStartUpFiles(commandLine, workingDirectory)
|
||||
if (!result) {
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
if (settingWindow.isMinimized()) {
|
||||
settingWindow.restore()
|
||||
}
|
||||
settingWindow.focus()
|
||||
}
|
||||
}
|
||||
})
|
||||
app.on('activate', () => {
|
||||
if (!windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||
}
|
||||
if (!windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.create(IWindowList.SETTING_WINDOW)
|
||||
}
|
||||
})
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: db.get(configPaths.settings.autoStart) || false
|
||||
})
|
||||
if (process.platform === 'win32') {
|
||||
app.setAppUserModelId('com.kuingsmile.piclist')
|
||||
}
|
||||
|
||||
if (process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.includes('Unity')) {
|
||||
process.env.XDG_CURRENT_DESKTOP = 'Unity'
|
||||
}
|
||||
}
|
||||
|
||||
#onQuit () {
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('will-quit', () => {
|
||||
UpDownTaskQueue.getInstance().persist()
|
||||
clearTempFolder()
|
||||
globalShortcut.unregisterAll()
|
||||
bus.removeAllListeners()
|
||||
server.shutdown()
|
||||
webServer.stop()
|
||||
stopFileServer()
|
||||
})
|
||||
// Exit cleanly on request from parent process in development mode.
|
||||
if (isDevelopment) {
|
||||
if (process.platform === 'win32') {
|
||||
process.on('message', data => {
|
||||
if (data === 'graceful-exit') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
process.on('SIGTERM', () => {
|
||||
app.quit()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async launchApp () {
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
await this.#beforeReady()
|
||||
this.#onReady()
|
||||
this.#onRunning()
|
||||
this.#onQuit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bootstrap = new LifeCycle()
|
||||
|
||||
export { bootstrap }
|
||||
import '~/lifeCycle/errorHandler'
|
||||
|
||||
import path from 'node:path'
|
||||
|
||||
import bus from '@core/bus'
|
||||
import db from '@core/datastore'
|
||||
import picgo from '@core/picgo'
|
||||
import logger from '@core/picgo/logger'
|
||||
import { remoteNoticeHandler } from 'apis/app/remoteNotice'
|
||||
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||
import { createTray, setDockMenu } from 'apis/app/system'
|
||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||
import windowManager from 'apis/app/window/windowManager'
|
||||
import axios from 'axios'
|
||||
import { app, dialog, globalShortcut, Notification, protocol, screen, shell } from 'electron'
|
||||
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
|
||||
import updater from 'electron-updater'
|
||||
import fs from 'fs-extra'
|
||||
|
||||
import busEventList from '~/events/busEventList'
|
||||
import { rpcServer } from '~/events/rpc'
|
||||
import { startFileServer, stopFileServer } from '~/fileServer'
|
||||
import { T as $t } from '~/i18n'
|
||||
import fixPath from '~/lifeCycle/fixPath'
|
||||
import UpDownTaskQueue from '~/manage/datastore/upDownTaskQueue'
|
||||
import getManageApi from '~/manage/Main'
|
||||
import { clearTempFolder } from '~/manage/utils/common'
|
||||
import server from '~/server/index'
|
||||
import webServer from '~/server/webServer'
|
||||
import beforeOpen from '~/utils/beforeOpen'
|
||||
import clipboardPoll from '~/utils/clipboardPoll'
|
||||
import { configPaths } from '~/utils/configPaths'
|
||||
import { II18nLanguage, IRemoteNoticeTriggerHook, ISartMode, IWindowList } from '~/utils/enum'
|
||||
import { getUploadFiles } from '~/utils/handleArgv'
|
||||
import { initI18n } from '~/utils/handleI18n'
|
||||
import { notificationList } from '~/utils/notification'
|
||||
import { CLIPBOARD_IMAGE_FOLDER } from '~/utils/static'
|
||||
import updateChecker from '~/utils/updateChecker'
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||
|
||||
const handleStartUpFiles = (argv: string[], cwd: string) => {
|
||||
const files = getUploadFiles(argv, cwd, logger)
|
||||
|
||||
if (files === null) {
|
||||
logger.info('cli -> uploading file from clipboard')
|
||||
uploadClipboardFiles()
|
||||
return true
|
||||
}
|
||||
|
||||
if (files.length > 0) {
|
||||
logger.info('cli -> uploading files from cli', ...files.map(file => file.path))
|
||||
const win = windowManager.getAvailableWindow()
|
||||
uploadChoosedFiles(win.webContents, files)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
updater.autoUpdater.setFeedURL({
|
||||
provider: 'generic',
|
||||
url: 'https://release.piclist.cn/latest',
|
||||
channel: 'latest'
|
||||
})
|
||||
|
||||
updater.autoUpdater.autoDownload = false
|
||||
|
||||
updater.autoUpdater.on('update-available', async (info: updater.UpdateInfo) => {
|
||||
const lang = db.get(configPaths.settings.language) || II18nLanguage.ZH_CN
|
||||
let updateLog = ''
|
||||
try {
|
||||
const url =
|
||||
lang === II18nLanguage.ZH_CN
|
||||
? 'https://release.piclist.cn/currentVersion.md'
|
||||
: 'https://release.piclist.cn/currentVersion_en.md'
|
||||
const res = await axios.get(url)
|
||||
updateLog = res.data
|
||||
} catch (e: any) {
|
||||
logger.error(e)
|
||||
}
|
||||
|
||||
const maxLogLength = 800
|
||||
let displayLog = updateLog
|
||||
let truncatedNote = ''
|
||||
|
||||
if (updateLog.length > maxLogLength) {
|
||||
const truncatePoint = updateLog.lastIndexOf('\n', maxLogLength)
|
||||
displayLog = updateLog.substring(0, truncatePoint > 0 ? truncatePoint : maxLogLength)
|
||||
truncatedNote =
|
||||
lang === II18nLanguage.ZH_CN
|
||||
? '\n\n... (更多详情请查看完整更新日志)'
|
||||
: '\n\n... (See full changelog for more details)'
|
||||
}
|
||||
|
||||
dialog
|
||||
.showMessageBox({
|
||||
type: 'info',
|
||||
title: $t('FIND_NEW_VERSION'),
|
||||
buttons: ['Yes', 'Go to download page'],
|
||||
message:
|
||||
$t('TIPS_FIND_NEW_VERSION', {
|
||||
v: info.version
|
||||
}) +
|
||||
'\n\n' +
|
||||
displayLog +
|
||||
truncatedNote,
|
||||
checkboxLabel: $t('NO_MORE_NOTICE'),
|
||||
checkboxChecked: false
|
||||
})
|
||||
.then(result => {
|
||||
if (result.response === 0) {
|
||||
updater.autoUpdater.downloadUpdate()
|
||||
} else {
|
||||
shell.openExternal('https://github.com/Kuingsmile/PicList/releases/latest')
|
||||
}
|
||||
db.set(configPaths.settings.showUpdateTip, !result.checkboxChecked)
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err)
|
||||
})
|
||||
})
|
||||
|
||||
updater.autoUpdater.on('download-progress', progressObj => {
|
||||
const percent = {
|
||||
progress: progressObj.percent
|
||||
}
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send('updateProgress', percent)
|
||||
})
|
||||
|
||||
updater.autoUpdater.on('update-downloaded', () => {
|
||||
dialog
|
||||
.showMessageBox({
|
||||
type: 'info',
|
||||
title: $t('UPDATE_DOWNLOADED'),
|
||||
buttons: ['Yes', 'No'],
|
||||
message: $t('TIPS_UPDATE_DOWNLOADED')
|
||||
})
|
||||
.then(result => {
|
||||
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
window.webContents.send('updateProgress', { progress: 100 })
|
||||
if (result.response === 0) {
|
||||
updater.autoUpdater.quitAndInstall()
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(err)
|
||||
})
|
||||
})
|
||||
|
||||
updater.autoUpdater.on('error', err => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
class LifeCycle {
|
||||
async #beforeReady () {
|
||||
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
|
||||
// fix the $PATH in macOS & linux
|
||||
fixPath()
|
||||
beforeOpen()
|
||||
getManageApi()
|
||||
UpDownTaskQueue.getInstance()
|
||||
initI18n()
|
||||
rpcServer.start()
|
||||
busEventList.listen()
|
||||
}
|
||||
|
||||
#onReady () {
|
||||
const readyFunction = async () => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
installExtension(VUEJS_DEVTOOLS).then(name => {
|
||||
console.log(`Added Extension: ${JSON.stringify(name)}`)
|
||||
}).catch(err => {
|
||||
console.log('An error occurred: ', err)
|
||||
})
|
||||
}
|
||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||
windowManager.create(IWindowList.SETTING_WINDOW)
|
||||
const isAutoListenClipboard = db.get(configPaths.settings.isAutoListenClipboard) || false
|
||||
const ClipboardWatcher = clipboardPoll
|
||||
if (isAutoListenClipboard) {
|
||||
db.set(configPaths.settings.isListeningClipboard, true)
|
||||
ClipboardWatcher.startListening()
|
||||
ClipboardWatcher.on('change', () => {
|
||||
picgo.log.info('clipboard changed')
|
||||
uploadClipboardFiles()
|
||||
})
|
||||
} else {
|
||||
db.set(configPaths.settings.isListeningClipboard, false)
|
||||
}
|
||||
const isHideDock = db.get(configPaths.settings.isHideDock) || false
|
||||
let startMode = db.get(configPaths.settings.startMode) || ISartMode.QUIET
|
||||
if (process.platform === 'darwin' && startMode === ISartMode.MINI) {
|
||||
startMode = ISartMode.QUIET
|
||||
}
|
||||
const currentPicBed = db.get(configPaths.picBed.uploader) || db.get(configPaths.picBed.current) || 'smms'
|
||||
const currentPicBedConfig = db.get(`picBed.${currentPicBed}`)?._configName || 'Default'
|
||||
const tooltip = `${currentPicBed} ${currentPicBedConfig}`
|
||||
if (process.platform === 'darwin') {
|
||||
isHideDock ? app.dock?.hide() : setDockMenu()
|
||||
startMode !== ISartMode.NO_TRAY && createTray(tooltip)
|
||||
} else {
|
||||
createTray(tooltip)
|
||||
}
|
||||
db.set(configPaths.needReload, false)
|
||||
updateChecker()
|
||||
// 不需要阻塞
|
||||
process.nextTick(() => {
|
||||
shortKeyHandler.init()
|
||||
})
|
||||
server.startup()
|
||||
webServer.start()
|
||||
startFileServer()
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
handleStartUpFiles(process.argv, process.cwd())
|
||||
}
|
||||
|
||||
if (notificationList && notificationList.length > 0) {
|
||||
while (notificationList.length) {
|
||||
const option = notificationList.pop()
|
||||
const notice = new Notification(option!)
|
||||
notice.show()
|
||||
}
|
||||
}
|
||||
await remoteNoticeHandler.init()
|
||||
remoteNoticeHandler.triggerHook(IRemoteNoticeTriggerHook.APP_START)
|
||||
if (startMode === ISartMode.MINI && process.platform !== 'darwin') {
|
||||
windowManager.create(IWindowList.MINI_WINDOW)
|
||||
const miniWindow = windowManager.get(IWindowList.MINI_WINDOW)!
|
||||
miniWindow.removeAllListeners()
|
||||
if (db.get(configPaths.settings.miniWindowOntop)) {
|
||||
miniWindow.setAlwaysOnTop(true)
|
||||
}
|
||||
const { width, height } = screen.getPrimaryDisplay().workAreaSize
|
||||
const lastPosition = db.get(configPaths.settings.miniWindowPosition)
|
||||
if (lastPosition) {
|
||||
if (lastPosition[0] < 0 || lastPosition[0] > width || lastPosition[1] < 0 || lastPosition[1] > height) {
|
||||
miniWindow.setPosition(width - 100, height - 100)
|
||||
db.set(configPaths.settings.miniWindowPosition, [width - 100, height - 100])
|
||||
} else if (lastPosition[0] + miniWindow.getSize()[0] > width || lastPosition[1] + miniWindow.getSize()[1] > height) {
|
||||
miniWindow.setPosition(width - miniWindow.getSize()[0], height - miniWindow.getSize()[1])
|
||||
db.set(configPaths.settings.miniWindowPosition, [width - miniWindow.getSize()[0], height - miniWindow.getSize()[1]])
|
||||
} else {
|
||||
miniWindow.setPosition(lastPosition[0], lastPosition[1])
|
||||
}
|
||||
} else {
|
||||
miniWindow.setPosition(width - 100, height - 100)
|
||||
}
|
||||
const setPositionFunc = () => {
|
||||
const position = miniWindow.getPosition()
|
||||
db.set(configPaths.settings.miniWindowPosition, position)
|
||||
}
|
||||
miniWindow.on('close', setPositionFunc)
|
||||
miniWindow.on('move', setPositionFunc)
|
||||
miniWindow.show()
|
||||
miniWindow.focus()
|
||||
} else if (startMode === ISartMode.MAIN) {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
settingWindow.show()
|
||||
settingWindow.focus()
|
||||
}
|
||||
const clipboardDir = path.join(picgo.baseDir, CLIPBOARD_IMAGE_FOLDER)
|
||||
fs.emptyDir(clipboardDir)
|
||||
}
|
||||
app.whenReady().then(readyFunction)
|
||||
}
|
||||
|
||||
#onRunning () {
|
||||
app.on('second-instance', (_, commandLine, workingDirectory) => {
|
||||
logger.info('detect second instance')
|
||||
const result = handleStartUpFiles(commandLine, workingDirectory)
|
||||
if (!result) {
|
||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||
if (settingWindow.isMinimized()) {
|
||||
settingWindow.restore()
|
||||
}
|
||||
settingWindow.focus()
|
||||
}
|
||||
}
|
||||
})
|
||||
app.on('activate', () => {
|
||||
if (!windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||
}
|
||||
if (!windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||
windowManager.create(IWindowList.SETTING_WINDOW)
|
||||
}
|
||||
})
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: db.get(configPaths.settings.autoStart) || false
|
||||
})
|
||||
if (process.platform === 'win32') {
|
||||
app.setAppUserModelId('com.kuingsmile.piclist')
|
||||
}
|
||||
|
||||
if (process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.includes('Unity')) {
|
||||
process.env.XDG_CURRENT_DESKTOP = 'Unity'
|
||||
}
|
||||
}
|
||||
|
||||
#onQuit () {
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('will-quit', () => {
|
||||
UpDownTaskQueue.getInstance().persist()
|
||||
clearTempFolder()
|
||||
globalShortcut.unregisterAll()
|
||||
bus.removeAllListeners()
|
||||
server.shutdown()
|
||||
webServer.stop()
|
||||
stopFileServer()
|
||||
})
|
||||
// Exit cleanly on request from parent process in development mode.
|
||||
if (isDevelopment) {
|
||||
if (process.platform === 'win32') {
|
||||
process.on('message', data => {
|
||||
if (data === 'graceful-exit') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
process.on('SIGTERM', () => {
|
||||
app.quit()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async launchApp () {
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
await this.#beforeReady()
|
||||
this.#onReady()
|
||||
this.#onRunning()
|
||||
this.#onQuit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lifeCycle = new LifeCycle()
|
||||
|
||||
export { lifeCycle }
|
||||
|
||||
@@ -1,33 +1,75 @@
|
||||
import crypto from 'node:crypto'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import { configPaths } from '~/utils/configPaths'
|
||||
|
||||
export class AESHelper {
|
||||
private key: Buffer = crypto.pbkdf2Sync(
|
||||
picgo.getConfig<string>(configPaths.settings.aesPassword) || 'aesPassword',
|
||||
Buffer.from('a8b3c4d2e4f5098712345678feedc0de', 'hex'),
|
||||
100000,
|
||||
32,
|
||||
'sha512'
|
||||
)
|
||||
|
||||
encrypt (plainText: string) {
|
||||
const iv = crypto.randomBytes(16)
|
||||
const cipher = crypto.createCipheriv('aes-256-cbc', this.key, iv)
|
||||
let encrypted = cipher.update(plainText, 'utf8', 'hex')
|
||||
encrypted += cipher.final('hex')
|
||||
return `${iv.toString('hex')}:${encrypted}`
|
||||
}
|
||||
|
||||
decrypt (encryptedData: string) {
|
||||
const [ivHex, encryptedText] = encryptedData.split(':')
|
||||
if (!ivHex || !encryptedText) return '{}'
|
||||
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, Buffer.from(ivHex, 'hex'))
|
||||
let decrypted = decipher.update(encryptedText, 'hex', 'utf8')
|
||||
decrypted += decipher.final('utf8')
|
||||
return decrypted
|
||||
}
|
||||
}
|
||||
import crypto from 'node:crypto'
|
||||
|
||||
import picgo from '@core/picgo'
|
||||
|
||||
import { configPaths } from '~/utils/configPaths'
|
||||
|
||||
export class AESHelper {
|
||||
static readonly #SALT = Buffer.from('a8b3c4d2e4f5098712345678feedc0de', 'hex')
|
||||
static readonly #ITERATIONS = 100_000
|
||||
static readonly #KEYLEN = 32
|
||||
static readonly #DIGEST = 'sha512' as const
|
||||
static readonly #ALGO = 'aes-256-cbc'
|
||||
static readonly #IV_LENGTH = 16
|
||||
static readonly #SEP = ':'
|
||||
|
||||
static #keyCache = new Map<string, Buffer>()
|
||||
|
||||
readonly key: Buffer
|
||||
|
||||
constructor (password?: string) {
|
||||
const pwd =
|
||||
password ??
|
||||
picgo.getConfig<string>(configPaths.settings.aesPassword) ??
|
||||
'aesPassword'
|
||||
this.key = AESHelper.#deriveKey(pwd)
|
||||
}
|
||||
|
||||
static #deriveKey (password: string): Buffer {
|
||||
const cached = this.#keyCache.get(password)
|
||||
if (cached) return cached
|
||||
const key = crypto.pbkdf2Sync(
|
||||
password,
|
||||
this.#SALT,
|
||||
this.#ITERATIONS,
|
||||
this.#KEYLEN,
|
||||
this.#DIGEST
|
||||
)
|
||||
this.#keyCache.set(password, key)
|
||||
return key
|
||||
}
|
||||
|
||||
encrypt (plainText: string): string {
|
||||
const iv = crypto.randomBytes(AESHelper.#IV_LENGTH)
|
||||
const cipher = crypto.createCipheriv(AESHelper.#ALGO, this.key, iv)
|
||||
const encrypted = Buffer.concat([
|
||||
cipher.update(plainText, 'utf8'),
|
||||
cipher.final()
|
||||
])
|
||||
return `${iv.toString('hex')}${AESHelper.#SEP}${encrypted.toString('hex')}`
|
||||
}
|
||||
|
||||
decrypt (encryptedData: string): string {
|
||||
if (!encryptedData) return '{}'
|
||||
|
||||
const sepIndex = encryptedData.indexOf(AESHelper.#SEP)
|
||||
if (sepIndex <= 0) return '{}'
|
||||
|
||||
const ivHex = encryptedData.slice(0, sepIndex)
|
||||
const encryptedHex = encryptedData.slice(sepIndex + 1)
|
||||
|
||||
try {
|
||||
const iv = Buffer.from(ivHex, 'hex')
|
||||
if (iv.length !== AESHelper.#IV_LENGTH) return '{}'
|
||||
|
||||
const decipher = crypto.createDecipheriv(AESHelper.#ALGO, this.key, iv)
|
||||
const decrypted = Buffer.concat([
|
||||
decipher.update(Buffer.from(encryptedHex, 'hex')),
|
||||
decipher.final()
|
||||
])
|
||||
return decrypted.toString('utf8')
|
||||
} catch {
|
||||
return '{}'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
96
tests/aeshelper.test.ts
Normal file
96
tests/aeshelper.test.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
// AESHelper.spec.ts
|
||||
import crypto from 'node:crypto'
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { AESHelper } from '../src/main/utils/aesHelper'
|
||||
|
||||
vi.mock('@core/picgo', () => ({
|
||||
default: { getConfig: vi.fn(() => undefined) }
|
||||
}))
|
||||
vi.mock('~/utils/configPaths', () => ({
|
||||
configPaths: { settings: { aesPassword: 'settings.aesPassword' } }
|
||||
}))
|
||||
|
||||
const HEX_RE = /^[0-9a-f]+$/i
|
||||
|
||||
describe('AESHelper', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('round-trips (encrypt -> decrypt) with explicit password', () => {
|
||||
const helper = new AESHelper('testPass123')
|
||||
const plaintext = JSON.stringify({ hello: 'world', n: 42 })
|
||||
const enc = helper.encrypt(plaintext)
|
||||
const dec = helper.decrypt(enc)
|
||||
expect(dec).toBe(plaintext)
|
||||
})
|
||||
|
||||
it('produces iv:cipher hex format with 16-byte IV', () => {
|
||||
const helper = new AESHelper('formatPass')
|
||||
const enc = helper.encrypt('format-check')
|
||||
const parts = enc.split(':')
|
||||
expect(parts.length).toBe(2)
|
||||
const [ivHex, cipherHex] = parts
|
||||
expect(ivHex.length).toBe(32)
|
||||
expect(HEX_RE.test(ivHex)).toBe(true)
|
||||
expect(cipherHex.length).toBeGreaterThan(0)
|
||||
expect(HEX_RE.test(cipherHex)).toBe(true)
|
||||
})
|
||||
|
||||
it('is non-deterministic (random IV) for the same plaintext', () => {
|
||||
const helper = new AESHelper('randomIvPass')
|
||||
const pt = 'same-plaintext'
|
||||
const a = helper.encrypt(pt)
|
||||
const b = helper.encrypt(pt)
|
||||
expect(a).not.toBe(b)
|
||||
expect(helper.decrypt(a)).toBe(pt)
|
||||
expect(helper.decrypt(b)).toBe(pt)
|
||||
})
|
||||
|
||||
it('returns "{}" on malformed inputs (compatibility behavior)', () => {
|
||||
const helper = new AESHelper('badInputPass')
|
||||
expect(helper.decrypt('')).toBe('{}') // empty
|
||||
expect(helper.decrypt('nocolonhere')).toBe('{}') // no separator
|
||||
expect(helper.decrypt('00:abcd')).toBe('{}') // IV too short
|
||||
})
|
||||
|
||||
it('returns "{}" if ciphertext is tampered', () => {
|
||||
const helper = new AESHelper('tamperPass')
|
||||
const enc = helper.encrypt('secure-data')
|
||||
const [ivHex, cipherHex] = enc.split(':')
|
||||
const last = cipherHex.at(-1)!
|
||||
const flipped = last.toLowerCase() === 'a' ? 'b' : 'a'
|
||||
const tampered = `${ivHex}:${cipherHex.slice(0, -1)}${flipped}`
|
||||
expect(helper.decrypt(tampered)).toBe('{}')
|
||||
})
|
||||
|
||||
it('two instances with the same password can decrypt each other', () => {
|
||||
const h1 = new AESHelper('sharedPass')
|
||||
const h2 = new AESHelper('sharedPass')
|
||||
const enc1 = h1.encrypt('hello')
|
||||
const enc2 = h2.encrypt('world')
|
||||
expect(h2.decrypt(enc1)).toBe('hello')
|
||||
expect(h1.decrypt(enc2)).toBe('world')
|
||||
})
|
||||
|
||||
it('works with default constructor (uses picgo fallback password)', async () => {
|
||||
const h1 = new AESHelper()
|
||||
const h2 = new AESHelper()
|
||||
const enc = h1.encrypt('fallback-ok')
|
||||
expect(h2.decrypt(enc)).toBe('fallback-ok')
|
||||
})
|
||||
|
||||
it('caches derived keys for the same password (pbkdf2Sync called once)', () => {
|
||||
const spy = vi.spyOn(crypto, 'pbkdf2Sync')
|
||||
const uniquePwd = 'cache-me-please-' + Math.random().toString(36).slice(2)
|
||||
|
||||
const a = new AESHelper(uniquePwd)
|
||||
const b = new AESHelper(uniquePwd)
|
||||
a.encrypt('x'); b.encrypt('y')
|
||||
|
||||
const callsForPwd = spy.mock.calls.filter(args => args[0] === uniquePwd)
|
||||
expect(callsForPwd.length).toBe(1)
|
||||
})
|
||||
})
|
||||
242
yarn.lock
242
yarn.lock
@@ -3807,6 +3807,13 @@
|
||||
"@types/node" "*"
|
||||
"@types/responselike" "^1.0.0"
|
||||
|
||||
"@types/chai@^5.2.2":
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-5.2.2.tgz#6f14cea18180ffc4416bc0fd12be05fdd73bdd6b"
|
||||
integrity sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==
|
||||
dependencies:
|
||||
"@types/deep-eql" "*"
|
||||
|
||||
"@types/connect@*":
|
||||
version "3.4.35"
|
||||
resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
|
||||
@@ -3828,6 +3835,11 @@
|
||||
dependencies:
|
||||
"@types/ms" "*"
|
||||
|
||||
"@types/deep-eql@*":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd"
|
||||
integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==
|
||||
|
||||
"@types/estree@1.0.8", "@types/estree@^1.0.0", "@types/estree@^1.0.6":
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
|
||||
@@ -4448,6 +4460,67 @@
|
||||
dependencies:
|
||||
"@rolldown/pluginutils" "1.0.0-beta.29"
|
||||
|
||||
"@vitest/expect@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-3.2.4.tgz#8362124cd811a5ee11c5768207b9df53d34f2433"
|
||||
integrity sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==
|
||||
dependencies:
|
||||
"@types/chai" "^5.2.2"
|
||||
"@vitest/spy" "3.2.4"
|
||||
"@vitest/utils" "3.2.4"
|
||||
chai "^5.2.0"
|
||||
tinyrainbow "^2.0.0"
|
||||
|
||||
"@vitest/mocker@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-3.2.4.tgz#4471c4efbd62db0d4fa203e65cc6b058a85cabd3"
|
||||
integrity sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==
|
||||
dependencies:
|
||||
"@vitest/spy" "3.2.4"
|
||||
estree-walker "^3.0.3"
|
||||
magic-string "^0.30.17"
|
||||
|
||||
"@vitest/pretty-format@3.2.4", "@vitest/pretty-format@^3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-3.2.4.tgz#3c102f79e82b204a26c7a5921bf47d534919d3b4"
|
||||
integrity sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==
|
||||
dependencies:
|
||||
tinyrainbow "^2.0.0"
|
||||
|
||||
"@vitest/runner@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-3.2.4.tgz#5ce0274f24a971f6500f6fc166d53d8382430766"
|
||||
integrity sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==
|
||||
dependencies:
|
||||
"@vitest/utils" "3.2.4"
|
||||
pathe "^2.0.3"
|
||||
strip-literal "^3.0.0"
|
||||
|
||||
"@vitest/snapshot@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-3.2.4.tgz#40a8bc0346ac0aee923c0eefc2dc005d90bc987c"
|
||||
integrity sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==
|
||||
dependencies:
|
||||
"@vitest/pretty-format" "3.2.4"
|
||||
magic-string "^0.30.17"
|
||||
pathe "^2.0.3"
|
||||
|
||||
"@vitest/spy@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-3.2.4.tgz#cc18f26f40f3f028da6620046881f4e4518c2599"
|
||||
integrity sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==
|
||||
dependencies:
|
||||
tinyspy "^4.0.3"
|
||||
|
||||
"@vitest/utils@3.2.4":
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-3.2.4.tgz#c0813bc42d99527fb8c5b138c7a88516bca46fea"
|
||||
integrity sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==
|
||||
dependencies:
|
||||
"@vitest/pretty-format" "3.2.4"
|
||||
loupe "^3.1.4"
|
||||
tinyrainbow "^2.0.0"
|
||||
|
||||
"@volar/language-core@2.4.22":
|
||||
version "2.4.22"
|
||||
resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-2.4.22.tgz#a980a18c4d3e550b55a8e389a9f590debd815810"
|
||||
@@ -4985,6 +5058,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
|
||||
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
|
||||
|
||||
assertion-error@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
|
||||
integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==
|
||||
|
||||
ast-types@^0.13.4:
|
||||
version "0.13.4"
|
||||
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782"
|
||||
@@ -5394,6 +5472,17 @@ caw@^2.0.1:
|
||||
tunnel-agent "^0.6.0"
|
||||
url-to-options "^1.0.1"
|
||||
|
||||
chai@^5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-5.2.1.tgz#a9502462bdc79cf90b4a0953537a9908aa638b47"
|
||||
integrity sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==
|
||||
dependencies:
|
||||
assertion-error "^2.0.1"
|
||||
check-error "^2.1.1"
|
||||
deep-eql "^5.0.1"
|
||||
loupe "^3.1.0"
|
||||
pathval "^2.0.0"
|
||||
|
||||
chalk@5.4.1, chalk@^5.3.0, chalk@^5.4.1:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.4.1.tgz#1b48bf0963ec158dce2aacf69c093ae2dd2092d8"
|
||||
@@ -5431,6 +5520,11 @@ charenc@0.0.2:
|
||||
resolved "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
||||
integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==
|
||||
|
||||
check-error@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-2.1.1.tgz#87eb876ae71ee388fa0471fe423f494be1d96ccc"
|
||||
integrity sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==
|
||||
|
||||
chownr@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
|
||||
@@ -6105,6 +6199,11 @@ dedent@0.7.0:
|
||||
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
|
||||
integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==
|
||||
|
||||
deep-eql@^5.0.1:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341"
|
||||
integrity sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==
|
||||
|
||||
deep-is@^0.1.3:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
|
||||
@@ -6534,6 +6633,11 @@ es-errors@^1.3.0:
|
||||
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||
|
||||
es-module-lexer@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a"
|
||||
integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==
|
||||
|
||||
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
|
||||
@@ -6868,6 +6972,13 @@ estree-walker@^2.0.2:
|
||||
resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
||||
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||
|
||||
estree-walker@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
|
||||
integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==
|
||||
dependencies:
|
||||
"@types/estree" "^1.0.0"
|
||||
|
||||
esutils@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||
@@ -6900,6 +7011,11 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
|
||||
dependencies:
|
||||
homedir-polyfill "^1.0.1"
|
||||
|
||||
expect-type@^1.2.1:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.2.tgz#c030a329fb61184126c8447585bc75a7ec6fbff3"
|
||||
integrity sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==
|
||||
|
||||
exponential-backoff@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
|
||||
@@ -8523,6 +8639,11 @@ js-tokens@^4.0.0:
|
||||
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
js-tokens@^9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4"
|
||||
integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==
|
||||
|
||||
js-yaml@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
@@ -8830,6 +8951,11 @@ longest@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/longest/-/longest-2.0.1.tgz#781e183296aa94f6d4d916dc335d0d17aefa23f8"
|
||||
integrity sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==
|
||||
|
||||
loupe@^3.1.0, loupe@^3.1.4:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/loupe/-/loupe-3.2.0.tgz#174073ba8e0a1d0d5e43cc08626ed8a19403c344"
|
||||
integrity sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==
|
||||
|
||||
lowdb@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-7.0.1.tgz#7354a684547d76206b1c730b9434604235b125e5"
|
||||
@@ -9948,11 +10074,16 @@ pathe@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"
|
||||
integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
|
||||
|
||||
pathe@^2.0.1:
|
||||
pathe@^2.0.1, pathe@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716"
|
||||
integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==
|
||||
|
||||
pathval@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.1.tgz#8855c5a2899af072d6ac05d11e46045ad0dc605d"
|
||||
integrity sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==
|
||||
|
||||
pause-stream@~0.0.11:
|
||||
version "0.0.11"
|
||||
resolved "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
|
||||
@@ -10846,6 +10977,11 @@ side-channel@^1.0.4:
|
||||
get-intrinsic "^1.0.2"
|
||||
object-inspect "^1.9.0"
|
||||
|
||||
siginfo@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
|
||||
integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==
|
||||
|
||||
signal-exit@^3.0.2:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af"
|
||||
@@ -11055,6 +11191,11 @@ stable-hash@^0.0.5:
|
||||
resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.5.tgz#94e8837aaeac5b4d0f631d2972adef2924b40269"
|
||||
integrity sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==
|
||||
|
||||
stackback@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
|
||||
integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==
|
||||
|
||||
stat-mode@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz#68b55cb61ea639ff57136f36b216a291800d1465"
|
||||
@@ -11065,6 +11206,11 @@ statuses@^1.3.1:
|
||||
resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
|
||||
|
||||
std-env@^3.9.0:
|
||||
version "3.9.0"
|
||||
resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.9.0.tgz#1a6f7243b339dca4c9fd55e1c7504c77ef23e8f1"
|
||||
integrity sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==
|
||||
|
||||
stdin-discarder@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.2.2.tgz#390037f44c4ae1a1ae535c5fe38dc3aba8d997be"
|
||||
@@ -11236,6 +11382,13 @@ strip-json-comments@3.1.1, strip-json-comments@^3.1.1:
|
||||
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
strip-literal@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-3.0.0.tgz#ce9c452a91a0af2876ed1ae4e583539a353df3fc"
|
||||
integrity sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==
|
||||
dependencies:
|
||||
js-tokens "^9.0.1"
|
||||
|
||||
strip-outer@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631"
|
||||
@@ -11428,6 +11581,16 @@ tiny-typed-emitter@^2.1.0:
|
||||
resolved "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz#b3b027fdd389ff81a152c8e847ee2f5be9fad7b5"
|
||||
integrity sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==
|
||||
|
||||
tinybench@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
|
||||
integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==
|
||||
|
||||
tinyexec@^0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2"
|
||||
integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==
|
||||
|
||||
tinyexec@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.0.1.tgz#70c31ab7abbb4aea0a24f55d120e5990bfa1e0b1"
|
||||
@@ -11441,6 +11604,21 @@ tinyglobby@^0.2.14:
|
||||
fdir "^6.4.4"
|
||||
picomatch "^4.0.2"
|
||||
|
||||
tinypool@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.1.1.tgz#059f2d042bd37567fbc017d3d426bdd2a2612591"
|
||||
integrity sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==
|
||||
|
||||
tinyrainbow@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tinyrainbow/-/tinyrainbow-2.0.0.tgz#9509b2162436315e80e3eee0fcce4474d2444294"
|
||||
integrity sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==
|
||||
|
||||
tinyspy@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-4.0.3.tgz#d1d0f0602f4c15f1aae083a34d6d0df3363b1b52"
|
||||
integrity sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==
|
||||
|
||||
tmp-promise@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7"
|
||||
@@ -12012,6 +12190,31 @@ videojs-vtt.js@0.15.5:
|
||||
dependencies:
|
||||
global "^4.3.1"
|
||||
|
||||
vite-node@3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.2.4.tgz#f3676d94c4af1e76898c162c92728bca65f7bb07"
|
||||
integrity sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==
|
||||
dependencies:
|
||||
cac "^6.7.14"
|
||||
debug "^4.4.1"
|
||||
es-module-lexer "^1.7.0"
|
||||
pathe "^2.0.3"
|
||||
vite "^5.0.0 || ^6.0.0 || ^7.0.0-0"
|
||||
|
||||
"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0":
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.1.tgz#a2c25f31742fd9f8b223d809df1ce3026e813fcf"
|
||||
integrity sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ==
|
||||
dependencies:
|
||||
esbuild "^0.25.0"
|
||||
fdir "^6.4.6"
|
||||
picomatch "^4.0.3"
|
||||
postcss "^8.5.6"
|
||||
rollup "^4.43.0"
|
||||
tinyglobby "^0.2.14"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
vite@^7.1.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-7.1.0.tgz#6fb13c74c13cfdd0e200ee61d6ea6e8fafc2e8b5"
|
||||
@@ -12026,6 +12229,35 @@ vite@^7.1.0:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
vitest@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/vitest/-/vitest-3.2.4.tgz#0637b903ad79d1539a25bc34c0ed54b5c67702ea"
|
||||
integrity sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==
|
||||
dependencies:
|
||||
"@types/chai" "^5.2.2"
|
||||
"@vitest/expect" "3.2.4"
|
||||
"@vitest/mocker" "3.2.4"
|
||||
"@vitest/pretty-format" "^3.2.4"
|
||||
"@vitest/runner" "3.2.4"
|
||||
"@vitest/snapshot" "3.2.4"
|
||||
"@vitest/spy" "3.2.4"
|
||||
"@vitest/utils" "3.2.4"
|
||||
chai "^5.2.0"
|
||||
debug "^4.4.1"
|
||||
expect-type "^1.2.1"
|
||||
magic-string "^0.30.17"
|
||||
pathe "^2.0.3"
|
||||
picomatch "^4.0.2"
|
||||
std-env "^3.9.0"
|
||||
tinybench "^2.9.0"
|
||||
tinyexec "^0.3.2"
|
||||
tinyglobby "^0.2.14"
|
||||
tinypool "^1.1.1"
|
||||
tinyrainbow "^2.0.0"
|
||||
vite "^5.0.0 || ^6.0.0 || ^7.0.0-0"
|
||||
vite-node "3.2.4"
|
||||
why-is-node-running "^2.3.0"
|
||||
|
||||
vscode-uri@^3.0.8:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c"
|
||||
@@ -12155,6 +12387,14 @@ which@^2.0.1, which@^2.0.2:
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
why-is-node-running@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
|
||||
integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==
|
||||
dependencies:
|
||||
siginfo "^2.0.0"
|
||||
stackback "0.0.2"
|
||||
|
||||
win-release@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz#5fa55e02be7ca934edfc12665632e849b72e5209"
|
||||
|
||||
Reference in New Issue
Block a user