diff --git a/capacitor/src/support.js b/capacitor/src/support.js index 3d38a54..4a06c6f 100644 --- a/capacitor/src/support.js +++ b/capacitor/src/support.js @@ -12,5 +12,6 @@ export const SUPPORTS = { torrentPersist: false, keybinds: false, isAndroid: true, - externalPlayer: false + externalPlayer: false, + permamentNAT: false // no way of safely closing app } diff --git a/common/jsconfig.json b/common/jsconfig.json index 887f71f..ad4d99b 100644 --- a/common/jsconfig.json +++ b/common/jsconfig.json @@ -1,23 +1,23 @@ { "compilerOptions": { - "baseUrl": "./", - "paths": { - "@/*": ["./*"], - "three": ["./types.d.ts"], - "rxjs": ["./types.d.ts"], - }, "checkJs": true, "target": "ESNext", - "moduleResolution": "bundler", + "moduleResolution": "node", + "module": "ESNext", + "allowSyntheticDefaultImports": true, "verbatimModuleSyntax": true, "isolatedModules": true, "resolveJsonModule": true, "sourceMap": true, "esModuleInterop": true, "skipLibCheck": true, - "module": "ESNext", "types": ["./types.d.ts"], - "allowSyntheticDefaultImports": true + "baseUrl": "./", + "paths": { + "@/*": ["./*"], + "three": ["./types.d.ts"], + "rxjs": ["./types.d.ts"], + }, }, "exclude": [ "node_modules", "dist", "build", "git_modules", ".svelte-kit", "public", "android", "@types/three", diff --git a/common/modules/support.js b/common/modules/support.js index 17820d2..0d2a157 100644 --- a/common/modules/support.js +++ b/common/modules/support.js @@ -13,5 +13,6 @@ export const SUPPORTS = { keybinds: true, extensions: true, isAndroid: false, - externalPlayer: true + externalPlayer: true, + permamentNAT: true } diff --git a/common/modules/webtorrent.js b/common/modules/webtorrent.js index 79f1816..21bcd98 100644 --- a/common/modules/webtorrent.js +++ b/common/modules/webtorrent.js @@ -39,6 +39,8 @@ export default class TorrentClient extends WebTorrent { playerProcess = null torrentPath = '' + ipc + constructor (ipc, storageQuota, serverMode, torrentPath, controller) { const settings = { ...defaults, ...storedSettings } super({ @@ -47,8 +49,10 @@ export default class TorrentClient extends WebTorrent { downloadLimit: settings.torrentSpeed * 1048576 || 0, uploadLimit: settings.torrentSpeed * 1572864 || 0, // :trolled: torrentPort: settings.torrentPort || 0, - dhtPort: settings.dhtPort || 0 + dhtPort: settings.dhtPort || 0, + natUpnp: SUPPORTS.permamentNAT ? 'permanent' : true }) + this.ipc = ipc this.torrentPath = torrentPath this._ready = new Promise(resolve => { ipc.on('port', ({ ports }) => { @@ -279,8 +283,11 @@ export default class TorrentClient extends WebTorrent { } destroy () { + if (this.destroyed) return this.parser?.destroy() this.server.close() - super.destroy() + super.destroy(() => { + this.ipc.send('destroyed') + }) } } diff --git a/common/webpack.config.cjs b/common/webpack.config.cjs index 3c9cd93..d39c998 100644 --- a/common/webpack.config.cjs +++ b/common/webpack.config.cjs @@ -6,9 +6,11 @@ const HtmlWebpackPlugin = require('html-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') +/** @type {(parentDir: string, alias?: Record, aliasFields?: (string | string[]), filename?: string) => import('webpack').WebpackOptionsNormalized} */ module.exports = (parentDir, alias = {}, aliasFields = 'browser', filename = 'app') => ({ devtool: 'source-map', - entry: [join(__dirname, 'main.js')], + entry: join(__dirname, 'main.js'), + stats: { warnings: false }, output: { path: join(parentDir, 'build'), filename: 'renderer.js' diff --git a/electron/package.json b/electron/package.json index 4a0d6a3..fb8a5b4 100644 --- a/electron/package.json +++ b/electron/package.json @@ -7,7 +7,7 @@ "main": "build/main.js", "homepage": "https://github.com/ThaUnknown/miru#readme", "scripts": { - "start": "cross-env NODE_ENV=development concurrently --kill-others \"npm run web:watch\" \"npm run electron:start\"", + "start": "cross-env NODE_ENV=development webpack build && concurrently --kill-others \"npm run web:watch\" \"npm run electron:start\"", "web:watch": "webpack serve", "web:build": "cross-env NODE_ENV=production webpack build", "electron:start": "electron ./build/main.js", @@ -25,6 +25,7 @@ "webpack-merge": "^5.10.0" }, "dependencies": { + "@paymoapp/electron-shutdown-handler": "^1.0.15", "utp-native": "^2.5.3" }, "standard": { diff --git a/electron/src/main/app.js b/electron/src/main/app.js new file mode 100644 index 0000000..aa9329b --- /dev/null +++ b/electron/src/main/app.js @@ -0,0 +1,134 @@ +import { join } from 'node:path' +import process from 'node:process' + +import { BrowserWindow, MessageChannelMain, app, dialog, ipcMain, powerMonitor, shell } from 'electron' +import electronShutdownHandler from '@paymoapp/electron-shutdown-handler' + +import { development } from './util.js' +import Discord from './discord.js' +import Protocol from './protocol.js' +import Updater from './updater.js' +import Dialog from './dialog.js' +import store from './store.js' + +export default class App { + webtorrentWindow = new BrowserWindow({ + show: development, + webPreferences: { + webSecurity: false, + allowRunningInsecureContent: false, + nodeIntegration: true, + contextIsolation: false, + backgroundThrottling: false + } + }) + + mainWindow = new BrowserWindow({ + width: 1600, + height: 900, + frame: process.platform === 'darwin', // Only keep the native frame on Mac + titleBarStyle: 'hidden', + titleBarOverlay: { + color: '#17191c', + symbolColor: '#eee', + height: 28 + }, + backgroundColor: '#17191c', + autoHideMenuBar: true, + webPreferences: { + webSecurity: false, + allowRunningInsecureContent: false, + enableBlinkFeatures: 'FontAccess, AudioVideoTracks', + backgroundThrottling: false, + preload: join(__dirname, '/preload.js') + }, + icon: join(__dirname, '/logo_filled.png'), + show: false + }) + + discord = new Discord(this.mainWindow) + protocol = new Protocol(this.mainWindow) + updater = new Updater(this.mainWindow) + dialog = new Dialog(this.webtorrentWindow) + + constructor () { + this.mainWindow.setMenuBarVisibility(false) + this.mainWindow.webContents.setWindowOpenHandler(() => ({ action: 'deny' })) + this.mainWindow.once('ready-to-show', () => this.mainWindow.show()) + this.mainWindow.on('minimize', () => this.mainWindow.webContents.postMessage('visibilitychange', 'hidden')) + this.mainWindow.on('restore', () => this.mainWindow.webContents.postMessage('visibilitychange', 'visible')) + ipcMain.on('devtools', () => this.webtorrentWindow.webContents.openDevTools()) + + this.mainWindow.on('closed', () => this.destroy()) + ipcMain.on('close', () => this.destroy()) + app.on('before-quit', e => { + if (this.destroyed) return + e.preventDefault() + this.destroy() + }) + + powerMonitor.on('shutdown', e => { + if (this.destroyed) return + e.preventDefault() + this.destroy() + }) + + if (process.platform === 'win32') { + // this message usually fires in dev-mode from the parent process + process.on('message', data => { + if (data === 'graceful-exit') this.destroy() + }) + electronShutdownHandler.setWindowHandle(this.mainWindow.getNativeWindowHandle()) + electronShutdownHandler.blockShutdown('Saving torrent data...') + electronShutdownHandler.on('shutdown', async () => { + await this.destroy() + electronShutdownHandler.releaseShutdown() + }) + } else { + process.on('SIGTERM', () => this.destroy()) + } + + const torrentLoad = this.webtorrentWindow.loadURL(development ? 'http://localhost:5000/background.html' : `file://${join(__dirname, '/background.html')}`) + this.mainWindow.loadURL(development ? 'http://localhost:5000/app.html' : `file://${join(__dirname, '/app.html')}`) + + if (development) { + this.webtorrentWindow.webContents.openDevTools() + this.mainWindow.webContents.openDevTools() + } + + let crashcount = 0 + this.mainWindow.webContents.on('render-process-gone', async (e, { reason }) => { + if (reason === 'crashed') { + if (++crashcount > 10) { + await dialog.showMessageBox({ message: 'Crashed too many times.', title: 'Miru', detail: 'App crashed too many times. For a fix visit https://miru.watch/faq/', icon: '/renderer/public/logo_filled.png' }) + shell.openExternal('https://miru.watch/faq/') + } else { + app.relaunch() + } + app.quit() + } + }) + + ipcMain.on('portRequest', async ({ sender }) => { + const { port1, port2 } = new MessageChannelMain() + await torrentLoad + this.webtorrentWindow.webContents.postMessage('port', null, [port1]) + this.webtorrentWindow.webContents.postMessage('player', store.get('player')) + this.webtorrentWindow.webContents.postMessage('torrentPath', store.get('torrentPath')) + sender.postMessage('port', null, [port2]) + }) + } + + destroyed = false + + async destroy () { + if (this.destroyed) return + this.webtorrentWindow.webContents.postMessage('destroy', null) + await new Promise(resolve => { + ipcMain.once('destroyed', resolve) + setTimeout(resolve, 5000).unref?.() + }) + this.destroyed = true + app.quit() + } +} diff --git a/electron/src/main/discord.js b/electron/src/main/discord.js index eafbcab..c42857d 100644 --- a/electron/src/main/discord.js +++ b/electron/src/main/discord.js @@ -23,18 +23,15 @@ export default class Discord { } } - discord + discord = new Client({ transport: 'ipc' }) + + /** @type {Discord['defaultStatus'] | undefined} */ allowDiscordDetails + /** @type {Discord['defaultStatus'] | undefined} */ cachedPresence - /** - * @param {import('electron').BrowserWindow} window - */ + /** @param {import('electron').BrowserWindow} window */ constructor (window) { - this.discord = new Client({ - transport: 'ipc' - }) - ipcMain.on('show-discord-status', (event, data) => { this.allowDiscordDetails = data this.debouncedDiscordRPC(this.allowDiscordDetails ? this.cachedPresence : undefined) diff --git a/electron/src/main/main.js b/electron/src/main/main.js index e9319d1..4711c3a 100644 --- a/electron/src/main/main.js +++ b/electron/src/main/main.js @@ -1,125 +1,16 @@ -/* eslint-disable no-new */ -import { app, BrowserWindow, shell, ipcMain, dialog, MessageChannelMain } from 'electron' -import path from 'path' -import Discord from './discord.js' -import Updater from './updater.js' -import Protocol from './protocol.js' -import { development } from './util.js' -import Dialog from './dialog.js' -import store from './store.js' +import { app } from 'electron' +import App from './app.js' // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. -let mainWindow -let webtorrentWindow +let main function createWindow () { - // Create the browser window. - webtorrentWindow = new BrowserWindow({ - show: development, - webPreferences: { - webSecurity: false, - allowRunningInsecureContent: false, - nodeIntegration: true, - contextIsolation: false, - backgroundThrottling: false - } - }) - mainWindow = new BrowserWindow({ - width: 1600, - height: 900, - frame: process.platform === 'darwin', // Only keep the native frame on Mac - titleBarStyle: 'hidden', - titleBarOverlay: { - color: '#17191c', - symbolColor: '#eee', - height: 28 - }, - backgroundColor: '#17191c', - autoHideMenuBar: true, - webPreferences: { - webSecurity: false, - allowRunningInsecureContent: false, - enableBlinkFeatures: 'FontAccess, AudioVideoTracks', - backgroundThrottling: false, - preload: path.join(__dirname, '/preload.js') - }, - icon: path.join(__dirname, '/logo_filled.png'), - show: false - }) - new Discord(mainWindow) - new Protocol(mainWindow) - new Updater(mainWindow) - new Dialog(webtorrentWindow) - mainWindow.setMenuBarVisibility(false) - - mainWindow.webContents.setWindowOpenHandler(() => { - return { action: 'deny' } - }) - - const torrentLoad = webtorrentWindow.loadURL(development ? 'http://localhost:5000/background.html' : `file://${path.join(__dirname, '/background.html')}`) - mainWindow.loadURL(development ? 'http://localhost:5000/app.html' : `file://${path.join(__dirname, '/app.html')}`) - - if (development) { - webtorrentWindow.webContents.openDevTools() - mainWindow.webContents.openDevTools() - } - - ipcMain.on('devtools', () => { - webtorrentWindow.webContents.openDevTools() - }) - - mainWindow.on('minimize', () => mainWindow.webContents.postMessage('visibilitychange', 'hidden')) - mainWindow.on('restore', () => mainWindow.webContents.postMessage('visibilitychange', 'visible')) - - mainWindow.on('closed', () => { - mainWindow = null - try { - webtorrentWindow.webContents.postMessage('destroy', null) - } catch (e) {} - app.quit() - }) - - ipcMain.on('close', () => { - mainWindow = null - try { - webtorrentWindow.webContents.postMessage('destroy', null) - } catch (e) {} - app.quit() - }) - - let crashcount = 0 - mainWindow.webContents.on('render-process-gone', (e, { reason }) => { - if (reason === 'crashed') { - if (++crashcount > 10) { - dialog.showMessageBox({ message: 'Crashed too many times.', title: 'Miru', detail: 'App crashed too many times. For a fix visit https://miru.watch/faq/', icon: '/renderer/public/logo_filled.png' }).then(() => { - shell.openExternal('https://miru.watch/faq/') - app.quit() - }) - } else { - app.relaunch() - app.quit() - } - } - }) - - // Emitted when the window is ready to be shown - // This helps in showing the window gracefully. - mainWindow.once('ready-to-show', () => { - mainWindow.show() - }) - ipcMain.on('portRequest', async ({ sender }) => { - const { port1, port2 } = new MessageChannelMain() - await torrentLoad - webtorrentWindow.webContents.postMessage('port', null, [port1]) - webtorrentWindow.webContents.postMessage('player', store.get('player')) - webtorrentWindow.webContents.postMessage('torrentPath', store.get('torrentPath')) - sender.postMessage('port', null, [port2]) - }) + main = new App() } app.on('ready', createWindow) app.on('activate', () => { - if (mainWindow === null) createWindow() + if (main === null) createWindow() }) diff --git a/electron/src/main/protocol.js b/electron/src/main/protocol.js index 669fc12..8db69fc 100644 --- a/electron/src/main/protocol.js +++ b/electron/src/main/protocol.js @@ -60,6 +60,9 @@ export default class Protocol { }) } + /** + * @param {string} line + */ sendToken (line) { let token = line.split('access_token=')[1].split('&token_type')[0] if (token) { @@ -68,6 +71,9 @@ export default class Protocol { } } + /** + * @param {string} text + */ handleProtocol (text) { const match = text.match(this.protocolRx) if (match) this.protocolMap[match[1]]?.(match[2]) diff --git a/electron/src/main/store.js b/electron/src/main/store.js index c13aaee..8092456 100644 --- a/electron/src/main/store.js +++ b/electron/src/main/store.js @@ -3,22 +3,37 @@ import { join } from 'node:path' import { writeFileSync, readFileSync } from 'node:fs' class Store { + /** + * @param {string} configName + * @param {{ angle: string; player: string; torrentPath: string; }} defaults + */ constructor (configName, defaults) { this.path = join(app.getPath('userData'), configName + '.json') this.data = parseDataFile(this.path, defaults) } + /** + * @param {string} key + */ get (key) { return this.data[key] } + /** + * @param {string} key + * @param {string} val + */ set (key, val) { this.data[key] = val writeFileSync(this.path, JSON.stringify(this.data)) } } +/** + * @param {import("fs").PathOrFileDescriptor} filePath + * @param {any} defaults + */ function parseDataFile (filePath, defaults) { try { return { ...defaults, ...JSON.parse(readFileSync(filePath).toString()) } diff --git a/electron/webpack.config.cjs b/electron/webpack.config.cjs index 2573b53..d1ba949 100644 --- a/electron/webpack.config.cjs +++ b/electron/webpack.config.cjs @@ -5,9 +5,11 @@ const mode = process.env.NODE_ENV?.trim() || 'development' const commonConfig = require('common/webpack.config.cjs') +/** @type {import('webpack').WebpackOptionsNormalized[]} */ module.exports = [ { devtool: 'source-map', + stats: { warnings: false }, entry: join(__dirname, 'src', 'background', 'background.js'), output: { path: join(__dirname, 'build'), @@ -44,6 +46,7 @@ module.exports = [ commonConfig(__dirname), { devtool: 'source-map', + stats: { warnings: false }, entry: join(__dirname, 'src', 'preload', 'preload.js'), output: { path: join(__dirname, 'build'), diff --git a/jsconfig.json b/jsconfig.json index 1f511ca..86c6ed6 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -22,5 +22,5 @@ "node_modules", "dist", "build", "git_modules", ".svelte-kit", "public", "android", "@types/three", "**/node_modules", "**/dist", "**/build", "**/git_modules", "**/.svelte-kit", "**/public", "**/android", "**/@types/three", "**/node_modules/*", "**/dist/*", "**/build/*", "**/git_modules/*", "**/.svelte-kit/*", "**/public/*", "**/android/*", "**@types/three/*.d.ts" -] + ] } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d8c9435..3b8dc72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -193,6 +193,9 @@ importers: electron: dependencies: + '@paymoapp/electron-shutdown-handler': + specifier: ^1.0.15 + version: 1.0.15 utp-native: specifier: ^2.5.3 version: 2.5.3 @@ -973,6 +976,14 @@ packages: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + /@paymoapp/electron-shutdown-handler@1.0.15: + resolution: {integrity: sha512-u3Pd81f58a/gyYVLeZ3UFpz27wLDUYJ+J6EVikiQPqj8C5Peh/nvCZBolVur0AOUhcXw8HkTDbmB0hulLx8NVw==} + requiresBuild: true + dependencies: + node-addon-api: 5.1.0 + prebuild-install: 7.1.1 + dev: false + /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -2392,7 +2403,6 @@ packages: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true /block-iterator@1.1.1: resolution: {integrity: sha512-DrjdVWZemVO4iBf4tiOXjUrY5cNesjzy0t7sIiu2rdl8cOCHRxAgKjSJFc3vBZYYMMmshUAxajl8QQh/uxXTKQ==} @@ -2511,7 +2521,6 @@ packages: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true /bufferutil@4.0.8: resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==} @@ -2708,7 +2717,6 @@ packages: /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - dev: true /chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} @@ -3425,7 +3433,6 @@ packages: /deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} - dev: true /deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -3535,7 +3542,6 @@ packages: /detect-libc@2.0.2: resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} engines: {node: '>=8'} - dev: true /detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} @@ -4376,7 +4382,6 @@ packages: /expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} - dev: true /express@4.18.3: resolution: {integrity: sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==} @@ -4668,7 +4673,6 @@ packages: /fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - dev: true /fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} @@ -4871,7 +4875,6 @@ packages: /github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - dev: true /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -5315,7 +5318,6 @@ packages: /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} requiresBuild: true - dev: true /ignore-by-default@1.0.1: resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} @@ -5374,7 +5376,6 @@ packages: /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: true /ini@2.0.0: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} @@ -6311,7 +6312,6 @@ packages: /mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - dev: true /mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} @@ -6363,7 +6363,6 @@ packages: /napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} - dev: true /napi-macros@2.2.2: resolution: {integrity: sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==} @@ -6420,7 +6419,6 @@ packages: engines: {node: '>=10'} dependencies: semver: 7.6.0 - dev: true /node-addon-api@1.7.2: resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} @@ -6431,6 +6429,10 @@ packages: resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} dev: true + /node-addon-api@5.1.0: + resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + dev: false + /node-addon-api@6.1.0: resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} dev: true @@ -7092,7 +7094,6 @@ packages: simple-get: 4.0.1 tar-fs: 2.1.1 tunnel-agent: 0.6.0 - dev: true /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -7248,7 +7249,6 @@ packages: ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 - dev: true /read-config-file@6.3.2: resolution: {integrity: sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==} @@ -8267,7 +8267,6 @@ packages: /strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} - dev: true /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} @@ -8475,7 +8474,6 @@ packages: mkdirp-classic: 0.5.3 pump: 3.0.0 tar-stream: 2.2.0 - dev: true /tar-fs@3.0.4: resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} @@ -8494,7 +8492,6 @@ packages: fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true /tar-stream@3.1.6: resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} @@ -8777,7 +8774,6 @@ packages: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} dependencies: safe-buffer: 5.2.1 - dev: true /tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}