feat: add sqlite database manager

This commit is contained in:
kangfenmao 2024-09-10 19:36:11 +08:00
parent 76d1f0bb1e
commit a03d619e2f
13 changed files with 502 additions and 48 deletions

View File

@ -31,6 +31,7 @@
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"better-sqlite3": "^11.3.0",
"electron-log": "^5.1.5",
"electron-store": "^8.2.0",
"electron-updater": "^6.1.7",

View File

@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS files (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
file_name TEXT NOT NULL,
path TEXT NOT NULL,
size INTEGER NOT NULL,
ext TEXT NOT NULL,
created_at TEXT NOT NULL
)

View File

@ -0,0 +1,95 @@
import Database from 'better-sqlite3'
import { app } from 'electron'
import Logger from 'electron-log'
import * as fs from 'fs'
import * as path from 'path'
interface Migration {
id: number
name: string
sql: string
}
export class DatabaseMigrator {
private storageDir: string
private db: Database.Database
private migrationsDir: string
constructor(migrationsDir: string) {
this.storageDir = path.join(app.getPath('userData'), 'Data')
this.migrationsDir = migrationsDir
this.initStorageDir()
this.initDatabase()
this.initMigrationsTable()
}
private initStorageDir(): void {
if (!fs.existsSync(this.storageDir)) {
fs.mkdirSync(this.storageDir, { recursive: true })
}
}
private initDatabase(): void {
const dbPath = path.join(this.storageDir, 'data.db')
this.db = new Database(dbPath)
}
private initMigrationsTable(): void {
this.db.exec(`
CREATE TABLE IF NOT EXISTS migrations (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`)
}
private getAppliedMigrations(): number[] {
const stmt = this.db.prepare('SELECT id FROM migrations ORDER BY id')
return stmt.all().map((row: any) => row.id)
}
private loadMigrations(): Migration[] {
const files = fs.readdirSync(this.migrationsDir).filter((file) => file.endsWith('.sql'))
return files
.map((file) => {
const [id, ...nameParts] = path.basename(file, '.sql').split('_')
return {
id: parseInt(id),
name: nameParts.join('_'),
sql: fs.readFileSync(path.join(this.migrationsDir, file), 'utf-8')
}
})
.sort((a, b) => a.id - b.id)
}
public async migrate(): Promise<void> {
const appliedMigrations = this.getAppliedMigrations()
const allMigrations = this.loadMigrations()
const pendingMigrations = allMigrations.filter((migration) => !appliedMigrations.includes(migration.id))
this.db.exec('BEGIN TRANSACTION')
try {
for (const migration of pendingMigrations) {
Logger.log(`Applying migration: ${migration.id}_${migration.name}`)
this.db.exec(migration.sql)
const insertStmt = this.db.prepare('INSERT INTO migrations (id, name) VALUES (?, ?)')
insertStmt.run(migration.id, migration.name)
}
this.db.exec('COMMIT')
Logger.log('All migrations applied successfully')
} catch (error) {
this.db.exec('ROLLBACK')
Logger.error('Error applying migrations:', error)
throw error
}
}
public close(): void {
this.db.close()
}
}

View File

