diff --git a/README.md b/README.md index 53b3c622..3572a1db 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Grist Desktop App, built with Electron +# Grist Desktop This is an Electron build of [Grist](https://github.com/gristlabs/grist-core/). Use it to easily open and edit Grist spreadsheets on your computer. It does not @@ -20,7 +20,7 @@ and [grist-omnibus](https://github.com/gristlabs/grist-omnibus/). ## Download -See https://github.com/gristlabs/grist-electron/releases +See https://github.com/gristlabs/grist-desktop/releases ## Screenshots @@ -40,7 +40,7 @@ A [Doggy Daycare](https://templates.getgrist.com/vAcfEKLQf3YF/Doggie-Daycare) sp ![Grist on Windows 7](https://user-images.githubusercontent.com/118367/215295214-83c46e03-16f6-45d2-84dd-d26d34cb5f95.jpeg) -Grist Electron being used as a server on a LAN, on Windows 10 Pro (credit: [Sylvain_Page](https://community.getgrist.com/t/packaging-grist-as-an-electron-app/1233/29)). +Grist Desktop being used as a server on a LAN, on Windows 10 Pro (credit: [Sylvain_Page](https://community.getgrist.com/t/packaging-grist-as-an-electron-app/1233/29)). ![Grist on Windows 10 Pro](https://user-images.githubusercontent.com/118367/221203024-ac8ad72d-bb08-43dd-9447-f9a06cfbce3e.jpeg) @@ -61,59 +61,80 @@ yarn run electron ## Configure -There's no configuration needed if you are just running this as a regular app -to view and edit Grist spreadsheets on your laptop. - -Some people use the app as a quick way to set up a simple Grist server -in a local network where everyone is trusted. Be sure you know what you're -doing - if you have any security concerns at all, I'd urge you to do a -proper Grist server installation - see https://support.getgrist.com/self-managed/ - -If you are sure you are in a trusted environment, you can set some environment -variables to make Grist listen on a specific network interface and port: - -``` -GRIST_HOST=192.168.1.22 # IP address to serve from -GRIST_PORT=8484 # Port number to serve at -GRIST_ELECTRON_AUTH=strict # Auth strategy (strict, mixed, or none) -``` - -(You can create a `.env` file in the root directory of the app and set -the environment variables there). Set `GRIST_ELECTRON_AUTH` to `none` -to allow access across the network just as if you were using the app. -Set `GRIST_ELECTRON_AUTH` to `mixed` to allow anonymous access -across the network, but not logins. Set `GRIST_ELECTRON_AUTH` to `strict` -to require logins and to permit them only in the app. - -It you use Grist on the network this way, be aware that data is being +There's no configuration needed if you are just running Grist Desktop as a +regular app to view and edit Grist spreadsheets on your laptop. +However, some aspects of Grist Desktop can be tuned with environment variables. + +For developers: You can create a `.env` file in the root directory of the app +and set the environment variables there. If you are a Grist Desktop end user, +consider using the config file instead. + +### Environment Variables + +**`GRIST_DEFAULT_USERNAME`**: The name of the default user. Only effective when +Grist Desktop initializes its database during the first launch. Default: `You` + +**`GRIST_DEFAULT_EMAIL`**: The email of the default user. This is only effective +when Grist Desktop initializes its database during the first launch. If you want +to change this after initialization, you need to manually reset the database, +re-initialize it and import your documents back. Usually you should not need to +worry about this. Default: `you@example.com` + +**`GRIST_HOST`**: The IP address to serve the Grist server from. It is not +recommended to set this. See this [note](#note-on-using-grist-desktop-as-a-server) +for more info. Default: `localhost` + +**`GRIST_PORT`**: The port number to listen on. It is not recommended to set this. +Default: Grist Desktop will randomly pick an available port. + +**`GRIST_DESKTOP_AUTH`**: The authentication mode to use. Must be one of `strict`, +`mixed` and `none`. `none` allows network access as you. `mixed` allows anonymous +network access. `strict` disallows network access. This used to be `GRIST_ELECTRON_AUTH`, +which is still supported but deprecated. When both are set, `GRIST_DESKTOP_AUTH` +has higher precedence. If you are still using `GRIST_ELECTRON_AUTH`, please consider +switching to `GRIST_DESKTOP_AUTH`. Default: `strict` + +**`GRIST_SANDBOX_FLAVOR`**: The sandbox mechanism to use. It is recommended to stick +to the default. Must be one of `pyodide`, `gvisor`, `macSandboxExec` and +`unsandboxed`. See this [note](#note-on-sandboxing) for more info. Default: `pyodide` + +**`GRIST_INST_DIR`**, **`GRIST_DATA_DIR`**, **`GRIST_USER_ROOT`** and +**`TYPEORM_DATABASE`**: These are a bit technical and require some understanding of how +Grist Desktop works. For the time being, Grist Desktop works by launching a Grist server +in the background. These variables can configure where the Grist server should store its files. +By default, `GRIST_INST_DIR` is set to `getPath("userData")` defined by Electron; +`GRIST_DATA_DIR` is set to `getPath("documents")`; `GRIST_USER_ROOT` is set to `.grist` +in your home directory. `TYPEORM_DATABASE` is set to `landing.db` under +`getPath("appData")`. If you change them, make sure to move existing data accordingly. +See [grist-core documentation](https://github.com/gristlabs/grist-core) for details. +You might want to store your Grist documents somewhere else and have a clean "Documents" +folder. In this case, set `GRIST_DATA_DIR` to your desired location and move all `.grist` +files there. + +### Note on using Grist Desktop as a server + +If you are sure you are in a trusted environment, you can use the app as a +quick way to set up a simple Grist server, but be aware that data is being sent using plain http and not encrypted https, so network traffic could be readable in transit. And there is no login mechanism built in. -An experimental sandboxing mechanism is turned on by default, so that -formulas in a spreadsheet are limited in their effect. Sandboxing can be -turned off by setting: +If you have security concerns, we recommend switching to a proper Grist server +installation instead - see https://support.getgrist.com/self-managed/ -``` -GRIST_SANDBOX_FLAVOR=unsandboxed -``` +### Note on sandboxing -It can be explicitly set by doing: +Sandboxing limits the effects of formulas in spreadsheets. It is recommended to use `pyodide`, +as `gvisor` and `macSandboxExec` are not yet easy to use. -``` -GRIST_SANDBOX_FLAVOR=pyodide -``` - -There are also `gvisor` and `macSandboxExec` sandbox -flavors, but they are not yet easy to use. - -If you turn off sandboxing, then the full raw power of Python will be available -to any Grist spreadsheet you open. So: +If you turn it off, the full raw power of Python will be available to any Grist +spreadsheet you open. So: * Use only with your own Grist spreadsheets, or - * Use with spreadsheets you trust, or + * Use only with spreadsheets you trust, or * Turn sandboxing the heck back on, or * Return to the YOLO days of opening spreadsheets and crossing your fingers. + ## History Learn the back-story of this work in the diff --git a/ext/app/electron/GristApp.ts b/ext/app/electron/GristApp.ts new file mode 100644 index 00000000..4c5780ea --- /dev/null +++ b/ext/app/electron/GristApp.ts @@ -0,0 +1,430 @@ +import * as electron from "electron"; +import * as fse from "fs-extra"; +import * as gutil from "app/common/gutil"; +import * as log from "app/server/lib/log"; +import * as path from "path"; +import * as shutdown from "app/server/lib/shutdown"; +import * as winston from "winston"; +import { ElectronServerMethods, FlexServer } from "app/server/lib/FlexServer"; +import { GristDesktopAuthMode, getMinimalElectronLoginSystem } from "app/electron/logins"; +import AppMenu from "app/electron/AppMenu"; +import RecentItems from "app/common/RecentItems"; +import { UpdateManager } from "app/electron/UpdateManager"; +import { makeId } from "app/server/lib/idUtils"; +import { main as mergedServerMain } from "app/server/mergedServerMain"; +import { updateDb } from "app/server/lib/dbUtils"; +import webviewOptions from "app/electron/webviewOptions"; + +export class GristApp { + private flexServer: FlexServer; + private app = electron.app; + private appWindows: Set = new Set(); // A set of all our window objects. + private appHost: string; // The hostname to connect to the local node server we start. + private pendingPathToOpen: string | undefined = undefined; // Path to open when app is started to open a document. + + // Function, set once the app is ready, that opens or focuses a window when Grist is started. It + // is called on 'ready' and by onInstanceStart (triggered when starting another Grist instance). + private onStartup: (optPath?: string) => Promise; + private credential: string = makeId(); + private shouldQuit = false; + private authMode: GristDesktopAuthMode; + + public constructor() { + this.authMode = process.env.GRIST_DESKTOP_AUTH as GristDesktopAuthMode; + } + + public main() { + if (!this.app.requestSingleInstanceLock()) { + this.app.quit(); + this.shouldQuit = true; + } + this.app.on('second-instance', (_, argv, cwd) => { + this.onInstanceStart(argv, cwd); + }); + + // limits access to the webview api, read the `webviewOptions` module documentation for more + // information + // TODO: check if this still works (has path information). + webviewOptions.setOptions({ + preloadURL: `file://${__dirname}/webviewPreload.js`, + nodeIntegration: false, + enableWhiteListOnly: true, + }); + + // It would be nice to just return when shouldQuit is true, but that's a problem for some tools + // (babel?), so we ... don't. + // TODO: this is a super old comment, check if we can simplify now. + if (this.shouldQuit) { + return; + } + + this.setupLogging(); + + // On Windows, opening a file by double-clicking it invokes Grist with path as the first arg. + // This is also a handy way to open files from the command line on Linux. + if (process.argv[1] && fse.existsSync(process.argv[1])) { + this.pendingPathToOpen = path.resolve(process.cwd(), process.argv[1]); + } + + // This is triggered on Mac when opening a .grist file, e.g. by double-clicking on it. + this.app.on('open-file', (_, path) => this.pendingPathToOpen = path); + + // on('ready') is too late to set up at this point, but whenReady will happily resolve if the + // application is already ready. + this.app.whenReady().then(() => this.onReady().catch(reportErrorAndStop)); + } + + private onInstanceStart(argv: string[], workingDir: string) { + argv = this.cleanArgv(argv); + // Someone tried to run a second instance, we should either open a file or focus a window. + log.debug("onInstanceStart %s in %s", JSON.stringify(argv), workingDir); + if (this.onStartup) { + this.onStartup(argv[1] ? path.resolve(workingDir, argv[1]) : undefined); + } + } + + private cleanArgv(argv: string[]) { + // Ignoring flags starting with '-' which might be added by electron on Mac (See + // https://phab.getgrist.com/T307). + return argv.filter((arg) => !arg.startsWith('-')); + } + + private openWindowForDoc(docID: string, openWith?: {loadURL: (url: string) => Promise}) { + // Create the browser window, and load the document. + (openWith || this.createWindow()).loadURL(this.getUrl(docID)); + } + + // Opens file at filepath for any accepted file type. + private async handleOpen(serverMethods: ElectronServerMethods, filepath: string) { + log.debug("handleOpen %s", filepath); + const ext = path.extname(filepath); + switch (ext) { + case '.csv': + case '.xlsx': + case '.xlsm': { + const doc = await serverMethods.importDoc(filepath); + this.openWindowForDoc(doc.id); + break; + } + default: + await this.openGristFile(filepath).catch(e => this.reportError(e)); + break; + } + } + + private getUrl(docID?: string) { + const url = new URL(this.appHost); + if (docID) { + url.pathname = 'doc/' + encodeURIComponent(docID); + } + if (this.authMode !== 'none') { + url.searchParams.set('electron_key', this.credential); + } + return url.href; + } + + private async openGristFile(filepath: string, openWith?: {loadURL: (url: string) => Promise}) { + const target = await this.normalizePath(filepath); + const docsRoot = this.flexServer.docsRoot; + const root = await this.normalizePath(docsRoot); + console.log("Opening a file", { + filepath, + target, + docsRoot, + root, + }); + + // Here is our dumb strategy for opening random Grist files on the + // file system: just mint a key and soft-link to them. If being + // professional, Grist should be watching for external modifications. + // Baby steps though. + let docId: string|undefined; + let maybeDocId: string|undefined; + if (!path.relative(root, target).startsWith('..')) { + const did = path.basename(target, '.grist'); + const p = path.join(docsRoot, `${did}.grist`); + if (await this.normalizePath(p) === target) { + maybeDocId = did; + } + } + const db = this.flexServer.getHomeDBManager(); + const user = await db.getUserByLogin(process.env.GRIST_DEFAULT_EMAIL as string); + if (!user) { throw new Error('cannot find default user'); } + const wss = db.unwrapQueryResult(await db.getOrgWorkspaces({userId: user.id}, 0)); + for (const doc of wss[0].docs) { + if (doc.options?.externalId === target || doc.id === maybeDocId) { + docId = doc.id; + break; + } + } + if (!docId) { + docId = db.unwrapQueryResult(await db.addDocument({ + userId: user.id, + }, wss[0].id, { + name: path.basename(target, '.grist'), + options: { externalId: target }, + })); + } + const link = path.join(docsRoot, `${docId}.grist`); + if (!await fse.pathExists(link)) { + await fse.symlink(target, link, 'junction'); + } + this.openWindowForDoc(docId, openWith); + } + + // Returns the last Grist window that was created. + private getLastWindow() { + let lastWindow = null; + for (const win of this.appWindows) { + lastWindow = win; + } + return lastWindow; + } + + private createWindow() { + const win = new electron.BrowserWindow({ + width: 1024, + height: 768, + webPreferences: { + nodeIntegration: false, + // TODO: check if this still works (has path information). + preload: path.join(__dirname, 'preload.js'), + webviewTag: true + }, + backgroundColor: '#42494B', + autoHideMenuBar: false, + }); + + // Add the window to the set of browser windows we maintain. + this.appWindows.add(win); + + // Register for title updates + win.on('page-title-updated', async (event, title) => { + event.preventDefault(); + + // Set represented filename (on macOS) to home directory if on Start page + if (title === 'Home - Grist') { + const docPath = this.app.getPath('documents'); + win.setTitle(path.basename(docPath)); + win.setRepresentedFilename(docPath); + } else { + let docPath = path.resolve(this.app.getPath('documents'), title); + docPath += (path.extname(docPath) === '.grist' ? '' : '.grist'); + + try { + await fse.access(docPath); + // If valid path, set to path + win.setTitle(path.basename(docPath) + ' (' + path.dirname(docPath) + ')'); + win.setRepresentedFilename(docPath); + } catch(err) { + // If not valid path, leave title as-is and don't set the represented file + win.setTitle(title); + win.setRepresentedFilename(''); + } + } + }); + + // If browser JS called window.open(), open it in an external browser if it's a non-local URL. + win.webContents.setWindowOpenHandler((details) => { + if (!gutil.startsWith(details.url, this.appHost)) { + electron.shell.openExternal(details.url); + return {action: "deny"}; + } + return {action: "allow"}; + }); + + // Remove the window from the set when it's closed. + win.on('closed', () => { + this.appWindows.delete(win); + }); + return win; + } + + /** + * Generally, our debug log output is discarded when running on Mac as a standalone application. + * For debug output, we will append log to ~/grist_debug.log, but only if it exists. + * + * So, to enable logging: `touch ~/grist_debug.log` + * To disable logging: `rm ~/grist_debug.log` + * To clear the log: `rm ~/grist_debug.log; touch ~/grist_debug.log` + * + * In summary: + * - When running app from finder or "open" command, no debug output. + * - When running from terminal as "Grist.app/Contents/MacOS/Grist, debug output goes to console. + * - When ~/grist_debug.log exists, log also to that file. + */ + private setupLogging() { + const debugLogPath = (process.env.GRIST_LOG_PATH || + path.join(this.app.getPath('home'), 'grist_debug.log')); + + if (process.env.GRIST_LOG_PATH || fse.existsSync(debugLogPath)) { + const output = fse.createWriteStream(debugLogPath, { flags: "a" }); + output.on('error', (err) => log.error("Failed to open %s: %s", debugLogPath, err)); + output.on('open', () => { + log.info('Logging also to %s', debugLogPath); + output.write('\n--- log starting by pid ' + process.pid + ' ---\n'); + + const fileTransportOptions = { + name: 'debugLog', + stream: output, + level: 'debug', + timestamp: log.timestamp, + colorize: true, + json: false + }; + + // TODO: This does not log HTTP requests to the file. For that we may want to use + // "express-winston" module, and possibly update winston (we are far behind). + log.add(winston.transports.File, fileTransportOptions); + winston.add(winston.transports.File, fileTransportOptions); + }); + } + } + + private async onReady() { + // APP_HOME_URL is set by loadConfig + this.appHost = process.env.APP_HOME_URL as string; + + await updateDb(); + + this.flexServer = await mergedServerMain( + parseInt(process.env["GRIST_PORT"] as string, 10), + ['home', 'docs', 'static', 'app'], { + loginSystem: getMinimalElectronLoginSystem.bind(null, this.credential, this.authMode), + }); + const serverMethods = this.flexServer.electronServerMethods; + // This function is what we'll call now, and also in onInstanceStart. The latter is used on + // Windows thanks to makeSingleInstance, and triggered when user clicks another .grist file. + // We can only set this callback once we have serverMethods and appHost. + this.onStartup = async (optPath?: string) => { + log.debug("onStartup %s", optPath); + if (optPath) { + await this.handleOpen(serverMethods, optPath); + return; + } + const win = this.getLastWindow(); + if (win) { + win.show(); + return; + } + // We had no file to open, so open a window to the DocList. + this.createWindow().loadURL(this.getUrl()); + }; + + // Call onStartup immediately. + this.onStartup(this.pendingPathToOpen); + this.pendingPathToOpen = undefined; + + const recentItems = new RecentItems({ + maxCount: 10, + intialItems: (await serverMethods.getUserConfig()).recentItems + }); + const appMenu = new AppMenu(recentItems); + electron.Menu.setApplicationMenu(appMenu.getMenu()); + const updateManager = new UpdateManager(appMenu); + console.log(updateManager ? 'updateManager loadable, but not used yet' : ''); + + // TODO: file new still does something, but it doesn't make a lot of sense. + appMenu.on('menu-file-new', () => this.createWindow().loadURL(this.getUrl())); + + appMenu.on('menu-file-open', async () => { + const result = await electron.dialog.showOpenDialog({ + title: 'Open existing Grist file', + defaultPath: this.app.getPath('documents'), + filters: [{ name: 'Grist files', extensions: ['grist'] }], + // disabling extensions 'csv', 'xlsx', and 'xlsm' for the moment. + properties: ['openFile'] + }); + const files = result.filePaths; + if (files) { + await this.handleOpen(serverMethods, files[0]); + } + }); + + // If we get a request to show the Open-File dialog, do so, and load the result file if one + // is selected. + electron.ipcMain.on('show-open-dialog', async (ev) => { + const result = await electron.dialog.showOpenDialog({ + title: 'Open existing Grist file', + defaultPath: this.app.getPath('documents'), + filters: [{ name: 'Grist files', extensions: ['grist'] }], + properties: ['openFile'] + }); + const files = result.filePaths; + if (files) { + // ev.sender is the webContents object that sent this message. + this.openGristFile(files[0], ev.sender); + } + }); + + serverMethods.onDocOpen((filePath: string) => { + // Add to list of recent docs in the dock (mac) or the JumpList (win) + this.app.addRecentDocument(filePath); + // Add to list of recent docs in the menu + recentItems.addItem(filePath); + serverMethods.updateUserConfig({ recentItems: recentItems.listItems() }); + // TODO: Electron does not yet support updating the menu except by reassigning the entire + // menu. There are proposals to allow menu templates include callbacks that + // are called on menu open. https://github.com/electron/electron/issues/528 + appMenu.rebuildMenu(); + electron.Menu.setApplicationMenu(appMenu.getMenu()); + }); + + // serverMethods.onBackupMade((bakPath: string) => notifyMigrateBackup(bakPath)); + + // Check for updates, and check again periodically (if user declines, it's the interval till + // the next reminder, so too short would be annoying). + // if (updateManager.startAutoCheck()) { + // updateManager.schedulePeriodicChecks(6*3600); + // } else { + // log.warn("updateManager not starting (known not to work on Linux)"); + // } + + // Now that we are ready, future 'open-file' events should just open windows directly. + this.app.removeAllListeners('open-file'); + this.app.on('open-file', (_, filepath) => this.handleOpen(serverMethods, filepath)); + + this.app.on('will-quit', function(event) { + event.preventDefault(); + shutdown.exit(0); + }); + + // Quit when all windows are closed. + this.app.on('window-all-closed', () => { + this.app.quit(); + }); + + // Plugins create elements with a "plugins" partition; here we add a special header + // to all such requests. Requests for plugin content without this header will be rejected by + // the server, to ensure that untrusted content is only loaded in protected elements. + electron.session.fromPartition("plugins").webRequest.onBeforeSendHeaders((details, callback) => { + details.requestHeaders["X-From-Plugin-WebView"] = "true"; + callback({requestHeaders: details.requestHeaders}); + }); + } + + private reportError(e: Error) { + electron.dialog.showMessageBoxSync({ + type: "info", + buttons: ["Ok"], + message: "Error", + detail: String(e) + }); + } + + private async normalizePath(filepath: string) { + // Use realpath if possible. + try { + filepath = await fse.realpath(filepath); + } catch (e) { + // if there's a problem, e.g. file doesn't exist or is symlink to + // nowhere, don't panic. + } + return path.normalize(filepath); + } +} + +function reportErrorAndStop(e: Error) { + console.error(e); + process.exit(1); +} diff --git a/ext/app/electron/UpdateManager.ts b/ext/app/electron/UpdateManager.ts index 70c5cd13..08312ea3 100644 --- a/ext/app/electron/UpdateManager.ts +++ b/ext/app/electron/UpdateManager.ts @@ -1,9 +1,9 @@ -import * as electron from 'electron'; -import * as electronUpdater from 'electron-updater'; -import * as log from 'app/server/lib/log'; -import * as version from 'app/common/version'; +import * as electron from "electron"; +import * as electronUpdater from "electron-updater"; +import * as log from "app/server/lib/log"; +import * as version from "app/common/version"; -class UpdateManager { +export class UpdateManager { private _suppressPopups: boolean; private _appMenu: any; private _timeout: any; @@ -175,5 +175,3 @@ class UpdateManager { }) as any); } } - -module.exports = UpdateManager; diff --git a/ext/app/electron/config.ts b/ext/app/electron/config.ts new file mode 100644 index 00000000..e1195457 --- /dev/null +++ b/ext/app/electron/config.ts @@ -0,0 +1,123 @@ +import * as dotenv from "dotenv"; +import * as electron from "electron"; +import * as fse from "fs-extra"; +import * as log from "app/server/lib/log"; +import * as packageJson from "desktop.package.json"; +import * as path from "path"; +import { commonUrls } from "app/common/gristUrls"; +import { getAvailablePort } from "app/server/lib/serverUtils"; + + +const NO_VALIDATION = () => true; + + +function validateOrFallback(envKey: string, validator: (value: string) => boolean, defaultValue: string,): void { + const envValue = process.env[envKey]; + if (envValue === undefined) { + log.warn(`${envKey} is not set, using default value ${defaultValue}`); + process.env[envKey] = defaultValue; + } else { + if (!validator(envValue)) { + log.warn(`$${envKey} has invalid value ${envValue}, using default value ${defaultValue}`); + process.env[envKey] = defaultValue; + } + } +} + + +export async function loadConfig() { + dotenv.config(); + if (process.env.GRIST_ELECTRON_AUTH !== undefined) { + if (process.env.GRIST_DESKTOP_AUTH === undefined) { + process.env.GRIST_DESKTOP_AUTH = process.env.GRIST_ELECTRON_AUTH; + log.warn("GRIST_ELECTRON_AUTH has been deprecated; use GRIST_DESKTOP_AUTH instead."); + } else { + log.warn("GRIST_DESKTOP_AUTH set, ignoring GRIST_ELECTRON_AUTH (deprecated)."); + } + } + validateOrFallback( + "GRIST_DEFAULT_USERNAME", + NO_VALIDATION, + "You" + ); + validateOrFallback( + "GRIST_DEFAULT_EMAIL", + NO_VALIDATION, + "you@example.com" + ); + validateOrFallback( + "GRIST_HOST", + NO_VALIDATION, + "localhost" + ); + validateOrFallback( + "GRIST_PORT", + (portstr) => { + if (! /^\d+$/.test(portstr)) { + return false; + } + const port = parseInt(portstr); + return port > 0 && port < 65536; + }, + (await getAvailablePort(47478)).toString() + ); + validateOrFallback( + "GRIST_DESKTOP_AUTH", + (auth) => ["strict", "none", "mixed"].includes(auth), + "strict" + ); + validateOrFallback( + "GRIST_SANDBOX_FLAVOR", + (flavor) => ["pyodide", "unsandboxed", "gvisor", "macSandboxExec"].includes(flavor), + "pyodide" + ); + validateOrFallback( + "GRIST_INST_DIR", + NO_VALIDATION, + electron.app.getPath("userData") + ); + validateOrFallback( + "GRIST_DATA_DIR", + NO_VALIDATION, + electron.app.getPath("documents") + ); + validateOrFallback( + "GRIST_USER_ROOT", + NO_VALIDATION, + path.join(electron.app.getPath("home"), ".grist") + ); + validateOrFallback( + "TYPEORM_DATABASE", + NO_VALIDATION, + path.join(electron.app.getPath("appData"), "landing.db") + ); + validateOrFallback( + "GRIST_WIDGET_LIST_URL", // Related to plugins (Would have to be changed if local custom widgets are used?) + NO_VALIDATION, + commonUrls.gristLabsWidgetRepository + ); + + const homeDBLocation = path.parse(path.resolve(process.env.TYPEORM_DATABASE as string)).dir; + if (!fse.existsSync(homeDBLocation)) { + log.warn(`Directory to contain the home DB does not exist, creating ${homeDBLocation}`); + fse.mkdirSync(homeDBLocation); + } + + // We don't allow manually setting these envvars anymore. Fixing them makes maintaining grist-desktop easier. + process.env.APP_HOME_URL = `http://${process.env["GRIST_HOST"]}:${process.env["GRIST_PORT"]}`; + process.env.GRIST_SINGLE_PORT = "true"; + process.env.GRIST_SERVE_SAME_ORIGIN = "true"; + process.env.GRIST_DEFAULT_PRODUCT = "Free"; + process.env.GRIST_ORG_IN_PATH = "true"; + process.env.GRIST_HIDE_UI_ELEMENTS = "helpCenter,billing,templates,multiSite,multiAccounts"; + process.env.GRIST_CONTACT_SUPPORT_URL = packageJson.repository + "/issues"; + if (process.env.GRIST_DESKTOP_AUTH !== "mixed") { + process.env.GRIST_FORCE_LOGIN = "true"; + } + + // Note: This is neither validated nor documented, and subject to deprecation. + // Original comment: TODO: check trust in electron scenario, this code is very rusty. + if (process.env.GRIST_UNTRUSTED_PORT === undefined && process.env.APP_UNTRUSTED_URL === undefined) { + process.env["GRIST_UNTRUSTED_PORT"] = (await getAvailablePort(47479)).toString(); + } +} diff --git a/ext/app/electron/logins.ts b/ext/app/electron/logins.ts index f1dbffc0..2467d147 100644 --- a/ext/app/electron/logins.ts +++ b/ext/app/electron/logins.ts @@ -1,14 +1,23 @@ -import {ApiError} from 'app/common/ApiError'; -import {expressWrap} from 'app/server/lib/expressWrap'; -import {GristLoginMiddleware, GristLoginSystem, GristServer, - setUserInSession} from 'app/server/lib/GristServer'; -import {getDefaultProfile} from 'app/server/lib/MinimalLogin'; -import {getOrgUrl} from 'app/server/lib/requestUtils'; -import {Request} from 'express'; +import { GristLoginMiddleware, GristLoginSystem, GristServer, setUserInSession } from "app/server/lib/GristServer"; +import { ApiError } from "app/common/ApiError"; +import { Request } from "express"; +import { UserProfile } from "app/common/LoginSessionAPI"; +import cookie from "cookie"; +import { expressWrap } from "app/server/lib/expressWrap"; +import { getOrgUrl } from "app/server/lib/requestUtils"; -const cookie = require('cookie'); -export type GristElectronAuthMode = 'strict' | 'none' | 'mixed'; +export type GristDesktopAuthMode = 'strict' | 'none' | 'mixed'; + +export function getProfile(): UserProfile { + // Both variables are guaranteed to be set when this function is invoked, + // since loadConfig() is called before a GristApp instance is created. + // If they are not set by the user, default values will be used. See config.ts for details. + return { + email: process.env.GRIST_DEFAULT_EMAIL as string, + name: process.env.GRIST_DEFAULT_USERNAME as string, + }; +} /** * A bare bones login system specialized for Electron. Single, hard-coded user. @@ -16,7 +25,7 @@ export type GristElectronAuthMode = 'strict' | 'none' | 'mixed'; * else is anonymous. */ export async function getMinimalElectronLoginSystem(credential: string, - authMode: GristElectronAuthMode): Promise { + authMode: GristDesktopAuthMode): Promise { // Login and logout, redirecting immediately back. Signup is treated as login, // no nuance here. return { @@ -25,19 +34,19 @@ export async function getMinimalElectronLoginSystem(credential: string, if (authMode !== 'none' && !(req as any).electronDirect) { return getOrgUrl(req) + 'electron_only'; } - await setUserInSession(req, gristServer, getDefaultProfile()); + await setUserInSession(req, gristServer, getProfile()); return url.href; } const middleware: GristLoginMiddleware = { getLoginRedirectUrl, getSignUpRedirectUrl: getLoginRedirectUrl, - async getLogoutRedirectUrl(req: Request, url: URL) { + async getLogoutRedirectUrl(_: Request, url: URL) { return url.href; }, async addEndpoints(app) { // Make sure default user exists. const dbManager = gristServer.getHomeDBManager(); - const profile = getDefaultProfile(); + const profile = getProfile(); const user = await dbManager.getUserByLoginWithRetry(profile.email, {profile}); if (user) { // No need to survey this user! diff --git a/ext/app/electron/main.ts b/ext/app/electron/main.ts index 02b5aea7..49c2d868 100644 --- a/ext/app/electron/main.ts +++ b/ext/app/electron/main.ts @@ -1,511 +1,41 @@ -import * as dotenv from "dotenv"; -import {commonUrls} from 'app/common/gristUrls'; -import * as version from 'app/common/version'; -import * as log from 'app/server/lib/log'; -import * as electron from 'electron'; -import * as fse from 'fs-extra'; -import * as os from 'os'; -import * as path from 'path'; -import * as winston from 'winston'; - -dotenv.config(); - -// Handle --version flag, which causes us to only print version, without running anything. -if (process.argv.includes('--version')) { - console.log(`${version.version} (${version.gitcommit} on ${version.channel})`); - process.exit(0); -} - -// Default to putting the database somewhere sensible for an app. -// TODO: what about grist-sessions.db? -setDefaultEnv('TYPEORM_DATABASE', path.resolve(electron.app.getPath('appData'), 'landing.db')); -// Default to pyodide unless user specifically asked for unsandboxed or other. -setDefaultEnv('GRIST_SANDBOX_FLAVOR', 'pyodide'); -setDefaultEnv('GRIST_MINIMAL_LOGIN', 'true'); -setDefaultEnv('GRIST_SINGLE_PORT', 'true'); -setDefaultEnv('GRIST_SERVE_SAME_ORIGIN', 'true'); -setDefaultEnv('GRIST_DEFAULT_PRODUCT', 'Free'); -setDefaultEnv('GRIST_ORG_IN_PATH', 'true'); -setDefaultEnv('APP_UNTRUSTED_URL', 'http://plugins.invalid'); -setDefaultEnv('GRIST_HIDE_UI_ELEMENTS', 'helpCenter,billing,templates,multiSite,multiAccounts'); -setDefaultEnv('GRIST_ELECTRON_AUTH', 'strict'); -setDefaultEnv('GRIST_WIDGET_LIST_URL', commonUrls.gristLabsWidgetRepository); -if (process.env.GRIST_ELECTRON_AUTH !== 'mixed') { - setDefaultEnv('GRIST_FORCE_LOGIN', 'true'); +import * as electron from "electron"; +import * as path from "path"; +import { program } from "commander"; +// A temporary hack to make `yarn start` work. +// TODO: Create a script that actually calls resolve-tspaths when source code changes, and ditch this. +if (!electron.app.isPackaged) { + process.env.NODE_PATH = + path.resolve(process.cwd(), 'core/_build') + + ':' + + path.resolve(process.cwd(), 'core/_build/ext') + + ':' + + path.resolve(process.cwd(), 'core/_build/stubs') + + ':' + process.env.NODE_PATH; + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('module').Module._initPaths(); } -const EMAIL = setDefaultEnv('GRIST_DEFAULT_EMAIL', 'you@example.com'); - -// The dbUtils import must happen after TYPEORM_DATABASE is set up. -// Safest to do most Grist codebase imports at this point, in case they -// include dbUtils indirectly, now or in the future. -import * as gutil from 'app/common/gutil'; -import { updateDb } from 'app/server/lib/dbUtils'; -import { FlexServer } from 'app/server/lib/FlexServer'; -import {makeId} from "app/server/lib/idUtils"; -import * as serverUtils from 'app/server/lib/serverUtils'; -import * as shutdown from 'app/server/lib/shutdown'; -import { main as mergedServerMain } from 'app/server/mergedServerMain'; - -import { getMinimalElectronLoginSystem, GristElectronAuthMode} from "app/electron/logins"; - -const RecentItems = require('app/common/RecentItems'); - -const AppMenu = require('app/electron/AppMenu'); -const UpdateManager = require('app/electron/UpdateManager'); -const webviewOptions = require('app/electron/webviewOptions'); - +// eslint-disable-next-line sort-imports +import * as log from "app/server/lib/log"; +import * as packageJson from "desktop.package.json"; +import * as version from "app/common/version"; +import { GristApp } from "app/electron/GristApp"; +import { loadConfig } from "app/electron/config"; + +program.name(packageJson.name).version(`${packageJson.productName} ${packageJson.version} (with Grist Core ${version.version})`); +program.parse(); + +// When unpackaged (yarn electron:preview), the module's name will be argv[1]. +// This snippet strips that to mimic the behavior when packaged. +// Since commander already handles this gotcha, the hack must be applied after parsing arguments. if (!electron.app.isPackaged) { - // When packaged, we do not expect the module's path in argv[1]. - // When unpackaged, mimic that. process.argv.splice(1, 1); } -class GristApp { - private flexServer: FlexServer; - private app = electron.app; - private appWindows = new Set(); // A set of all our window objects. - private appHost: any = null; // The hostname to connect to the local node server we start. - private pendingPathToOpen: any = null; // Path to open when app is started to open a document. - - // Function, set once the app is ready, that opens or focuses a window when Grist is started. It - // is called on 'ready' and by onInstanceStart (triggered when starting another Grist instance). - private onStartup: any = null; - private credential: string = makeId(); - private shouldQuit = false; - private authMode: GristElectronAuthMode; - - public constructor() { - const mode = process.env.GRIST_ELECTRON_AUTH; - if (mode === 'strict' || mode === 'none' || mode === 'mixed') { - this.authMode = mode; - } else { - this.authMode = 'strict'; - } - } - - public main() { - if (!this.app.requestSingleInstanceLock()) { - this.app.quit(); - this.shouldQuit = true; - } - this.app.on('second-instance', (_, argv, cwd) => { - this.onInstanceStart(argv, cwd); - }); - - // limits access to the webview api, read the `webviewOptions` module documentation for more - // information - // TODO: check if this still works (has path information). - webviewOptions.setOptions({ - preloadURL: `file://${__dirname}/webviewPreload.js`, - nodeIntegration: false, - enableWhiteListOnly: true, - }); - - // It would be nice to just return when shouldQuit is true, but that's a problem for some tools - // (babel?), so we ... don't. - // TODO: this is a super old comment, check if we can simplify now. - if (this.shouldQuit) { - return; - } - - this.setupLogging(); - - // On Windows, opening a file by double-clicking it invokes Grist with path as the first arg. - // This is also a handy way to open files from the command line on Linux. - if (process.argv[1] && fse.existsSync(process.argv[1])) { - this.pendingPathToOpen = path.resolve(process.cwd(), process.argv[1]); - } - - // This is triggered on Mac when opening a .grist file, e.g. by double-clicking on it. - this.app.on('open-file', (_, path) => this.pendingPathToOpen = path); - - // This method will be called when Electron has finished - // initialization and is ready to create browser windows. - this.app.on('ready', () => this.onReady().catch(reportErrorAndStop)); - } - - private onInstanceStart(argv: any, workingDir: any) { - argv = this.cleanArgv(argv); - // Someone tried to run a second instance, we should either open a file or focus a window. - log.debug("onInstanceStart %s in %s", JSON.stringify(argv), workingDir); - if (this.onStartup) { - this.onStartup(argv[1] ? path.resolve(workingDir, argv[1]) : null); - } - } - - private cleanArgv(argv: any) { - // Ignoring flags starting with '-' which might be added by electron on Mac (See - // https://phab.getgrist.com/T307). - return argv.filter((arg: any) => !arg.startsWith('-')); - } - - private openWindowForPath(path: string, openWith?: {loadURL: (url: string) => Promise}) { - // Create the browser window, and load the document. - (openWith || this.createWindow()).loadURL(this.getUrl({doc: path})); - } - - // Opens file at filepath for any accepted file type. - private async handleOpen(serverMethods: any, filepath: string) { - log.debug("handleOpen %s", filepath); - const ext = path.extname(filepath); - switch (ext) { - case '.csv': - case '.xlsx': - case '.xlsm': - const docName = serverMethods.importDoc(filepath); - this.openWindowForPath(docName); - break; - default: - await this.openGristFile(filepath).catch(e => this.reportError(e)); - break; - } - } - - private getUrl(options: { - doc?: string, - } = {}) { - const url = new URL(this.appHost); - if (options.doc) { - url.pathname = 'doc/' + encodeURIComponent(options.doc); - } - if (this.authMode !== 'none') { - url.searchParams.set('electron_key', this.credential); - } - return url.href; - } - - private async openGristFile(filepath: string, openWith?: {loadURL: (url: string) => Promise}) { - const target = await this.normalizePath(filepath); - const docsRoot = this.flexServer.docsRoot; - const root = await this.normalizePath(docsRoot); - console.log("Opening a file", { - filepath, - target, - docsRoot, - root, - }); - - // Here is our dumb strategy for opening random Grist files on the - // file system: just mint a key and soft-link to them. If being - // professional, Grist should be watching for external modifications. - // Baby steps though. - let docId: string|undefined; - let maybeDocId: string|undefined; - if (!path.relative(root, target).startsWith('..')) { - const did = path.basename(target, '.grist'); - const p = path.join(docsRoot, `${did}.grist`); - if (await this.normalizePath(p) === target) { - maybeDocId = did; - } - } - const db = this.flexServer.getHomeDBManager(); - const user = await db.getUserByLogin(EMAIL); - if (!user) { throw new Error('cannot find default user'); } - const wss = db.unwrapQueryResult(await db.getOrgWorkspaces({userId: user.id}, 0)); - for (const doc of wss[0].docs) { - if (doc.options?.externalId === target || doc.id === maybeDocId) { - docId = doc.id; - break; - } - } - if (!docId) { - docId = db.unwrapQueryResult(await db.addDocument({ - userId: user.id, - }, wss[0].id, { - name: path.basename(target, '.grist'), - options: { externalId: target }, - })); - } - const link = path.join(docsRoot, `${docId}.grist`); - if (!await fse.pathExists(link)) { - await fse.symlink(target, link, 'junction'); - } - this.openWindowForPath(docId, openWith); - } - - // Returns the last Grist window that was created. - private getLastWindow() { - let lastWindow = null; - for (let win of this.appWindows) { - lastWindow = win; - } - return lastWindow; - } - - private createWindow() { - const win = new electron.BrowserWindow({ - width: 1024, - height: 768, - webPreferences: { - nodeIntegration: false, - // TODO: check if this still works (has path information). - preload: path.join(__dirname, 'preload.js'), - webviewTag: true - }, - backgroundColor: '#42494B', - autoHideMenuBar: false, - }); - - // Add the window to the set of browser windows we maintain. - this.appWindows.add(win); - - // Register for title updates - win.on('page-title-updated', async (event, title) => { - event.preventDefault(); - - // Set represented filename (on macOS) to home directory if on Start page - if (title === 'Home - Grist') { - let docPath = this.app.getPath('documents'); - win.setTitle(path.basename(docPath)); - win.setRepresentedFilename(docPath); - } else { - let docPath = path.resolve(this.app.getPath('documents'), title); - docPath += (path.extname(docPath) === '.grist' ? '' : '.grist'); - - try { - await fse.access(docPath); - // If valid path, set to path - win.setTitle(path.basename(docPath) + ' (' + path.dirname(docPath) + ')'); - win.setRepresentedFilename(docPath); - } catch(err) { - // If not valid path, leave title as-is and don't set the represented file - win.setTitle(title); - win.setRepresentedFilename(''); - } - } - }); - - // If browser JS called window.open(), open it in an external browser if it's a non-local URL. - win.webContents.setWindowOpenHandler((details) => { - if (!gutil.startsWith(details.url, this.appHost)) { - electron.shell.openExternal(details.url); - return {action: "deny"} - } - return {action: "allow"} - }); - - // Remove the window from the set when it's closed. - win.on('closed', () => { - this.appWindows.delete(win); - }); - return win; - } - - /** - * Generally, our debug log output is discarded when running on Mac as a standalone application. - * For debug output, we will append log to ~/grist_debug.log, but only if it exists. - * - * So, to enable logging: `touch ~/grist_debug.log` - * To disable logging: `rm ~/grist_debug.log` - * To clear the log: `rm ~/grist_debug.log; touch ~/grist_debug.log` - * - * In summary: - * - When running app from finder or "open" command, no debug output. - * - When running from terminal as "Grist.app/Contents/MacOS/Grist, debug output goes to console. - * - When ~/grist_debug.log exists, log also to that file. - */ - private setupLogging() { - const debugLogPath = (process.env.GRIST_LOG_PATH || - path.join(this.app.getPath('home'), 'grist_debug.log')); - - if (process.env.GRIST_LOG_PATH || fse.existsSync(debugLogPath)) { - const output = fse.createWriteStream(debugLogPath, { flags: "a" }); - output.on('error', (err: any) => log.error("Failed to open %s: %s", debugLogPath, err)); - output.on('open', () => { - log.info('Logging also to %s', debugLogPath); - output.write('\n--- log starting by pid ' + process.pid + ' ---\n'); - - const fileTransportOptions = { - name: 'debugLog', - stream: output, - level: 'debug', - timestamp: log.timestamp, - colorize: true, - json: false - }; - - // TODO: This does not log HTTP requests to the file. For that we may want to use - // "express-winston" module, and possibly update winston (we are far behind). - log.add(winston.transports.File, fileTransportOptions); - winston.add(winston.transports.File, fileTransportOptions); - }); - } - } - - private async onReady() { - // The port doesn't matter, we only care to avoid interfering with something that another - // application might want after we have bound it. We pick 47478 (phone number for GRIST). - const port = process.env.PORT || process.env.GRIST_PORT || - await serverUtils.getAvailablePort(47478); - // get available port for untrusted content - const untrustedPort = process.env.UNTRUSTED_PORT || - process.env.GRIST_UNTRUSTED_PORT || - await serverUtils.getAvailablePort(47479); - - const host = setDefaultEnv('GRIST_HOST', 'localhost'); - setDefaultEnv('PORT', String(port)); - this.appHost = `http://${host}:${port}`; - setDefaultEnv('APP_HOME_URL', this.appHost); - // TODO: check trust in electron scenario, this code is very rusty. - setDefaultEnv('APP_UNTRUSTED_URL', `http://${host}:${untrustedPort}`); - - await updateDb(); - - setDefaultEnv('GRIST_USER_ROOT', path.join(os.homedir(), '.grist')); - setDefaultEnv('GRIST_DATA_DIR', this.app.getPath('documents')); - setDefaultEnv('GRIST_INST_DIR', process.env.GRIST_USER_DATA_DIR || this.app.getPath('userData')); - this.flexServer = await mergedServerMain( - parseInt(String(port), 10), - ['home', 'docs', 'static', 'app'], { - loginSystem: getMinimalElectronLoginSystem.bind(null, this.credential, this.authMode), - }); - const serverMethods = this.flexServer.electronServerMethods; - // This function is what we'll call now, and also in onInstanceStart. The latter is used on - // Windows thanks to makeSingleInstance, and triggered when user clicks another .grist file. - // We can only set this callback once we have serverMethods and appHost. - this.onStartup = async (optPath: any) => { - log.debug("onStartup %s", optPath); - if (optPath) { - await this.handleOpen(serverMethods, optPath); - return; - } - let win = this.getLastWindow(); - if (win) { - (win as any).show(); - return; - } - // We had no file to open, so open a window to the DocList. - this.createWindow().loadURL(this.getUrl()); - }; - - // Call onStartup immediately. - this.onStartup(this.pendingPathToOpen); - this.pendingPathToOpen = null; - - let recentItems = new RecentItems({ - maxCount: 10, - intialItems: (await serverMethods.getUserConfig()).recentItems - }); - let appMenu = new AppMenu(recentItems); - electron.Menu.setApplicationMenu(appMenu.getMenu()); - let updateManager = new UpdateManager(appMenu); - console.log(updateManager ? 'updateManager loadable, but not used yet' : ''); - - // TODO: file new still does something, but it doesn't make a lot of sense. - appMenu.on('menu-file-new', () => this.createWindow().loadURL(this.getUrl())); - - appMenu.on('menu-file-open', async () => { - const result = await electron.dialog.showOpenDialog({ - title: 'Open existing Grist file', - defaultPath: this.app.getPath('documents'), - filters: [{ name: 'Grist files', extensions: ['grist'] }], - // disabling extensions 'csv', 'xlsx', and 'xlsm' for the moment. - properties: ['openFile'] - }); - const files = result.filePaths; - if (files) { - await this.handleOpen(serverMethods, files[0]); - } - }); - - // If we get a request to show the Open-File dialog, do so, and load the result file if one - // is selected. - electron.ipcMain.on('show-open-dialog', async (ev) => { - const result = await electron.dialog.showOpenDialog({ - title: 'Open existing Grist file', - defaultPath: this.app.getPath('documents'), - filters: [{ name: 'Grist files', extensions: ['grist'] }], - properties: ['openFile'] - }); - const files = result.filePaths; - if (files) { - // ev.sender is the webContents object that sent this message. - this.openGristFile(files[0], ev.sender); - } - }); - - serverMethods.onDocOpen((filePath: string) => { - // Add to list of recent docs in the dock (mac) or the JumpList (win) - this.app.addRecentDocument(filePath); - // Add to list of recent docs in the menu - recentItems.addItem(filePath); - serverMethods.updateUserConfig({ recentItems: recentItems.listItems() }); - // TODO: Electron does not yet support updating the menu except by reassigning the entire - // menu. There are proposals to allow menu templates include callbacks that - // are called on menu open. https://github.com/electron/electron/issues/528 - appMenu.rebuildMenu(); - electron.Menu.setApplicationMenu(appMenu.getMenu()); - }); - - // serverMethods.onBackupMade((bakPath: string) => notifyMigrateBackup(bakPath)); - - // Check for updates, and check again periodically (if user declines, it's the interval till - // the next reminder, so too short would be annoying). - // if (updateManager.startAutoCheck()) { - // updateManager.schedulePeriodicChecks(6*3600); - // } else { - // log.warn("updateManager not starting (known not to work on Linux)"); - // } - - // Now that we are ready, future 'open-file' events should just open windows directly. - this.app.removeAllListeners('open-file'); - this.app.on('open-file', (_, filepath) => this.handleOpen(serverMethods, filepath)); - - this.app.on('will-quit', function(event) { - event.preventDefault(); - shutdown.exit(0); - }); - - // Quit when all windows are closed. - this.app.on('window-all-closed', () => { - this.app.quit(); - }); - - // Plugins create elements with a "plugins" partition; here we add a special header - // to all such requests. Requests for plugin content without this header will be rejected by - // the server, to ensure that untrusted content is only loaded in protected elements. - electron.session.fromPartition("plugins").webRequest.onBeforeSendHeaders((details, callback) => { - details.requestHeaders["X-From-Plugin-WebView"] = "true"; - callback({requestHeaders: details.requestHeaders}); - }); - } - - private reportError(e: Error) { - electron.dialog.showMessageBoxSync({ - type: "info", - buttons: ["Ok"], - message: "Error", - detail: String(e) - }); - } - - private async normalizePath(filepath: string) { - // Use realpath if possible. - try { - filepath = await fse.realpath(filepath); - } catch (e) { - // if there's a problem, e.g. file doesn't exist or is symlink to - // nowhere, don't panic. - } - return path.normalize(filepath); - } -} - -const gristApp = new GristApp(); -gristApp.main(); - - -// Set a default for an environment variable. -function setDefaultEnv(name: string, value: string) { - const current = process.env[name] - if (current !== undefined) { - return current; - } - process.env[name] = value; - return value; -} - -function reportErrorAndStop(e: Error) { - console.error(e); - process.exit(1); -} +loadConfig() + .then(() => { + new GristApp().main(); + }) + .catch((err) => { + log.error(`Failed to load config, aborting: ${err}`); + process.exit(1); + }); diff --git a/ext/app/tsconfig.json b/ext/app/tsconfig.json index f344f8d6..e35b0224 100644 --- a/ext/app/tsconfig.json +++ b/ext/app/tsconfig.json @@ -4,6 +4,7 @@ "include": [ "electron/**/*", "../../stubs/app/common/version.ts", + "../desktop.package.json", ], "references": [ { "path": "../../app/client" }, diff --git a/ext/desktop.package.json b/ext/desktop.package.json new file mode 120000 index 00000000..4e26811d --- /dev/null +++ b/ext/desktop.package.json @@ -0,0 +1 @@ +../package.json \ No newline at end of file diff --git a/ext/eslint.config.mjs b/ext/eslint.config.mjs new file mode 100644 index 00000000..e21e4016 --- /dev/null +++ b/ext/eslint.config.mjs @@ -0,0 +1,18 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import tseslint from "typescript-eslint"; + + +export default [ + {languageOptions: { globals: globals.browser }}, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, + { + rules: { + "semi": ["warn", "always"], + "eqeqeq": ["warn", "always"], + "sort-imports": ["warn", {}], + "@typescript-eslint/no-explicit-any": "warn" + } + } +]; diff --git a/ext/package.json b/ext/package.json index 20a2613a..3cac34f5 100644 --- a/ext/package.json +++ b/ext/package.json @@ -2,14 +2,19 @@ "name": "ext", "version": "1.0.0", "dependencies": { - "aws-sdk": "2.1061.0", "@azure/storage-blob": "12.9.0", + "aws-sdk": "2.1061.0", + "commander": "^12.1.0", "dotenv": "16.0.3", "electron-updater": "^5.3.0" }, "devDependencies": { + "@electron/notarize": "2.3.2", "electron": "30.0.6", "electron-builder": "23.6.0", - "@electron/notarize": "2.3.2" + "eslint": "9.x", + "@eslint/js": "^9.4.0", + "globals": "^15.4.0", + "typescript-eslint": "^7.12.0" } } diff --git a/ext/tsconfig.json b/ext/tsconfig.json deleted file mode 100644 index 8de5ab30..00000000 --- a/ext/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "include": [] -} diff --git a/package.json b/package.json index 541dc38f..9eb425e1 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,22 @@ { "private": true, - "name": "grist-electron", - "productName": "grist-electron", + "name": "grist-desktop", + "productName": "Grist Desktop", "description": "Grist is the evolution of spreadsheets", "version": "0.2.9", "main": "core/_build/ext/app/electron/main.js", - "repository": "git@github.com:paulfitz/grist-electron", - "author": "Paul Fitzpatrick ", - "license": "MIT", - "workspaces": [ - "core", - "ext" - ], + "repository": "https://github.com/gristlabs/grist-desktop", + "author": "Grist Labs Inc. ", + "license": "Apache-2.0", + "workspaces": { + "packages": ["core", "ext"], + "nohoist": [ + "**/eslint/**", + "**/typescript-eslint/**", + "**/globals/**", + "**/@eslint/js/**" + ] + }, "devDependencies": { "resolve-tspaths": "^0.8.19", "typescript": "4.7.4" @@ -47,6 +52,7 @@ "target": "portable" } ], + "artifactName": "${name}-windows-${version}-${arch}.${ext}", "icon": "core/static/icons/grist.ico" }, "portable": { @@ -57,7 +63,7 @@ "perMachine": true }, "mac": { - "artifactName": "${productName}-mac-${version}-${arch}.${ext}", + "artifactName": "${name}-mac-${version}-${arch}.${ext}", "icon": "core/static/icons/grist.icns", "category": "public.app-category.developer-tools", "hardenedRuntime": true, @@ -66,7 +72,7 @@ "entitlementsInherit": "scripts/entitlements.mac.plist" }, "linux": { - "artifactName": "${productName}-linux-${version}-${arch}.${ext}", + "artifactName": "${name}-linux-${version}-${arch}.${ext}", "icon": "core/static/icons/grist.png", "target": "AppImage" }, diff --git a/scripts/setup.sh b/scripts/setup.sh index 2563355d..fde2bfe5 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -140,6 +140,9 @@ echo "Configure Grist to include external Electron code during build" if [ ! -e core/ext ]; then ln -s ../ext core/ext fi +if [ ! -e ext/desktop.package.json ]; then + ln -s ../package.json ext/desktop.package.json +fi echo "" echo "=======================================================================" diff --git a/yarn.lock b/yarn.lock index ab6e5e68..6e5aa95e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -127,18 +127,18 @@ events "^3.0.0" tslib "^2.2.0" -"@babel/code-frame@^7.16.7", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.2": - version "7.24.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" - integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== +"@babel/code-frame@^7.16.7", "@babel/code-frame@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.6.tgz#ab88da19344445c3d8889af2216606d3329f3ef2" + integrity sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA== dependencies: - "@babel/highlight" "^7.24.2" + "@babel/highlight" "^7.24.6" picocolors "^1.0.0" -"@babel/compat-data@^7.23.5": - version "7.24.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a" - integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== +"@babel/compat-data@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.6.tgz#b3600217688cabb26e25f8e467019e66d71b7ae2" + integrity sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ== "@babel/core@7.18.5": version "7.18.5" @@ -170,157 +170,156 @@ eslint-visitor-keys "^2.1.0" semver "^6.3.0" -"@babel/generator@^7.18.2", "@babel/generator@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.5.tgz#e5afc068f932f05616b66713e28d0f04e99daeb3" - integrity sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA== +"@babel/generator@^7.18.2", "@babel/generator@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.6.tgz#dfac82a228582a9d30c959fe50ad28951d4737a7" + integrity sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg== dependencies: - "@babel/types" "^7.24.5" + "@babel/types" "^7.24.6" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" jsesc "^2.5.1" "@babel/helper-compilation-targets@^7.18.2": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" - integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz#4a51d681f7680043d38e212715e2a7b1ad29cb51" + integrity sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg== dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-validator-option" "^7.23.5" + "@babel/compat-data" "^7.24.6" + "@babel/helper-validator-option" "^7.24.6" browserslist "^4.22.2" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== +"@babel/helper-environment-visitor@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz#ac7ad5517821641550f6698dd5468f8cef78620d" + integrity sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g== -"@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== +"@babel/helper-function-name@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz#cebdd063386fdb95d511d84b117e51fc68fec0c8" + integrity sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w== dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" + "@babel/template" "^7.24.6" + "@babel/types" "^7.24.6" -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== +"@babel/helper-hoist-variables@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz#8a7ece8c26756826b6ffcdd0e3cf65de275af7f9" + integrity sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.24.6" -"@babel/helper-module-imports@^7.24.3": - version "7.24.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" - integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== +"@babel/helper-module-imports@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz#65e54ffceed6a268dc4ce11f0433b82cfff57852" + integrity sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g== dependencies: - "@babel/types" "^7.24.0" + "@babel/types" "^7.24.6" "@babel/helper-module-transforms@^7.18.0": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz#ea6c5e33f7b262a0ae762fd5986355c45f54a545" - integrity sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A== + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz#22346ed9df44ce84dee850d7433c5b73fab1fe4e" + integrity sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-module-imports" "^7.24.3" - "@babel/helper-simple-access" "^7.24.5" - "@babel/helper-split-export-declaration" "^7.24.5" - "@babel/helper-validator-identifier" "^7.24.5" + "@babel/helper-environment-visitor" "^7.24.6" + "@babel/helper-module-imports" "^7.24.6" + "@babel/helper-simple-access" "^7.24.6" + "@babel/helper-split-export-declaration" "^7.24.6" + "@babel/helper-validator-identifier" "^7.24.6" -"@babel/helper-simple-access@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz#50da5b72f58c16b07fbd992810be6049478e85ba" - integrity sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ== +"@babel/helper-simple-access@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz#1d6e04d468bba4fc963b4906f6dac6286cfedff1" + integrity sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g== dependencies: - "@babel/types" "^7.24.5" + "@babel/types" "^7.24.6" -"@babel/helper-split-export-declaration@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz#b9a67f06a46b0b339323617c8c6213b9055a78b6" - integrity sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q== +"@babel/helper-split-export-declaration@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz#e830068f7ba8861c53b7421c284da30ae656d7a3" + integrity sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw== dependencies: - "@babel/types" "^7.24.5" + "@babel/types" "^7.24.6" -"@babel/helper-string-parser@^7.24.1": - version "7.24.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" - integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== +"@babel/helper-string-parser@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz#28583c28b15f2a3339cfafafeaad42f9a0e828df" + integrity sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q== -"@babel/helper-validator-identifier@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62" - integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA== +"@babel/helper-validator-identifier@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz#08bb6612b11bdec78f3feed3db196da682454a5e" + integrity sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw== -"@babel/helper-validator-option@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" - integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== +"@babel/helper-validator-option@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz#59d8e81c40b7d9109ab7e74457393442177f460a" + integrity sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ== "@babel/helpers@^7.18.2": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.5.tgz#fedeb87eeafa62b621160402181ad8585a22a40a" - integrity sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q== + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.6.tgz#cd124245299e494bd4e00edda0e4ea3545c2c176" + integrity sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA== dependencies: - "@babel/template" "^7.24.0" - "@babel/traverse" "^7.24.5" - "@babel/types" "^7.24.5" + "@babel/template" "^7.24.6" + "@babel/types" "^7.24.6" -"@babel/highlight@^7.24.2": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.5.tgz#bc0613f98e1dd0720e99b2a9ee3760194a704b6e" - integrity sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw== +"@babel/highlight@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.6.tgz#6d610c1ebd2c6e061cade0153bf69b0590b7b3df" + integrity sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ== dependencies: - "@babel/helper-validator-identifier" "^7.24.5" + "@babel/helper-validator-identifier" "^7.24.6" chalk "^2.4.2" js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.18.5", "@babel/parser@^7.24.0", "@babel/parser@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790" - integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg== +"@babel/parser@^7.18.5", "@babel/parser@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.6.tgz#5e030f440c3c6c78d195528c3b688b101a365328" + integrity sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q== "@babel/runtime@^7.17.2", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.5.tgz#230946857c053a36ccc66e1dd03b17dd0c4ed02c" - integrity sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g== + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.6.tgz#5b76eb89ad45e2e4a0a8db54c456251469a3358e" + integrity sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.16.7", "@babel/template@^7.22.15", "@babel/template@^7.24.0": - version "7.24.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" - integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/parser" "^7.24.0" - "@babel/types" "^7.24.0" - -"@babel/traverse@^7.18.5", "@babel/traverse@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.5.tgz#972aa0bc45f16983bf64aa1f877b2dd0eea7e6f8" - integrity sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA== - dependencies: - "@babel/code-frame" "^7.24.2" - "@babel/generator" "^7.24.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.24.5" - "@babel/parser" "^7.24.5" - "@babel/types" "^7.24.5" +"@babel/template@^7.16.7", "@babel/template@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.6.tgz#048c347b2787a6072b24c723664c8d02b67a44f9" + integrity sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw== + dependencies: + "@babel/code-frame" "^7.24.6" + "@babel/parser" "^7.24.6" + "@babel/types" "^7.24.6" + +"@babel/traverse@^7.18.5": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.6.tgz#0941ec50cdeaeacad0911eb67ae227a4f8424edc" + integrity sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw== + dependencies: + "@babel/code-frame" "^7.24.6" + "@babel/generator" "^7.24.6" + "@babel/helper-environment-visitor" "^7.24.6" + "@babel/helper-function-name" "^7.24.6" + "@babel/helper-hoist-variables" "^7.24.6" + "@babel/helper-split-export-declaration" "^7.24.6" + "@babel/parser" "^7.24.6" + "@babel/types" "^7.24.6" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.18.4", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.24.5": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.5.tgz#7661930afc638a5383eb0c4aee59b74f38db84d7" - integrity sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ== +"@babel/types@^7.18.4", "@babel/types@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.6.tgz#ba4e1f59870c10dc2fa95a274ac4feec23b21912" + integrity sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ== dependencies: - "@babel/helper-string-parser" "^7.24.1" - "@babel/helper-validator-identifier" "^7.24.5" + "@babel/helper-string-parser" "^7.24.6" + "@babel/helper-validator-identifier" "^7.24.6" to-fast-properties "^2.0.0" "@develar/schema-utils@~2.6.5": @@ -378,6 +377,27 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw== +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": + version "4.10.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.1.tgz#361461e5cb3845d874e61731c11cfedd664d83a0" + integrity sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA== + +"@eslint/config-array@^0.15.1": + version "0.15.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.15.1.tgz#1fa78b422d98f4e7979f2211a1fde137e26c7d61" + integrity sha512-K4gzNq+yymn/EVsXYmf+SBcBro8MTf+aXJZUphM96CdzUEr+ClGDvAbpmaEK+cGVigVXIgs9gNmvHAlrzzY5JQ== + dependencies: + "@eslint/object-schema" "^2.1.3" + debug "^4.3.1" + minimatch "^3.0.5" + "@eslint/eslintrc@^1.3.0": version "1.4.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" @@ -393,6 +413,31 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/eslintrc@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" + integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.4.0", "@eslint/js@^9.4.0": + version "9.4.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.4.0.tgz#96a2edd37ec0551ce5f9540705be23951c008a0c" + integrity sha512-fdI7VJjP3Rvc70lC4xkFXHB0fiPeojiL1PxVG6t1ZvXQrarj893PweuBTujxDUFk0Fxj4R7PIIAZ/aiiyZPZcg== + +"@eslint/object-schema@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.3.tgz#e65ae80ee2927b4fd8c5c26b15ecacc2b2a6cc2a" + integrity sha512-HAbhAYKfsAC2EkTqve00ibWIZlaU74Z1EHwAjYr4PXF0YU2VEA1zSIKSSpKszRLRWwHzzRZXvK632u+uXzvsvw== + "@fast-csv/format@4.3.5": version "4.3.5" resolved "https://registry.yarnpkg.com/@fast-csv/format/-/format-4.3.5.tgz#90d83d1b47b6aaf67be70d6118f84f3e12ee1ff3" @@ -505,11 +550,21 @@ debug "^4.1.1" minimatch "^3.0.4" +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/retry@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" + integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== + "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" @@ -595,7 +650,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -906,9 +961,9 @@ integrity sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g== "@types/express-serve-static-core@^4.17.33": - version "4.19.0" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz#3ae8ab3767d98d0b682cda063c3339e1e86ccfaa" - integrity sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ== + version "4.19.3" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz#e469a13e4186c9e1c0418fb17be8bc8ff1b19a7a" + integrity sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg== dependencies: "@types/node" "*" "@types/qs" "*" @@ -1105,7 +1160,7 @@ "@types/node" "*" form-data "^4.0.0" -"@types/node@*", "@types/node@>=10.0.0", "@types/node@^20.9.0": +"@types/node@*", "@types/node@^20.9.0": version "20.12.12" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.12.tgz#7cbecdf902085cec634fdb362172dfe12b8f2050" integrity sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw== @@ -1117,6 +1172,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== +"@types/node@>=10.0.0": + version "20.12.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.13.tgz#90ed3b8a4e52dd3c5dc5a42dde5b85b74ad8ed88" + integrity sha512-gBGeanV41c1L171rR7wjbMiEpEI/l5XFQdLLfhr/REwpgDy/4U8y89+i8kRiLzDyZdOkXh+cRaTetUnCYutoXA== + dependencies: + undici-types "~5.26.4" + "@types/node@^14.0.1": version "14.18.63" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.63.tgz#1788fa8da838dbb5f9ea994b834278205db6ca2b" @@ -1334,6 +1396,21 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/eslint-plugin@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.12.0.tgz#f87a32e8972b8a60024f2f8f12205e7c8108bc41" + integrity sha512-7F91fcbuDf/d3S8o21+r3ZncGIke/+eWk0EpO21LXhDfLahriZF9CGj4fbAetEjlaBdjdSm9a6VeXbpbT6Z40Q== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "7.12.0" + "@typescript-eslint/type-utils" "7.12.0" + "@typescript-eslint/utils" "7.12.0" + "@typescript-eslint/visitor-keys" "7.12.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + "@typescript-eslint/parser@5.29.0": version "5.29.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.29.0.tgz#41314b195b34d44ff38220caa55f3f93cfca43cf" @@ -1344,6 +1421,17 @@ "@typescript-eslint/typescript-estree" "5.29.0" debug "^4.3.4" +"@typescript-eslint/parser@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.12.0.tgz#8761df3345528b35049353db80010b385719b1c3" + integrity sha512-dm/J2UDY3oV3TKius2OUZIFHsomQmpHtsV0FTh1WO8EKgHLQ1QCADUqscPgTpU+ih1e21FQSRjXckHn3txn6kQ== + dependencies: + "@typescript-eslint/scope-manager" "7.12.0" + "@typescript-eslint/types" "7.12.0" + "@typescript-eslint/typescript-estree" "7.12.0" + "@typescript-eslint/visitor-keys" "7.12.0" + debug "^4.3.4" + "@typescript-eslint/scope-manager@5.29.0": version "5.29.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz#2a6a32e3416cb133e9af8dcf54bf077a916aeed3" @@ -1352,6 +1440,14 @@ "@typescript-eslint/types" "5.29.0" "@typescript-eslint/visitor-keys" "5.29.0" +"@typescript-eslint/scope-manager@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.12.0.tgz#259c014362de72dd34f995efe6bd8dda486adf58" + integrity sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg== + dependencies: + "@typescript-eslint/types" "7.12.0" + "@typescript-eslint/visitor-keys" "7.12.0" + "@typescript-eslint/type-utils@5.29.0": version "5.29.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz#241918001d164044020b37d26d5b9f4e37cc3d5d" @@ -1361,11 +1457,26 @@ debug "^4.3.4" tsutils "^3.21.0" +"@typescript-eslint/type-utils@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.12.0.tgz#9dfaaa1972952f395ec5be4f5bbfc4d3cdc63908" + integrity sha512-lib96tyRtMhLxwauDWUp/uW3FMhLA6D0rJ8T7HmH7x23Gk1Gwwu8UZ94NMXBvOELn6flSPiBrCKlehkiXyaqwA== + dependencies: + "@typescript-eslint/typescript-estree" "7.12.0" + "@typescript-eslint/utils" "7.12.0" + debug "^4.3.4" + ts-api-utils "^1.3.0" + "@typescript-eslint/types@5.29.0": version "5.29.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.29.0.tgz#7861d3d288c031703b2d97bc113696b4d8c19aab" integrity sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg== +"@typescript-eslint/types@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.12.0.tgz#bf208f971a8da1e7524a5d9ae2b5f15192a37981" + integrity sha512-o+0Te6eWp2ppKY3mLCU+YA9pVJxhUJE15FV7kxuD9jgwIAa+w/ycGJBMrYDTpVGUM/tgpa9SeMOugSabWFq7bg== + "@typescript-eslint/typescript-estree@5.29.0": version "5.29.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz#e83d19aa7fd2e74616aab2f25dfbe4de4f0b5577" @@ -1379,6 +1490,20 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.12.0.tgz#e6c1074f248b3db6573ab6a7c47a39c4cd498ff9" + integrity sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ== + dependencies: + "@typescript-eslint/types" "7.12.0" + "@typescript-eslint/visitor-keys" "7.12.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + "@typescript-eslint/utils@5.29.0": version "5.29.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.29.0.tgz#775046effd5019667bd086bcf326acbe32cd0082" @@ -1391,6 +1516,16 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" +"@typescript-eslint/utils@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.12.0.tgz#c6e58fd7f724cdccc848f71e388ad80cbdb95dd0" + integrity sha512-Y6hhwxwDx41HNpjuYswYp6gDbkiZ8Hin9Bf5aJQn1bpTs3afYY4GX+MPYxma8jtoIV2GRwTM/UJm/2uGCVv+DQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.12.0" + "@typescript-eslint/types" "7.12.0" + "@typescript-eslint/typescript-estree" "7.12.0" + "@typescript-eslint/visitor-keys@5.29.0": version "5.29.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz#7a4749fa7ef5160c44a451bf060ac1dc6dfb77ee" @@ -1399,6 +1534,14 @@ "@typescript-eslint/types" "5.29.0" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@7.12.0": + version "7.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.12.0.tgz#c053b55a996679528beeedd8e565710ce1ae1ad3" + integrity sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ== + dependencies: + "@typescript-eslint/types" "7.12.0" + eslint-visitor-keys "^3.4.3" + "@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" @@ -1664,7 +1807,7 @@ acorn@^7.0.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.11.3, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -2015,7 +2158,7 @@ aws-sdk@2.1061.0: uuid "3.3.2" xml2js "0.4.19" -axios@1.6.8, axios@^1.2.1: +axios@1.6.8: version "1.6.8" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== @@ -2024,6 +2167,20 @@ axios@1.6.8, axios@^1.2.1: form-data "^4.0.0" proxy-from-env "^1.1.0" +axios@^1.2.1: + version "1.7.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" + integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +b4a@^1.6.4: + version "1.6.6" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.6.tgz#a4cc349a3851987c3c4ac2d7785c18744f6da9ba" + integrity sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg== + backbone@1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.3.3.tgz#4cc80ea7cb1631ac474889ce40f2f8bc683b2999" @@ -2037,9 +2194,9 @@ balanced-match@^1.0.0: integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== bare-events@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.2.2.tgz#a98a41841f98b2efe7ecc5c5468814469b018078" - integrity sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ== + version "2.3.1" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.3.1.tgz#5af2ee0be9578f81e3c1aa9bc3a6a2bcf22307ce" + integrity sha512-sJnSOTVESURZ61XgEleqmP255T6zTYwHPwE4r6SssIh0U9/uDvfpdoJYpVUerJJZH2fueO+CdT8ZT+OC/7aZDA== base64-js@^1.0.2, base64-js@^1.2.0, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" @@ -2205,13 +2362,20 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" +braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -2578,9 +2742,9 @@ camelcase@^6.0.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001587: - version "1.0.30001620" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz#78bb6f35b8fe315b96b8590597094145d0b146b4" - integrity sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew== + version "1.0.30001625" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001625.tgz#ead1b155ea691d6a87938754d3cb119c24465b03" + integrity sha512-4KE9N2gcRH+HQhpeiRZXd+1niLB/XNLAhSy4z7fI8EzcbcPoAqjNInxVHTiTwWfTIV4w096XG8OtCOCQQKPv3w== chai-as-promised@7.1.1: version "7.1.1" @@ -2703,9 +2867,9 @@ chownr@^2.0.0: integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== chromedriver@110.0.0: version "110.0.0" @@ -2907,6 +3071,11 @@ commander@9.3.0: resolved "https://registry.yarnpkg.com/commander/-/commander-9.3.0.tgz#f619114a5a2d2054e0d9ff1b31d5ccf89255e26b" integrity sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw== +commander@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + commander@^2.11.0, commander@^2.12.2, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -3232,7 +3401,7 @@ debug@2.6.9, debug@^2.6.8: dependencies: ms "2.0.0" -debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1: +debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -3253,6 +3422,13 @@ debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.0.1, debug@^4.3.0, debug@^4.3.2, debug@^4.3.3, debug@~4.3.1: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -3590,9 +3766,9 @@ electron-publish@23.6.0: mime "^2.5.2" electron-to-chromium@^1.4.668: - version "1.4.774" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.774.tgz#1017d1758aaeeefe5423aa9d67b4b1e5d1d0a856" - integrity sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg== + version "1.4.787" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.787.tgz#3eedd0a3b8be2b9e96e21675d399786ad90b99ed" + integrity sha512-d0EFmtLPjctczO3LogReyM2pbBiiZbnsKnGF+cdZhsYzHm/A0GV7W94kqzLD8SN4O3f3iHlgLUChqghgyznvCQ== electron-updater@^5.3.0: version "5.3.0" @@ -3937,6 +4113,14 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" +eslint-scope@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.1.tgz#a9601e4b81a0b9171657c343fb13111688963cfc" + integrity sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-utils@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" @@ -3949,11 +4133,16 @@ eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== +eslint-visitor-keys@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" + integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== + eslint@8.18.0: version "8.18.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.18.0.tgz#78d565d16c993d0b73968c523c0446b13da784fd" @@ -3995,6 +4184,55 @@ eslint@8.18.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +eslint@9.x: + version "9.4.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.4.0.tgz#79150c3610ae606eb131f1d648d5f43b3d45f3cd" + integrity sha512-sjc7Y8cUD1IlwYcTS9qPSvGjAC8Ne9LctpxKKu3x/1IC9bnOg98Zy6GxEJUfr1NojMgVPlyANXYns8oE2c1TAA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/config-array" "^0.15.1" + "@eslint/eslintrc" "^3.1.0" + "@eslint/js" "9.4.0" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.3.0" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.0.1" + eslint-visitor-keys "^4.0.0" + espree "^10.0.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.0.1.tgz#600e60404157412751ba4a6f3a2ee1a42433139f" + integrity sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww== + dependencies: + acorn "^8.11.3" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.0.0" + espree@^9.3.2, espree@^9.4.0: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -4014,7 +4252,7 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.0: +esquery@^1.4.0, esquery@^1.4.2: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== @@ -4182,7 +4420,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-fifo@^1.1.0: +fast-fifo@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== @@ -4251,6 +4489,13 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + file-type@16.5.4: version "16.5.4" resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd" @@ -4274,6 +4519,13 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + filter-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" @@ -4292,7 +4544,7 @@ finalhandler@1.2.0: statuses "2.0.1" unpipe "~1.0.0" -find-up@5.0.0: +find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== @@ -4324,6 +4576,14 @@ flat-cache@^3.0.4: keyv "^4.5.3" rimraf "^3.0.2" +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + flat@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" @@ -4648,6 +4908,16 @@ globals@^13.15.0, globals@^13.19.0: dependencies: type-fest "^0.20.2" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^15.4.0: + version "15.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.4.0.tgz#3e36ea6e4d9ddcf1cb42d92f5c4a145a8a2ddc1c" + integrity sha512-unnwvMZpv0eDUyjNyh9DH/yxUaRYrEjW/qK4QcdrHg3oO11igUQrCSgODHEqxlKg8v2CD2Sd7UkqqEBoz5U7TQ== + globalthis@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" @@ -4749,6 +5019,11 @@ grainjs@1.0.2: resolved "https://registry.yarnpkg.com/grainjs/-/grainjs-1.0.2.tgz#5ab9b03de21bdff8da0f98408276fcbd31de8f17" integrity sha512-wrj8TqpgxTGOKHpTlMBxMeX2uS3lTvXj4ROLKC+EZNM7J6RHQLGjMzMqWtiryBnMhGIBlbCicMNFppCrK1zv9w== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + gtoken@^5.0.4: version "5.3.2" resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.3.2.tgz#deb7dc876abe002178e0515e383382ea9446d58f" @@ -5041,9 +5316,9 @@ i18next-scanner@4.4.0: vinyl-fs "^4.0.0" i18next@*: - version "23.11.4" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.11.4.tgz#3f0e620fd2cff3825324191615d0ab0a1eec3baf" - integrity sha512-CCUjtd5TfaCl+mLUzAA0uPSN+AVn4fP/kWCYt/hocPUwusTpMVczdrRyOBUwk6N05iH40qiKx6q1DoNJtBIwdg== + version "23.11.5" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.11.5.tgz#d71eb717a7e65498d87d0594f2664237f9e361ef" + integrity sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA== dependencies: "@babel/runtime" "^7.23.2" @@ -5098,7 +5373,7 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== -ignore@^5.2.0: +ignore@^5.2.0, ignore@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== @@ -5296,6 +5571,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" @@ -5637,7 +5917,7 @@ jws@^4.0.0: jwa "^2.0.0" safe-buffer "^5.0.1" -keyv@^4.0.0, keyv@^4.5.3: +keyv@^4.0.0, keyv@^4.5.3, keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -6055,7 +6335,7 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -6069,6 +6349,13 @@ minimatch@^5.0.1, minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.4: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -6576,7 +6863,7 @@ openid-client@5.6.1: object-hash "^2.2.0" oidc-token-hash "^5.0.3" -optionator@^0.9.1: +optionator@^0.9.1, optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== @@ -7529,7 +7816,7 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.4: +semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.5.4, semver@^7.6.0: version "7.6.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== @@ -7891,12 +8178,13 @@ stream-transform@^3.3.1: integrity sha512-v64PUnPy9Qw94NGuaEMo+9RHQe4jTBYf+NkTtqkCgeuiNo8NlL0LtLR7fkKWNVFtp3RhIm5Dlxkgm5uz7TDimQ== streamx@^2.12.0, streamx@^2.12.5, streamx@^2.13.2, streamx@^2.14.0: - version "2.16.1" - resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.16.1.tgz#2b311bd34832f08aa6bb4d6a80297c9caef89614" - integrity sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ== + version "2.18.0" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.18.0.tgz#5bc1a51eb412a667ebfdcd4e6cf6a6fc65721ac7" + integrity sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ== dependencies: - fast-fifo "^1.1.0" + fast-fifo "^1.3.2" queue-tick "^1.0.1" + text-decoder "^1.1.0" optionalDependencies: bare-events "^2.2.0" @@ -8105,6 +8393,13 @@ terser@^5.26.0: commander "^2.20.0" source-map-support "~0.5.20" +text-decoder@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.1.0.tgz#3379e728fcf4d3893ec1aea35e8c2cac215ef190" + integrity sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw== + dependencies: + b4a "^1.6.4" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -8254,6 +8549,11 @@ truncate-utf8-bytes@^1.0.0: dependencies: utf8-byte-length "^1.0.1" +ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + ts-interface-builder@0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/ts-interface-builder/-/ts-interface-builder-0.3.2.tgz#664f7f4d2bd0079950ba6bb7cd2780262009a68f" @@ -8359,6 +8659,15 @@ typeorm@0.3.17: uuid "^9.0.0" yargs "^17.6.2" +typescript-eslint@^7.12.0: + version "7.12.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-7.12.0.tgz#e3fd685590ffbf158e13208218f0a122581ed645" + integrity sha512-D6HKNbQcnNu3BaN4HkQCR16tgG8Q2AMUWPgvhrJksOXu+d6ys07yC06ONiV2kcsEfWC22voB6C3PvK2MqlBZ7w== + dependencies: + "@typescript-eslint/eslint-plugin" "7.12.0" + "@typescript-eslint/parser" "7.12.0" + "@typescript-eslint/utils" "7.12.0" + typescript@4.7.4: version "4.7.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"