From f7f76fb5e719592e98c0b949fb436fdb297cad6a Mon Sep 17 00:00:00 2001 From: Paul Fitzpatrick Date: Mon, 13 Feb 2023 15:52:17 -0500 Subject: [PATCH] A set of tweaks to simplify electron packaging (#421) * Replace `ormconfig.js` with a newer mechanism of configuring TypeORM that can be included in the source code properly. The path to `ormconfig.js` has always been awkward to handle, and eliminating the file makes building different Grist setups a bit simpler. * Remove `electron` package. It is barely used, just for some old remnants of an older attempt at electron packaging. It was used for two types, which I left at `any` for now. More code pruning is no doubt possible here, but I'd rather do it when Electron packaging has solidified. * Add a hook for replacing the login system, and for adding some extra middleware the login system may need. * Add support for some more possible locations of Python, which arise when a standalone version of it is included in the Electron package. This isn't very general purpose, just configurations that I found useful. * Support using grist-core within a yarn workspace - the only tweak needed was webpack related. * Allow an external ID to be optionally associated with documents. --- Dockerfile | 1 - app/client/lib/SafeBrowser.ts | 3 +- app/client/lib/uploads.ts | 11 +- app/common/UserAPI.ts | 2 + app/gen-server/entity/Document.ts | 3 + app/server/lib/FlexServer.ts | 25 ++++- app/server/lib/GristServer.ts | 2 + app/server/lib/MinimalLogin.ts | 2 +- app/server/lib/NSandbox.ts | 28 +++-- app/server/lib/dbUtils.ts | 51 ++++++++- app/server/lib/places.ts | 26 ++++- app/server/mergedServerMain.ts | 10 +- buildtools/prepare_python.sh | 17 ++- buildtools/prepare_python3.sh | 28 ++++- buildtools/webpack.api.config.js | 8 +- buildtools/webpack.config.js | 8 +- ormconfig.js | 49 --------- package.json | 1 - yarn.lock | 172 ++---------------------------- 19 files changed, 192 insertions(+), 255 deletions(-) delete mode 100644 ormconfig.js diff --git a/Dockerfile b/Dockerfile index 665a7e44..fb803b40 100644 --- a/Dockerfile +++ b/Dockerfile @@ -103,7 +103,6 @@ COPY --from=sandbox /runsc /usr/bin/runsc # Add files needed for running server. ADD package.json /grist/package.json -ADD ormconfig.js /grist/ormconfig.js ADD bower_components /grist/bower_components ADD sandbox /grist/sandbox ADD plugins /grist/plugins diff --git a/app/client/lib/SafeBrowser.ts b/app/client/lib/SafeBrowser.ts index e980d5bb..e0d01a7b 100644 --- a/app/client/lib/SafeBrowser.ts +++ b/app/client/lib/SafeBrowser.ts @@ -40,7 +40,6 @@ import { getOriginUrl } from 'app/common/urlUtils'; import { GristAPI, RPC_GRISTAPI_INTERFACE } from 'app/plugin/GristAPI'; import { RenderOptions, RenderTarget } from 'app/plugin/RenderOptions'; import { checkers } from 'app/plugin/TypeCheckers'; -import { IpcMessageEvent } from 'electron'; import { IMsgCustom, IMsgRpcCall, Rpc } from 'grain-rpc'; import { Disposable } from './dispose'; const G = getBrowserGlobals('document', 'window'); @@ -316,7 +315,7 @@ class WebviewProcess extends ViewProcess { // TODO: find a way for keyboard events to play nice when webviews are non-modal. Mousetrap.setPaused(true); this.autoDisposeCallback(() => Mousetrap.setPaused(false)); - webview.addEventListener('ipc-message', (event: IpcMessageEvent) => { + webview.addEventListener('ipc-message', (event: any /* IpcMessageEvent */) => { // The event object passed to the listener is missing proper documentation. In the examples // listed in https://electronjs.org/docs/api/ipc-main the arguments should be passed to the // listener after the event object, but this is not happening here. Only we know it is a diff --git a/app/client/lib/uploads.ts b/app/client/lib/uploads.ts index a7df3e66..29d80fa2 100644 --- a/app/client/lib/uploads.ts +++ b/app/client/lib/uploads.ts @@ -14,7 +14,6 @@ import {GristLoadConfig} from 'app/common/gristUrls'; import {byteString, safeJsonParse} from 'app/common/gutil'; import {FetchUrlOptions, UPLOAD_URL_PATH, UploadResult} from 'app/common/uploads'; import {docUrl} from 'app/common/urlUtils'; -import {OpenDialogOptions} from 'electron'; import noop = require('lodash/noop'); import trimStart = require('lodash/trimStart'); import {basename} from 'path'; // made available by webpack using path-browserify module. @@ -69,18 +68,18 @@ function getFileDialogOptions(options: SelectFileOptions): FileDialogOptions { } // Helper to convert SelectFileOptions to electron's OpenDialogOptions. -function getElectronOptions(options: SelectFileOptions): OpenDialogOptions { - const resOptions: OpenDialogOptions = { - filters: [], +function getElectronOptions(options: SelectFileOptions) /*: OpenDialogOptions */ { + const resOptions /*: OpenDialogOptions*/ = { + filters: [] as Array<{name: string, extensions: any}>, properties: ['openFile'], }; if (options.extensions) { // Electron does not expect leading period. const extensions = options.extensions.map(e => trimStart(e, '.')); - resOptions.filters!.push({name: 'Select files', extensions}); + resOptions.filters.push({name: 'Select files', extensions}); } if (options.multiple) { - resOptions.properties!.push('multiSelections'); + resOptions.properties.push('multiSelections'); } return resOptions; } diff --git a/app/common/UserAPI.ts b/app/common/UserAPI.ts index ad9f9a79..780a7c70 100644 --- a/app/common/UserAPI.ts +++ b/app/common/UserAPI.ts @@ -115,6 +115,8 @@ export interface DocumentOptions { description?: string|null; icon?: string|null; openMode?: OpenDocMode|null; + externalId?: string|null; // A slot for storing an externally maintained id. + // Not used in grist-core, but handy for Electron app. } export interface DocumentProperties extends CommonProperties { diff --git a/app/gen-server/entity/Document.ts b/app/gen-server/entity/Document.ts index 3b3101c0..38c163ad 100644 --- a/app/gen-server/entity/Document.ts +++ b/app/gen-server/entity/Document.ts @@ -99,6 +99,9 @@ export class Document extends Resource { if (props.options.icon !== undefined) { this.options.icon = sanitizeIcon(props.options.icon); } + if (props.options.externalId !== undefined) { + this.options.externalId = props.options.externalId; + } // Normalize so that null equates with absence. for (const key of Object.keys(this.options) as Array) { if (this.options[key] === null) { diff --git a/app/server/lib/FlexServer.ts b/app/server/lib/FlexServer.ts index 111d3132..eadae1d3 100644 --- a/app/server/lib/FlexServer.ts +++ b/app/server/lib/FlexServer.ts @@ -36,7 +36,8 @@ import {DocWorkerInfo, IDocWorkerMap} from 'app/server/lib/DocWorkerMap'; import {expressWrap, jsonErrorHandler, secureJsonErrorHandler} from 'app/server/lib/expressWrap'; import {Hosts, RequestWithOrg} from 'app/server/lib/extractOrg'; import {addGoogleAuthEndpoint} from "app/server/lib/GoogleAuth"; -import {DocTemplate, GristLoginMiddleware, GristServer, RequestWithGrist} from 'app/server/lib/GristServer'; +import {DocTemplate, GristLoginMiddleware, GristLoginSystem, GristServer, + RequestWithGrist} from 'app/server/lib/GristServer'; import {initGristSessions, SessionStore} from 'app/server/lib/gristSessions'; import {HostedStorageManager} from 'app/server/lib/HostedStorageManager'; import {IBilling} from 'app/server/lib/IBilling'; @@ -157,6 +158,7 @@ export class FlexServer implements GristServer { private _getSignUpRedirectUrl: (req: express.Request, target: URL) => Promise; private _getLogoutRedirectUrl: (req: express.Request, nextUrl: URL) => Promise; private _sendAppPage: (req: express.Request, resp: express.Response, options: ISendAppPageOptions) => Promise; + private _getLoginSystem?: () => Promise; constructor(public port: number, public name: string = 'flexServer', public readonly options: FlexServerOptions = {}) { @@ -233,6 +235,11 @@ export class FlexServer implements GristServer { }); } + // Allow overridding the login system. + public setLoginSystem(loginSystem: () => Promise) { + this._getLoginSystem = loginSystem; + } + public getHost(): string { return `${this.host}:${this.getOwnPort()}`; } @@ -481,12 +488,19 @@ export class FlexServer implements GristServer { this.app.use(/^\/help\//, expressWrap(async (req, res) => { res.redirect('https://support.getgrist.com'); })); + // If there is a directory called "static_ext", serve material from there + // as well. This isn't used in grist-core but is handy for extensions such + // as an Electron app. + const staticExtDir = getAppPathTo(this.appRoot, 'static') + '_ext'; + const staticExtApp = fse.existsSync(staticExtDir) ? + express.static(staticExtDir, options) : null; const staticApp = express.static(getAppPathTo(this.appRoot, 'static'), options); const bowerApp = express.static(getAppPathTo(this.appRoot, 'bower_components'), options); if (process.env.GRIST_LOCALES_DIR) { const locales = express.static(process.env.GRIST_LOCALES_DIR, options); this.app.use("/locales", this.tagChecker.withTag(locales)); } + if (staticExtApp) { this.app.use(this.tagChecker.withTag(staticExtApp)); } this.app.use(this.tagChecker.withTag(staticApp)); this.app.use(this.tagChecker.withTag(bowerApp)); } @@ -700,7 +714,7 @@ export class FlexServer implements GristServer { this.addOrg(); // Create the sessionStore and related objects. - const {sessions, sessionMiddleware, sessionStore} = initGristSessions(this.instanceRoot, this); + const {sessions, sessionMiddleware, sessionStore} = initGristSessions(getUnpackedAppRoot(this.instanceRoot), this); this.app.use(sessionMiddleware); this.app.use(signInStatusMiddleware); @@ -901,11 +915,16 @@ export class FlexServer implements GristServer { // TODO: We could include a third mock provider of login/logout URLs for better tests. Or we // could create a mock SAML identity provider for testing this using the SAML flow. - const loginSystem = await (process.env.GRIST_TEST_LOGIN ? getTestLoginSystem() : getLoginSystem()); + const loginSystem = await (process.env.GRIST_TEST_LOGIN ? getTestLoginSystem() : + (this._getLoginSystem?.() || getLoginSystem())); this._loginMiddleware = await loginSystem.getMiddleware(this); this._getLoginRedirectUrl = tbind(this._loginMiddleware.getLoginRedirectUrl, this._loginMiddleware); this._getSignUpRedirectUrl = tbind(this._loginMiddleware.getSignUpRedirectUrl, this._loginMiddleware); this._getLogoutRedirectUrl = tbind(this._loginMiddleware.getLogoutRedirectUrl, this._loginMiddleware); + const wildcardMiddleware = this._loginMiddleware.getWildcardMiddleware?.(); + if (wildcardMiddleware?.length) { + this.app.use(wildcardMiddleware); + } } public addComm() { diff --git a/app/server/lib/GristServer.ts b/app/server/lib/GristServer.ts index 500fb80a..96847707 100644 --- a/app/server/lib/GristServer.ts +++ b/app/server/lib/GristServer.ts @@ -62,6 +62,8 @@ export interface GristLoginMiddleware { getLoginOrSignUpMiddleware?(): express.RequestHandler[]; // Optional middleware for the GET /logout route. getLogoutMiddleware?(): express.RequestHandler[]; + // Optional middleware for all routes. + getWildcardMiddleware?(): express.RequestHandler[]; // Returns arbitrary string for log. addEndpoints(app: express.Express): Promise; // Optionally, extract profile from request. Result can be a profile, diff --git a/app/server/lib/MinimalLogin.ts b/app/server/lib/MinimalLogin.ts index 8d004262..c1e75939 100644 --- a/app/server/lib/MinimalLogin.ts +++ b/app/server/lib/MinimalLogin.ts @@ -40,7 +40,7 @@ export async function getMinimalLoginSystem(): Promise { }; } -function getDefaultProfile(): UserProfile { +export function getDefaultProfile(): UserProfile { return { email: process.env.GRIST_DEFAULT_EMAIL || 'you@example.com', name: 'You', diff --git a/app/server/lib/NSandbox.ts b/app/server/lib/NSandbox.ts index f84ab8cb..aff564f8 100644 --- a/app/server/lib/NSandbox.ts +++ b/app/server/lib/NSandbox.ts @@ -5,6 +5,7 @@ import {arrayToString} from 'app/common/arrayToString'; import * as marshal from 'app/common/marshal'; import {ISandbox, ISandboxCreationOptions, ISandboxCreator} from 'app/server/lib/ISandbox'; import log from 'app/server/lib/log'; +import {getAppRoot, getAppRootFor, getUnpackedAppRoot} from 'app/server/lib/places'; import { DirectProcessControl, ISandboxControl, @@ -575,7 +576,7 @@ function gvisor(options: ISandboxOptions): SandboxProcess { // Check for local virtual environments created with core's // install:python2 or install:python3 targets. They'll need // some extra sharing to make available in the sandbox. - const venv = path.join(process.cwd(), + const venv = path.join(getAppRootFor(getAppRoot(), 'sandbox'), pythonVersion === '2' ? 'venv' : 'sandbox_venv3'); if (fs.existsSync(venv)) { wrapperArgs.addMount(venv); @@ -869,19 +870,24 @@ function findPython(command: string|undefined, preferredVersion?: string) { // TODO: rationalize this, it is a product of haphazard growth. const prefs = preferredVersion === '2' ? ['venv', 'sandbox_venv3'] : ['sandbox_venv3', 'venv']; for (const venv of prefs) { - const pythonPath = path.join(process.cwd(), venv, 'bin', 'python'); - if (fs.existsSync(pythonPath)) { - command = pythonPath; - break; + const base = getUnpackedAppRoot(); + // Try a battery of possible python executable paths when python is installed + // in a standalone directory. + // This battery of possibilities comes from Electron packaging, where python + // is bundled with Grist. Not all the possibilities are needed (there are + // multiple popular python bundles per OS). + for (const possiblePath of [['bin', 'python'], ['bin', 'python3'], + ['Scripts', 'python.exe'], ['python.exe']] as const) { + const pythonPath = path.join(base, venv, ...possiblePath); + if (fs.existsSync(pythonPath)) { + return pythonPath; + } } } // Fall back on system python. - if (!command) { - command = which.sync(preferredVersion === '2' ? 'python2' : 'python3', {nothrow: true}) - || which.sync(preferredVersion === '2' ? 'python2.7' : 'python3.9', {nothrow: true}) - || which.sync('python'); - } - return command; + return which.sync(preferredVersion === '2' ? 'python2' : 'python3', {nothrow: true}) + || which.sync(preferredVersion === '2' ? 'python2.7' : 'python3.9', {nothrow: true}) + || which.sync('python'); } /** diff --git a/app/server/lib/dbUtils.ts b/app/server/lib/dbUtils.ts index facfa162..be299b3b 100644 --- a/app/server/lib/dbUtils.ts +++ b/app/server/lib/dbUtils.ts @@ -1,6 +1,7 @@ import {synchronizeProducts} from 'app/gen-server/entity/Product'; +import {codeRoot} from 'app/server/lib/places'; import {Mutex} from 'async-mutex'; -import {Connection, createConnection, getConnection} from 'typeorm'; +import {Connection, createConnection, DataSourceOptions, getConnection} from 'typeorm'; // Summary of migrations found in database and in code. interface MigrationSummary { @@ -61,7 +62,7 @@ export async function getOrCreateConnection(): Promise { if (!String(e).match(/ConnectionNotFoundError/)) { throw e; } - const connection = await createConnection(); + const connection = await createConnection(getTypeORMSettings()); // When using Sqlite, set a busy timeout of 3s to tolerate a little // interference from connections made by tests. Logging doesn't show // any particularly slow queries, but bad luck is possible. @@ -98,3 +99,49 @@ export async function undoLastMigration(connection: Connection) { }); if (sqlite) { await connection.query("PRAGMA foreign_keys = ON;"); } } + +// Replace the old janky ormconfig.js file, which was always a source of +// pain to use since it wasn't properly integrated into the typescript +// project. +function getTypeORMSettings(): DataSourceOptions { + // If we have a redis server available, tell typeorm. Then any queries built with + // .cache() called on them will be cached via redis. + // We use a separate environment variable for the moment so that we don't have to + // enable this until we really need it. + const redisUrl = process.env.TYPEORM_REDIS_URL ? new URL(process.env.TYPEORM_REDIS_URL) : undefined; + const cache = redisUrl ? { + cache: { + type: "redis", + options: { + host: redisUrl.hostname, + port: parseInt(redisUrl.port || "6379", 10) + } + } as const + } : undefined; + + return { + "name": process.env.TYPEORM_NAME || "default", + "type": (process.env.TYPEORM_TYPE as any) || "sqlite", // officially, TYPEORM_CONNECTION - + // but if we use that, this file will never + // be read, and we can't configure + // caching otherwise. + "database": process.env.TYPEORM_DATABASE || "landing.db", + "username": process.env.TYPEORM_USERNAME || undefined, + "password": process.env.TYPEORM_PASSWORD || undefined, + "host": process.env.TYPEORM_HOST || undefined, + "port": process.env.TYPEORM_PORT ? parseInt(process.env.TYPEORM_PORT, 10) : undefined, + "synchronize": false, + "migrationsRun": false, + "logging": process.env.TYPEORM_LOGGING === "true", + "entities": [ + `${codeRoot}/app/gen-server/entity/*.js` + ], + "migrations": [ + `${codeRoot}/app/gen-server/migration/*.js` // migration files don't actually get packaged. + ], + "subscribers": [ + `${codeRoot}/app/gen-server/subscriber/*.js` + ], + ...cache, + }; +} diff --git a/app/server/lib/places.ts b/app/server/lib/places.ts index c3316cca..9567db24 100644 --- a/app/server/lib/places.ts +++ b/app/server/lib/places.ts @@ -9,14 +9,25 @@ import * as path from 'path'; */ export const codeRoot = path.dirname(path.dirname(path.dirname(__dirname))); +let _cachedAppRoot: string|undefined; + /** - * Returns the appRoot, i.e. the directory containing ./sandbox, ./node_modules, ./ormconfig.js, + * Returns the appRoot, i.e. the directory containing ./sandbox, ./node_modules, * etc. */ export function getAppRoot(): string { + if (_cachedAppRoot) { return _cachedAppRoot; } + _cachedAppRoot = getAppRootWithoutCaching(); + return _cachedAppRoot; +} + +// Uncached version of getAppRoot() +function getAppRootWithoutCaching(): string { if (process.env.APP_ROOT_PATH) { return process.env.APP_ROOT_PATH; } - if (codeRoot.endsWith('/_build/core')) { return path.dirname(path.dirname(codeRoot)); } - return codeRoot.endsWith('/_build') ? path.dirname(codeRoot) : codeRoot; + if (codeRoot.endsWith('/_build/core') || codeRoot.endsWith('\\_build\\core')) { + return path.dirname(path.dirname(codeRoot)); + } + return (codeRoot.endsWith('/_build') || codeRoot.endsWith('\\_build')) ? path.dirname(codeRoot) : codeRoot; } /** @@ -25,7 +36,14 @@ export function getAppRoot(): string { * which is that .asar file in packaged form, and returns a directory where * remaining files are available on the regular filesystem. */ -export function getUnpackedAppRoot(appRoot: string): string { +export function getUnpackedAppRoot(appRoot: string = getAppRoot()): string { + if (path.basename(appRoot) == 'app.asar') { + return path.resolve(path.dirname(appRoot), 'app.asar.unpacked'); + } + if (path.dirname(appRoot).endsWith('app.asar')) { + return path.resolve(path.dirname(path.dirname(appRoot)), + 'app.asar.unpacked', 'core'); + } return path.resolve(path.dirname(appRoot), path.basename(appRoot, '.asar')); } diff --git a/app/server/mergedServerMain.ts b/app/server/mergedServerMain.ts index f31691cd..06d9b266 100644 --- a/app/server/mergedServerMain.ts +++ b/app/server/mergedServerMain.ts @@ -6,6 +6,7 @@ */ import {FlexServer, FlexServerOptions} from 'app/server/lib/FlexServer'; +import {GristLoginSystem} from 'app/server/lib/GristServer'; import log from 'app/server/lib/log'; // Allowed server types. We'll start one or a combination based on the value of GRIST_SERVERS @@ -36,13 +37,14 @@ interface ServerOptions extends FlexServerOptions { // logToConsole is set to true) externalStorage?: boolean; // If set, documents saved to external storage such as s3 (default is to check environment // variables, which get set in various ways in dev/test entry points) + loginSystem?: () => Promise; } /** * Start a server on the given port, including the functionality specified in serverTypes. */ export async function main(port: number, serverTypes: ServerType[], - options: ServerOptions = {logToConsole: true}) { + options: ServerOptions = {}) { const includeHome = serverTypes.includes("home"); const includeDocs = serverTypes.includes("docs"); const includeStatic = serverTypes.includes("static"); @@ -50,6 +52,10 @@ export async function main(port: number, serverTypes: ServerType[], const server = new FlexServer(port, `server(${serverTypes.join(",")})`, options); + if (options.loginSystem) { + server.setLoginSystem(options.loginSystem); + } + server.addCleanup(); server.setDirectory(); @@ -58,7 +64,7 @@ export async function main(port: number, serverTypes: ServerType[], server.testAddRouter(); } - if (options.logToConsole) { server.addLogging(); } + if (options.logToConsole !== false) { server.addLogging(); } if (options.externalStorage === false) { server.disableExternalStorage(); } await server.loadConfig(); diff --git a/buildtools/prepare_python.sh b/buildtools/prepare_python.sh index b3be736e..716f979c 100755 --- a/buildtools/prepare_python.sh +++ b/buildtools/prepare_python.sh @@ -2,10 +2,25 @@ set -e +# Use a built-in standalone version of Python if available in a directory +# called python. This is used for Electron packaging. The standalone Python +# will have extra packages installed, and then be moved to a standard location +# (sandbox_venv3). +for possible_path in python/bin/python python/bin/python3 \ + python/Scripts/python.exe python/python.exe; do + if [[ -e $possible_path ]]; then + echo "found $possible_path" + buildtools/prepare_python3.sh $possible_path python + # Make sure Python2 sandbox is not around. + rm -rf venv + exit 0 + fi +done + echo "Use Python3 if available and recent enough, otherwise Python2" if python3 -c 'import sys; assert sys.version_info >= (3,9)' 2> /dev/null; then # Default to python3 if recent enough. - buildtools/prepare_python3.sh + buildtools/prepare_python3.sh python3 # Make sure python2 isn't around. rm -rf venv else diff --git a/buildtools/prepare_python3.sh b/buildtools/prepare_python3.sh index a6a2c1c8..d84e6edd 100755 --- a/buildtools/prepare_python3.sh +++ b/buildtools/prepare_python3.sh @@ -1,12 +1,32 @@ #!/usr/bin/env bash +# Prepare a Python3 sandbox in the sandbox_venv3 directory. +# Optionally, can be called with the command to use for Python, +# and the directory of a standalone version of Python to incorporate. + set -e -echo "Making Python3 sandbox" -if [ ! -e sandbox_venv3 ]; then - python3 -m venv sandbox_venv3 +if [[ -e sandbox_venv3 ]]; then + echo "Have Python3 sandbox" + exit 0 +fi + +python="$1" +python_dir="$2" +if [[ "$python_dir" = "" ]]; then + python=python3 + pip=sandbox_venv3/bin/pip + echo "Making Python3 sandbox" + $python -m venv sandbox_venv3 +else + pip="$python -m pip" fi echo "Updating Python3 packages" -sandbox_venv3/bin/pip install --no-deps -r sandbox/requirements3.txt +$pip install --no-deps -r sandbox/requirements3.txt + +if [[ ! -e sandbox_venv3 ]]; then + echo "Moving $python_dir to sandbox_venv3" + mv $python_dir sandbox_venv3 +fi echo "Python3 packages ready in sandbox_venv3" diff --git a/buildtools/webpack.api.config.js b/buildtools/webpack.api.config.js index e14f973c..d0ee3398 100644 --- a/buildtools/webpack.api.config.js +++ b/buildtools/webpack.api.config.js @@ -1,5 +1,10 @@ const path = require('path'); +// Get path to top-level node_modules if in a yarn workspace. +// Otherwise node_modules one level up won't get resolved. +// This is used in Electron packaging. +const base = path.dirname(path.dirname(require.resolve('grainjs/package.json'))); + module.exports = { target: 'web', entry: { @@ -18,7 +23,8 @@ module.exports = { path.resolve('.'), path.resolve('./ext'), path.resolve('./stubs'), - path.resolve('./node_modules') + path.resolve('./node_modules'), + base, ], fallback: { 'path': require.resolve("path-browserify"), diff --git a/buildtools/webpack.config.js b/buildtools/webpack.config.js index 8a0a6922..26c2832e 100644 --- a/buildtools/webpack.config.js +++ b/buildtools/webpack.config.js @@ -2,6 +2,11 @@ const MomentLocalesPlugin = require('moment-locales-webpack-plugin'); const { ProvidePlugin } = require('webpack'); const path = require('path'); +// Get path to top-level node_modules if in a yarn workspace. +// Otherwise node_modules one level up won't get resolved. +// This is used in Electron packaging. +const base = path.dirname(path.dirname(require.resolve('grainjs/package.json'))); + module.exports = { target: 'web', entry: { @@ -39,7 +44,8 @@ module.exports = { path.resolve('.'), path.resolve('./ext'), path.resolve('./stubs'), - path.resolve('./node_modules') + path.resolve('./node_modules'), + base, ], fallback: { 'path': require.resolve("path-browserify"), diff --git a/ormconfig.js b/ormconfig.js deleted file mode 100644 index 5245c4ee..00000000 --- a/ormconfig.js +++ /dev/null @@ -1,49 +0,0 @@ -// Cache configuration for typeorm does not seem available via ormconfig.env, so -// we use ormconfig.js style. - -const {codeRoot} = require('app/server/lib/places'); - -module.exports = { - "name": process.env.TYPEORM_NAME || "default", - "type": process.env.TYPEORM_TYPE || "sqlite", // officially, TYPEORM_CONNECTION - - // but if we use that, this file will never - // be read, and we can't configure - // caching otherwise. - "database": process.env.TYPEORM_DATABASE || "landing.db", - "username": process.env.TYPEORM_USERNAME || null, - "password": process.env.TYPEORM_PASSWORD || null, - "host": process.env.TYPEORM_HOST || null, - "port": process.env.TYPEORM_PORT || null, - "synchronize": false, - "migrationsRun": false, - "logging": process.env.TYPEORM_LOGGING === "true", - "entities": [ - `${codeRoot}/app/gen-server/entity/*.js` - ], - "migrations": [ - `${codeRoot}/app/gen-server/migration/*.js` // migration files don't actually get packaged. - ], - "subscribers": [ - `${codeRoot}/app/gen-server/subscriber/*.js` - ], - "cli": { - "entitiesDir": `${codeRoot}/app/gen-server/entity`, - "migrationsDir": `${codeRoot}/app/gen-server/migration`, - "subscribersDir": `${codeRoot}/app/gen-server/subscriber` - } -}; - -// If we have a redis server available, tell typeorm. Then any queries built with -// .cache() called on them will be cached via redis. -// We use a separate environment variable for the moment so that we don't have to -// enable this until we really need it. -if (process.env.TYPEORM_REDIS_URL) { - const url = require('url').parse(process.env.TYPEORM_REDIS_URL); - module.exports.cache = { - type: "redis", - options: { - host: url.hostname, - port: parseInt(url.port || "6379", 10) - } - }; -} diff --git a/package.json b/package.json index 9f3deb5d..30c160a1 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,6 @@ "csv": "4.0.0", "diff-match-patch": "1.0.5", "double-ended-queue": "2.1.0-0", - "electron": "19.0.9", "exceljs": "4.2.1", "express": "4.16.4", "file-type": "16.5.4", diff --git a/yarn.lock b/yarn.lock index 32d961c0..86d0e89e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -218,22 +218,6 @@ resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@electron/get@^1.14.1": - version "1.14.1" - resolved "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz" - integrity sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw== - dependencies: - debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^9.6.0" - progress "^2.0.3" - semver "^6.2.0" - sumchecker "^3.0.1" - optionalDependencies: - global-agent "^3.0.0" - global-tunnel-ng "^2.7.1" - "@eslint/eslintrc@^1.3.0": version "1.4.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" @@ -831,11 +815,6 @@ resolved "https://registry.npmjs.org/@types/node/-/node-14.17.6.tgz" integrity sha512-iBxsxU7eswQDGhlr3AiamBxOssaYxbM+NKXVil8jg9yFXvrfEFbDumLD/2dMTB+zYyg7w+Xjt8yuxfdbUHAtcQ== -"@types/node@^16.11.26": - version "16.11.58" - resolved "https://registry.npmjs.org/@types/node/-/node-16.11.58.tgz" - integrity sha512-uMVxJ111wpHzkx/vshZFb6Qni3BOMnlWLq7q9jrwej7Yw/KvjsEbpxCCxw+hLKxexFMc8YmpG8J9tnEe/rKsIg== - "@types/parse5@*": version "7.0.0" resolved "https://registry.npmjs.org/@types/parse5/-/parse5-7.0.0.tgz" @@ -1780,11 +1759,6 @@ body-parser@1.18.3: raw-body "2.3.3" type-is "~1.6.16" -boolean@^3.0.1: - version "3.2.0" - resolved "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz" - integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== - bootstrap-datepicker@1.9.0: version "1.9.0" resolved "https://registry.npmjs.org/bootstrap-datepicker/-/bootstrap-datepicker-1.9.0.tgz" @@ -2539,14 +2513,6 @@ concat-stream@~1.5.0, concat-stream@~1.5.1: readable-stream "~2.0.0" typedarray "~0.0.5" -config-chain@^1.1.11: - version "1.1.13" - resolved "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz" - integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - configstore@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz" @@ -2979,11 +2945,6 @@ detect-libc@^2.0.0: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - detective@^4.0.0: version "4.7.1" resolved "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz" @@ -3126,15 +3087,6 @@ electron-to-chromium@^1.4.251: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== -electron@19.0.9: - version "19.0.9" - resolved "https://registry.npmjs.org/electron/-/electron-19.0.9.tgz" - integrity sha512-ooEwrv8Y7NSzdhKcl6kPCYecnzcg5nFWuS5ryG+VFH3MMBR8zXh9nW2wLsZrBz6OGUxXrcc5BKBC7dA8C6RhGQ== - dependencies: - "@electron/get" "^1.14.1" - "@types/node" "^16.11.26" - extract-zip "^1.0.3" - elliptic@^6.0.0, elliptic@^6.5.2: version "6.5.4" resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" @@ -3168,7 +3120,7 @@ encode-utf8@^1.0.3: resolved "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz" integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw== -encodeurl@^1.0.2, encodeurl@~1.0.2: +encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= @@ -3207,7 +3159,7 @@ entities@^4.3.0: env-paths@^2.2.0: version "2.2.1" - resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== envinfo@^7.7.3: @@ -3664,7 +3616,7 @@ extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -extract-zip@^1.0.3, extract-zip@^1.6.7: +extract-zip@^1.6.7: version "1.7.0" resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz" integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== @@ -3921,7 +3873,7 @@ fs-extra@^4.0.2, fs-extra@^4.0.3: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^8.0.1, fs-extra@^8.1.0: +fs-extra@^8.0.1: version "8.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== @@ -4192,18 +4144,6 @@ glob@~3.2.7: inherits "2" minimatch "0.3" -global-agent@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz" - integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== - dependencies: - boolean "^3.0.1" - es6-error "^4.1.1" - matcher "^3.0.0" - roarr "^2.15.3" - semver "^7.3.2" - serialize-error "^7.0.1" - global-dirs@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz" @@ -4211,16 +4151,6 @@ global-dirs@^2.0.1: dependencies: ini "^1.3.5" -global-tunnel-ng@^2.7.1: - version "2.7.1" - resolved "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz" - integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== - dependencies: - encodeurl "^1.0.2" - lodash "^4.17.10" - npm-conf "^1.1.3" - tunnel "^0.0.6" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -4233,13 +4163,6 @@ globals@^13.15.0, globals@^13.19.0: dependencies: type-fest "^0.20.2" -globalthis@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== - dependencies: - define-properties "^1.1.3" - globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -4793,11 +4716,6 @@ inherits@2.0.3: resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4: - version "1.3.8" - resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - ini@^1.3.5, ini@~1.3.0: version "1.3.7" resolved "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz" @@ -5331,7 +5249,7 @@ json-stream@^1.0.0: resolved "https://registry.yarnpkg.com/json-stream/-/json-stream-1.0.0.tgz#1a3854e28d2bbeeab31cc7ddf683d2ddc5652708" integrity sha512-H/ZGY0nIAg3QcOwE1QN/rK/Fa7gJn7Ii5obwp6zyPO4xiPNwpIMjqy2gwjBEGqzkF/vSWEIBQCBuN19hYiL6Qg== -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -5671,7 +5589,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@4.17.21, lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: +lodash@4.17.21, lodash@^4.0.0, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5753,13 +5671,6 @@ make-fetch-happen@^9.1.0: socks-proxy-agent "^6.0.0" ssri "^8.0.0" -matcher@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz" - integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== - dependencies: - escape-string-regexp "^4.0.0" - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz" @@ -6334,14 +6245,6 @@ now-and-later@^2.0.0: dependencies: once "^1.3.2" -npm-conf@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - npm-run-path@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz" @@ -6906,11 +6809,6 @@ process@~0.11.0: resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= -progress@^2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -6924,11 +6822,6 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz" - integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== - proxy-addr@~2.0.4: version "2.0.6" resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz" @@ -7458,18 +7351,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -roarr@^2.15.3: - version "2.15.4" - resolved "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz" - integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== - dependencies: - boolean "^3.0.1" - detect-node "^2.0.4" - globalthis "^1.0.1" - json-stringify-safe "^5.0.1" - semver-compare "^1.0.0" - sprintf-js "^1.1.2" - run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -7552,11 +7433,6 @@ selenium-webdriver@^4.0.0-alpha.1: tmp "^0.2.1" ws "^7.3.1" -semver-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz" - integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== - semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz" @@ -7574,13 +7450,6 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2: - version "7.3.7" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - semver@^7.3.5, semver@^7.3.7: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" @@ -7607,13 +7476,6 @@ send@0.16.2: range-parser "~1.2.0" statuses "~1.4.0" -serialize-error@^7.0.1: - version "7.0.1" - resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz" - integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== - dependencies: - type-fest "^0.13.1" - serialize-javascript@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" @@ -7838,11 +7700,6 @@ split2@^4.1.0: resolved "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz" integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== -sprintf-js@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" @@ -8096,13 +7953,6 @@ subarg@^1.0.0: dependencies: minimist "^1.1.0" -sumchecker@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz" - integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== - dependencies: - debug "^4.1.0" - supports-color@5.4.0: version "5.4.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz" @@ -8476,11 +8326,6 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" @@ -8505,11 +8350,6 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.13.1: - version "0.13.1" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== - type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"