@ -1,3 +1,4 @@
import Database from 'better-sqlite3'
import * as crypto from 'crypto'
import { app, dialog, OpenDialogOptions } from 'electron'
import * as fs from 'fs'
@ -7,19 +8,21 @@ import { v4 as uuidv4 } from 'uuid'
interface FileMetadata {
id: string
name: string
fileName: string
file_name: string
path: string
size: number
ext: string
createdAt: Date
created_at: Date
}
export class File {
private storageDir: string
private db: Database.Database
constructor() {
this.storageDir = path.join(app.getPath('userData'), 'Data', 'Files')
this.initStorageDir()
this.initDatabase()
}
private initStorageDir(): void {
@ -28,6 +31,11 @@ export class File {
}
}
private initDatabase(): void {
const dbPath = path.join(app.getPath('userData'), 'Data', 'data.db')
this.db = new Database(dbPath)
}
private async getFileHash(filePath: string): Promise<string> {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('md5')
@ -55,15 +63,8 @@ export class File {
if (originalHash === storedHash) {
const ext = path.extname(file)
return {
id: path.basename(file, ext),
name: path.basename(filePath),
fileName: file,
path: storedFilePath,
createdAt: storedStats.birthtime,
size: storedStats.size,
ext: ext
}
const id = path.basename(file, ext)
return this.getFile(id)
}
}
}
@ -91,9 +92,9 @@ export class File {
return {
id: uuidv4(),
name: path.basename(filePath),
fileName: path.basename(filePath),
file_name: path.basename(filePath),
path: filePath,
createdAt: stats.birthtime,
created_at: stats.birthtime,
size: stats.size,
ext: ext
}
@ -117,20 +118,41 @@ export class File {
await fs.promises.copyFile(filePath, destPath)
const stats = await fs.promises.stat(destPath)
return {
const fileMetadata: FileMetadata = {
id: uuid,
name,
fileName: uuid + ext,
file_name: uuid + ext,
path: destPath,
createdAt: stats.birthtime,
created_at: stats.birthtime,
size: stats.size,
ext: ext
}
const stmt = this.db.prepare(`
INSERT INTO files (id, name, file_name, path, size, ext, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?)
`)
stmt.run(
fileMetadata.id,
fileMetadata.name,
fileMetadata.file_name,
fileMetadata.path,
fileMetadata.size,
fileMetadata.ext,
fileMetadata.created_at.toISOString()
)
return fileMetadata
}
async deleteFile(fileId: string): Promise<void> {
const filePath = path.join(this.storageDir, fileId)
await fs.promises.unlink(filePath)
const fileMetadata = this.getFile(fileId)
if (fileMetadata) {
await fs.promises.unlink(fileMetadata.path)
const stmt = this.db.prepare('DELETE FROM files WHERE id = ?')
stmt.run(fileId)
}
}
async batchUploadFiles(filePaths: string[]): Promise<FileMetadata[]> {
@ -142,4 +164,25 @@ export class File {
const deletePromises = fileIds.map((fileId) => this.deleteFile(fileId))
await Promise.all(deletePromises)
}
getFile(id: string): FileMetadata | null {
const stmt = this.db.prepare('SELECT * FROM files WHERE id = ?')
const row = stmt.get(id) as any
if (row) {
return {
...row,
created_at: new Date(row.created_at)
}
}
return null
}
getAllFiles(): FileMetadata[] {
const stmt = this.db.prepare('SELECT * FROM files')
const rows = stmt.all() as any[]
return rows.map((row) => ({
...row,
created_at: new Date(row.created_at)
}))
}
}

View File

@ -1,15 +1,30 @@
import { electronApp, optimizer } from '@electron-toolkit/utils'
import { app, BrowserWindow } from 'electron'
import Logger from 'electron-log'
import path from 'path'
import { DatabaseMigrator } from './db/DatabaseMigrator'
import { registerIpc } from './ipc'
import { getResourcePath } from './utils'
import { updateUserDataPath } from './utils/upgrade'
import { createMainWindow } from './window'
async function migrateDatabase() {
const migrationsDir = path.join(getResourcePath(), 'migrations')
const migrator = new DatabaseMigrator(migrationsDir)
await migrator.migrate()
migrator.close()
Logger.log('Database migration completed successfully.')
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(async () => {
await updateUserDataPath()
await migrateDatabase()
// Set app user model id for windows
electronApp.setAppUserModelId('com.kangfenmao.CherryStudio')

View File

@ -34,27 +34,18 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
ipcMain.handle('zip:compress', (_, text: string) => compress(text))
ipcMain.handle('zip:decompress', (_, text: Buffer) => decompress(text))
ipcMain.handle('file:select', async (_, options?: OpenDialogOptions) => {
return await fileManager.selectFile(options)
})
ipcMain.handle('file:upload', async (_, filePath: string) => {
return await fileManager.uploadFile(filePath)
})
ipcMain.handle('file:select', async (_, options?: OpenDialogOptions) => await fileManager.selectFile(options))
ipcMain.handle('file:upload', async (_, filePath: string) => await fileManager.uploadFile(filePath))
ipcMain.handle('file:delete', async (_, fileId: string) => {
await fileManager.deleteFile(fileId)
return { success: true }
})
ipcMain.handle('file:batchUpload', async (_, filePaths: string[]) => {
return await fileManager.batchUploadFiles(filePaths)
})
ipcMain.handle('file:batchUpload', async (_, filePaths: string[]) => await fileManager.batchUploadFiles(filePaths))
ipcMain.handle('file:batchDelete', async (_, fileIds: string[]) => {
await fileManager.batchDeleteFiles(fileIds)
return { success: true }
})
ipcMain.handle('file:getAll', () => fileManager.getAllFiles())
ipcMain.handle('minapp', (_, args) => {
createMinappWindow({

7
src/main/utils/index.ts Normal file
View File

@ -0,0 +1,7 @@
import path from 'node:path'
import { app } from 'electron'
export function getResourcePath() {
return path.join(app.getAppPath(), 'resources')
}

View File

@ -27,6 +27,7 @@ declare global {
fileDelete: (fileId: string) => Promise<{ success: boolean }>
fileBatchUpload: (filePaths: string[]) => Promise<FileMetadata[]>
fileBatchDelete: (fileIds: string[]) => Promise<{ success: boolean }>
fileGetAll: () => Promise<FileMetadata[]>
}
}
}

View File

@ -20,7 +20,8 @@ const api = {
fileUpload: (filePath: string) => ipcRenderer.invoke('file:upload', filePath),
fileDelete: (fileId: string) => ipcRenderer.invoke('file:delete', fileId),
fileBatchUpload: (filePaths: string[]) => ipcRenderer.invoke('file:batchUpload', filePaths),
fileBatchDelete: (fileIds: string[]) => ipcRenderer.invoke('file:batchDelete', fileIds)
fileBatchDelete: (fileIds: string[]) => ipcRenderer.invoke('file:batchDelete', fileIds),
fileGetAll: () => ipcRenderer.invoke('file:getAll')
}
// Use `contextBridge` APIs to expose Electron APIs to

View File

@ -5,7 +5,7 @@
<meta name="viewport" content="initial-scale=1, width=device-width" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; connect-src *; script-src 'self' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: *; frame-src * file:" />
content="default-src 'self'; connect-src *; script-src 'self' *; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' *; font-src 'self' data: *; img-src 'self' data: file: *; frame-src * file:" />
</head>
<body>
<div id="root"></div>

View File

@ -1,21 +1,60 @@
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
import { Button } from 'antd'
import { FC } from 'react'
import { VStack } from '@renderer/components/Layout'
import { FileMetadata } from '@renderer/types'
import { Image, Table } from 'antd'
import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
const FilesPage: FC = () => {
const { t } = useTranslation()
const [files, setFiles] = useState<FileMetadata[]>([])
const handleSelectFile = async () => {
const files = await window.api.fileSelect({
properties: ['openFile', 'multiSelections']
})
for (const file of files || []) {
const result = await window.api.fileUpload(file.path)
console.log('Selected file:', file, result)
}
useEffect(() => {
window.api.fileGetAll().then(setFiles)
}, [])
const dataSource = files.map((file) => ({
file: <Image src={'file://' + file.path} preview={false} style={{ maxHeight: '40px' }} />,
name: <a href={'file://' + file.path}>{file.name}</a>,
size: `${(file.size / 1024 / 1024).toFixed(2)} MB`,
created_at: file.created_at.toISOString().split('T')[0]
}))
const columns = [
{
title: 'File',
dataIndex: 'file',
key: 'file'
},
{
title: 'Name',
dataIndex: 'name',
key: 'name'
},
{
title: 'Size',
dataIndex: 'size',
key: 'size',
width: '100px'
},
{
title: 'Created At',
dataIndex: 'created_at',
key: 'created_at',
width: '120px'
}
]
// const handleSelectFile = async () => {
// const files = await window.api.fileSelect({
// properties: ['openFile', 'multiSelections']
// })
// for (const file of files || []) {
// const result = await window.api.fileUpload(file.path)
// console.log('Selected file:', file, result)
// }
// }
return (
<Container>
@ -23,7 +62,9 @@ const FilesPage: FC = () => {
<NavbarCenter style={{ borderRight: 'none' }}>{t('files.title')}</NavbarCenter>
</Navbar>
<ContentContainer>
<Button onClick={handleSelectFile}></Button>
<VStack style={{ flex: 1 }}>
<Table dataSource={dataSource} columns={columns} style={{ width: '100%', height: '100%' }} />
</VStack>
</ContentContainer>
</Container>
)

View File

@ -86,3 +86,13 @@ export type MinAppType = {
logo: string
url: string
}
export interface FileMetadata {
id: string
name: string
file_name: string
path: string
size: number
ext: string
created_at: Date
}

248
yarn.lock
View File

@ -1922,6 +1922,7 @@ __metadata:
"@vitejs/plugin-react": "npm:^4.2.1"
antd: "npm:^5.18.3"
axios: "npm:^1.7.3"
better-sqlite3: "npm:^11.3.0"
browser-image-compression: "npm:^2.0.2"
dayjs: "npm:^1.11.11"
dotenv-cli: "npm:^7.4.2"
@ -2462,6 +2463,17 @@ __metadata:
languageName: node
linkType: hard
"better-sqlite3@npm:^11.3.0":
version: 11.3.0
resolution: "better-sqlite3@npm:11.3.0"
dependencies:
bindings: "npm:^1.5.0"
node-gyp: "npm:latest"
prebuild-install: "npm:^7.1.1"
checksum: 10c0/9adc99683300699581da5d7288e4a261b7d4381fd99c762fc6a0e9b1e1e226009c1333b46b10c1c453c356b20cb8be037a4616b1e717b3d1a00bd8493bec506e
languageName: node
linkType: hard
"binary-extensions@npm:^2.0.0":
version: 2.3.0
resolution: "binary-extensions@npm:2.3.0"
@ -2469,6 +2481,26 @@ __metadata:
languageName: node
linkType: hard
"bindings@npm:^1.5.0":
version: 1.5.0
resolution: "bindings@npm:1.5.0"
dependencies:
file-uri-to-path: "npm:1.0.0"
checksum: 10c0/3dab2491b4bb24124252a91e656803eac24292473e56554e35bbfe3cc1875332cfa77600c3bac7564049dc95075bf6fcc63a4609920ff2d64d0fe405fcf0d4ba
languageName: node
linkType: hard
"bl@npm:^4.0.3":
version: 4.1.0
resolution: "bl@npm:4.1.0"
dependencies:
buffer: "npm:^5.5.0"
inherits: "npm:^2.0.4"
readable-stream: "npm:^3.4.0"
checksum: 10c0/02847e1d2cb089c9dc6958add42e3cdeaf07d13f575973963335ac0fdece563a50ac770ac4c8fa06492d2dd276f6cc3b7f08c7cd9c7a7ad0f8d388b2a28def5f
languageName: node
linkType: hard
"bluebird-lst@npm:^1.0.9":
version: 1.0.9
resolution: "bluebird-lst@npm:1.0.9"
@ -2564,7 +2596,7 @@ __metadata:
languageName: node
linkType: hard
"buffer@npm:^5.1.0":
"buffer@npm:^5.1.0, buffer@npm:^5.5.0":
version: 5.7.1
resolution: "buffer@npm:5.7.1"
dependencies:
@ -2797,6 +2829,13 @@ __metadata:
languageName: node
linkType: hard
"chownr@npm:^1.1.1":
version: 1.1.4
resolution: "chownr@npm:1.1.4"
checksum: 10c0/ed57952a84cc0c802af900cf7136de643d3aba2eecb59d29344bc2f3f9bf703a301b9d84cdc71f82c3ffc9ccde831b0d92f5b45f91727d6c9da62f23aef9d9db
languageName: node
linkType: hard
"chownr@npm:^2.0.0":
version: 2.0.0
resolution: "chownr@npm:2.0.0"
@ -3155,6 +3194,13 @@ __metadata:
languageName: node
linkType: hard
"deep-extend@npm:^0.6.0":
version: 0.6.0
resolution: "deep-extend@npm:0.6.0"
checksum: 10c0/1c6b0abcdb901e13a44c7d699116d3d4279fdb261983122a3783e7273844d5f2537dc2e1c454a23fcf645917f93fbf8d07101c1d03c015a87faa662755212566
languageName: node
linkType: hard
"deep-is@npm:^0.1.3":
version: 0.1.4
resolution: "deep-is@npm:0.1.4"
@ -3205,6 +3251,13 @@ __metadata:
languageName: node
linkType: hard
"detect-libc@npm:^2.0.0":
version: 2.0.3
resolution: "detect-libc@npm:2.0.3"
checksum: 10c0/88095bda8f90220c95f162bf92cad70bd0e424913e655c20578600e35b91edc261af27531cf160a331e185c0ced93944bc7e09939143225f56312d7fd800fdb7
languageName: node
linkType: hard
"detect-node@npm:^2.0.4":
version: 2.1.0
resolution: "detect-node@npm:2.1.0"
@ -3522,7 +3575,7 @@ __metadata:
languageName: node
linkType: hard
"end-of-stream@npm:^1.1.0":
"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1":
version: 1.4.4
resolution: "end-of-stream@npm:1.4.4"
dependencies:
@ -4100,6 +4153,13 @@ __metadata:
languageName: node
linkType: hard
"expand-template@npm:^2.0.3":
version: 2.0.3
resolution: "expand-template@npm:2.0.3"
checksum: 10c0/1c9e7afe9acadf9d373301d27f6a47b34e89b3391b1ef38b7471d381812537ef2457e620ae7f819d2642ce9c43b189b3583813ec395e2938319abe356a9b2f51
languageName: node
linkType: hard
"exponential-backoff@npm:^3.1.1":
version: 3.1.1
resolution: "exponential-backoff@npm:3.1.1"
@ -4222,6 +4282,13 @@ __metadata:
languageName: node
linkType: hard
"file-uri-to-path@npm:1.0.0":
version: 1.0.0
resolution: "file-uri-to-path@npm:1.0.0"
checksum: 10c0/3b545e3a341d322d368e880e1c204ef55f1d45cdea65f7efc6c6ce9e0c4d22d802d5629320eb779d006fe59624ac17b0e848d83cc5af7cd101f206cb704f5519
languageName: node
linkType: hard
"filelist@npm:^1.0.4":
version: 1.0.4
resolution: "filelist@npm:1.0.4"
@ -4341,6 +4408,13 @@ __metadata:
languageName: node
linkType: hard
"fs-constants@npm:^1.0.0":
version: 1.0.0
resolution: "fs-constants@npm:1.0.0"
checksum: 10c0/a0cde99085f0872f4d244e83e03a46aa387b74f5a5af750896c6b05e9077fac00e9932fdf5aef84f2f16634cd473c63037d7a512576da7d5c2b9163d1909f3a8
languageName: node
linkType: hard
"fs-extra@npm:^10.0.0, fs-extra@npm:^10.1.0":
version: 10.1.0
resolution: "fs-extra@npm:10.1.0"
@ -4492,6 +4566,13 @@ __metadata:
languageName: node
linkType: hard
"github-from-package@npm:0.0.0":
version: 0.0.0
resolution: "github-from-package@npm:0.0.0"
checksum: 10c0/737ee3f52d0a27e26332cde85b533c21fcdc0b09fb716c3f8e522cfaa9c600d4a631dec9fcde179ec9d47cca89017b7848ed4d6ae6b6b78f936c06825b1fcc12
languageName: node
linkType: hard
"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2":
version: 5.1.2
resolution: "glob-parent@npm:5.1.2"
@ -5077,13 +5158,20 @@ __metadata:
languageName: node
linkType: hard
"inherits@npm:2":
"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:^2.0.4":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
languageName: node
linkType: hard
"ini@npm:~1.3.0":
version: 1.3.8
resolution: "ini@npm:1.3.8"
checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a
languageName: node
linkType: hard
"inline-style-parser@npm:0.2.3":
version: 0.2.3
resolution: "inline-style-parser@npm:0.2.3"
@ -6577,7 +6665,7 @@ __metadata:
languageName: node
linkType: hard
"minimist@npm:^1.2.6":
"minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.6":
version: 1.2.8
resolution: "minimist@npm:1.2.8"
checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6
@ -6675,6 +6763,13 @@ __metadata:
languageName: node
linkType: hard
"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3":
version: 0.5.3
resolution: "mkdirp-classic@npm:0.5.3"
checksum: 10c0/95371d831d196960ddc3833cc6907e6b8f67ac5501a6582f47dfae5eb0f092e9f8ce88e0d83afcae95d6e2b61a01741ba03714eeafb6f7a6e9dcc158ac85b168
languageName: node
linkType: hard
"mkdirp@npm:^0.5.1":
version: 0.5.6
resolution: "mkdirp@npm:0.5.6"
@ -6718,6 +6813,13 @@ __metadata:
languageName: node
linkType: hard
"napi-build-utils@npm:^1.0.1":
version: 1.0.2
resolution: "napi-build-utils@npm:1.0.2"
checksum: 10c0/37fd2cd0ff2ad20073ce78d83fd718a740d568b225924e753ae51cb69d68f330c80544d487e5e5bd18e28702ed2ca469c2424ad948becd1862c1b0209542b2e9
languageName: node
linkType: hard
"natural-compare@npm:^1.4.0":
version: 1.4.0
resolution: "natural-compare@npm:1.4.0"
@ -6732,6 +6834,15 @@ __metadata:
languageName: node
linkType: hard
"node-abi@npm:^3.3.0":
version: 3.67.0
resolution: "node-abi@npm:3.67.0"
dependencies:
semver: "npm:^7.3.5"
checksum: 10c0/72ce2edbdfb84745bc201a4e48aa7146fd88a0d2c80046b6b17f28439c9a7683eab846f40f1e819349c31f7d9331ed5c50d1e741208d938dd5f38b29cab2275e
languageName: node
linkType: hard
"node-addon-api@npm:^1.6.3":
version: 1.7.2
resolution: "node-addon-api@npm:1.7.2"
@ -7190,6 +7301,28 @@ __metadata:
languageName: node
linkType: hard
"prebuild-install@npm:^7.1.1":
version: 7.1.2
resolution: "prebuild-install@npm:7.1.2"
dependencies:
detect-libc: "npm:^2.0.0"
expand-template: "npm:^2.0.3"
github-from-package: "npm:0.0.0"
minimist: "npm:^1.2.3"
mkdirp-classic: "npm:^0.5.3"
napi-build-utils: "npm:^1.0.1"
node-abi: "npm:^3.3.0"
pump: "npm:^3.0.0"
rc: "npm:^1.2.7"
simple-get: "npm:^4.0.0"
tar-fs: "npm:^2.0.0"
tunnel-agent: "npm:^0.6.0"
bin:
prebuild-install: bin.js
checksum: 10c0/e64868ba9ef2068fd7264f5b03e5298a901e02a450acdb1f56258d88c09dea601eefdb3d1dfdff8513fdd230a92961712be0676192626a3b4d01ba154d48bdd3
languageName: node
linkType: hard
"prelude-ls@npm:^1.2.1":
version: 1.2.1
resolution: "prelude-ls@npm:1.2.1"
@ -7868,6 +8001,20 @@ __metadata:
languageName: node
linkType: hard
"rc@npm:^1.2.7":
version: 1.2.8
resolution: "rc@npm:1.2.8"
dependencies:
deep-extend: "npm:^0.6.0"
ini: "npm:~1.3.0"
minimist: "npm:^1.2.0"
strip-json-comments: "npm:~2.0.1"
bin:
rc: ./cli.js
checksum: 10c0/24a07653150f0d9ac7168e52943cc3cb4b7a22c0e43c7dff3219977c2fdca5a2760a304a029c20811a0e79d351f57d46c9bde216193a0f73978496afc2b85b15
languageName: node
linkType: hard
"react-dom@npm:^18.2.0":
version: 18.3.1
resolution: "react-dom@npm:18.3.1"
@ -8063,6 +8210,17 @@ __metadata:
languageName: node
linkType: hard
"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0":
version: 3.6.2
resolution: "readable-stream@npm:3.6.2"
dependencies:
inherits: "npm:^2.0.3"
string_decoder: "npm:^1.1.1"
util-deprecate: "npm:^1.0.1"
checksum: 10c0/e37be5c79c376fdd088a45fa31ea2e423e5d48854be7a22a58869b4e84d25047b193f6acb54f1012331e1bcd667ffb569c01b99d36b0bd59658fb33f513511b7
languageName: node
linkType: hard
"readdirp@npm:~3.6.0":
version: 3.6.0
resolution: "readdirp@npm:3.6.0"
@ -8428,6 +8586,13 @@ __metadata:
languageName: node
linkType: hard
"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0":
version: 5.2.1
resolution: "safe-buffer@npm:5.2.1"
checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3
languageName: node
linkType: hard
"safe-regex-test@npm:^1.0.3":
version: 1.0.3
resolution: "safe-regex-test@npm:1.0.3"
@ -8595,6 +8760,24 @@ __metadata:
languageName: node
linkType: hard
"simple-concat@npm:^1.0.0":
version: 1.0.1
resolution: "simple-concat@npm:1.0.1"
checksum: 10c0/62f7508e674414008910b5397c1811941d457dfa0db4fd5aa7fa0409eb02c3609608dfcd7508cace75b3a0bf67a2a77990711e32cd213d2c76f4fd12ee86d776
languageName: node
linkType: hard
"simple-get@npm:^4.0.0":
version: 4.0.1
resolution: "simple-get@npm:4.0.1"
dependencies:
decompress-response: "npm:^6.0.0"
once: "npm:^1.3.1"
simple-concat: "npm:^1.0.0"
checksum: 10c0/b0649a581dbca741babb960423248899203165769747142033479a7dc5e77d7b0fced0253c731cd57cf21e31e4d77c9157c3069f4448d558ebc96cf9e1eebcf0
languageName: node
linkType: hard
"simple-update-notifier@npm:2.0.0":
version: 2.0.0
resolution: "simple-update-notifier@npm:2.0.0"
@ -8794,6 +8977,15 @@ __metadata:
languageName: node
linkType: hard
"string_decoder@npm:^1.1.1":
version: 1.3.0
resolution: "string_decoder@npm:1.3.0"
dependencies:
safe-buffer: "npm:~5.2.0"
checksum: 10c0/810614ddb030e271cd591935dcd5956b2410dd079d64ff92a1844d6b7588bf992b3e1b69b0f4d34a3e06e0bd73046ac646b5264c1987b20d0601f81ef35d731d
languageName: node
linkType: hard
"stringify-entities@npm:^4.0.0":
version: 4.0.4
resolution: "stringify-entities@npm:4.0.4"
@ -8829,6 +9021,13 @@ __metadata:
languageName: node
linkType: hard
"strip-json-comments@npm:~2.0.1":
version: 2.0.1
resolution: "strip-json-comments@npm:2.0.1"
checksum: 10c0/b509231cbdee45064ff4f9fd73609e2bcc4e84a4d508e9dd0f31f70356473fde18abfb5838c17d56fb236f5a06b102ef115438de0600b749e818a35fbbc48c43
languageName: node
linkType: hard
"style-to-object@npm:^1.0.0":
version: 1.0.6
resolution: "style-to-object@npm:1.0.6"
@ -8909,6 +9108,31 @@ __metadata:
languageName: node
linkType: hard
"tar-fs@npm:^2.0.0":
version: 2.1.1
resolution: "tar-fs@npm:2.1.1"
dependencies:
chownr: "npm:^1.1.1"
mkdirp-classic: "npm:^0.5.2"
pump: "npm:^3.0.0"
tar-stream: "npm:^2.1.4"
checksum: 10c0/871d26a934bfb7beeae4c4d8a09689f530b565f79bd0cf489823ff0efa3705da01278160da10bb006d1a793fa0425cf316cec029b32a9159eacbeaff4965fb6d
languageName: node
linkType: hard
"tar-stream@npm:^2.1.4":
version: 2.2.0
resolution: "tar-stream@npm:2.2.0"
dependencies:
bl: "npm:^4.0.3"
end-of-stream: "npm:^1.4.1"
fs-constants: "npm:^1.0.0"
inherits: "npm:^2.0.3"
readable-stream: "npm:^3.1.1"
checksum: 10c0/2f4c910b3ee7196502e1ff015a7ba321ec6ea837667220d7bcb8d0852d51cb04b87f7ae471008a6fb8f5b1a1b5078f62f3a82d30c706f20ada1238ac797e7692
languageName: node
linkType: hard
"tar@npm:^6.1.11, tar@npm:^6.1.12, tar@npm:^6.2.1":
version: 6.2.1
resolution: "tar@npm:6.2.1"
@ -9046,6 +9270,15 @@ __metadata:
languageName: node
linkType: hard
"tunnel-agent@npm:^0.6.0":
version: 0.6.0
resolution: "tunnel-agent@npm:0.6.0"
dependencies:
safe-buffer: "npm:^5.0.1"
checksum: 10c0/4c7a1b813e7beae66fdbf567a65ec6d46313643753d0beefb3c7973d66fcec3a1e7f39759f0a0b4465883499c6dc8b0750ab8b287399af2e583823e40410a17a
languageName: node
linkType: hard
"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
version: 0.4.0
resolution: "type-check@npm:0.4.0"
@ -9330,6 +9563,13 @@ __metadata:
languageName: node
linkType: hard
"util-deprecate@npm:^1.0.1":
version: 1.0.2
resolution: "util-deprecate@npm:1.0.2"
checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942
languageName: node
linkType: hard
"uuid@npm:^10.0.0":
version: 10.0.0
resolution: "uuid@npm:10.0.0"