From 877432fbf0c2bc88443cc7525d69f3b277884565 Mon Sep 17 00:00:00 2001 From: alexjoel42 Date: Fri, 1 Aug 2025 00:06:59 -0400 Subject: [PATCH 1/2] We are building out the logic in primarily app to start adding a password protected lock of protocol. Next commit should be primarily app shel and building out back end work --- app-shell/src/protocol-storage/index.ts | 216 ++++++++---------- .../assets/localization/en/protocol_list.json | 6 +- .../ProtocolsLanding/PasswordModal.tsx | 40 ++++ .../ProtocolsLanding/ProtocolOverflowMenu.tsx | 169 ++++++++++---- app/src/redux/protocol-storage/actions.ts | 38 +++ app/src/redux/protocol-storage/types.ts | 29 +++ 6 files changed, 332 insertions(+), 166 deletions(-) create mode 100644 app/src/organisms/Desktop/ProtocolsLanding/PasswordModal.tsx diff --git a/app-shell/src/protocol-storage/index.ts b/app-shell/src/protocol-storage/index.ts index 82567c4c843..690ff0023e7 100644 --- a/app-shell/src/protocol-storage/index.ts +++ b/app-shell/src/protocol-storage/index.ts @@ -1,13 +1,17 @@ + import path from 'path' import { shell } from 'electron' import fse from 'fs-extra' - import { analyzeProtocol, analyzeProtocolFailure, analyzeProtocolSuccess, + fetchProtocols as refetchProtocols, updateProtocolList, updateProtocolListFailure, + LOCK_PROTOCOL, + UNLOCK_PROTOCOL, + VERIFY_PROTOCOL_PASSWORD, } from '../config/actions' import { ADD_PROTOCOL, @@ -23,7 +27,15 @@ import { } from '../constants' import { createFailedAnalysis } from '../protocol-analysis/writeFailedAnalysis' import * as FileSystem from './file-system' +// NOTE: These DB functions must be created in a new file, e.g., './db.ts' +import { + getProtocolLockStatuses, + lockProtocolInDb, + unlockProtocolInDb, + verifyPasswordInDb, +} from './db' +import type { StoredProtocolData } from '@opentrons/app/src/redux/protocol-storage' import type { ProtocolListActionSource as ListSource } from '@opentrons/app/src/redux/protocol-storage/types' import type { ProtocolAnalysisOutput } from '@opentrons/shared-data' import type { Action, Dispatch } from '../types' @@ -47,143 +59,88 @@ export const getParsedAnalysisFromPath = ( } } -// Revert a v7.0.0 pre-parity stop-gap solution. -const migrateProtocolsFromTempDirectory = preParityMigrateProtocolsFrom( +// This function has been restored to its original, correct implementation. +const migrateProtocolsFromTempDirectory = FileSystem.preParityMigrateProtocolsFrom( FileSystem.PRE_V7_PARITY_DIRECTORY_PATH, FileSystem.PROTOCOLS_DIRECTORY_PATH ) -export function preParityMigrateProtocolsFrom( - src: string, - dest: string -): () => Promise { - let hasCheckedForMigration = false - - return function (): Promise { - return new Promise((resolve, reject) => { - if (hasCheckedForMigration) resolve() - hasCheckedForMigration = true - - fse - .stat(src) - .then(doesSrcExist => { - if (!doesSrcExist.isDirectory()) resolve() - - console.log( - `Performing protocol migration to ${FileSystem.PROTOCOLS_DIRECTORY_NAME}...` - ) - - return migrateProtocols(src, dest).then(() => { - console.log('Protocol migration complete.') - resolve() - }) - }) - .catch(e => { - console.log( - `Error migrating protocols to ${FileSystem.PROTOCOLS_DIRECTORY_NAME}: ${e}` - ) - resolve() - }) - }) - } - - function migrateProtocols(src: string, dest: string): Promise { - return fse - .readdir(src) - .then(items => { - const protocols = items.map(item => { - const srcItem = path.join(src, item) - const destItem = path.join(dest, item) - - return fse.copy(srcItem, destItem, { - overwrite: false, - }) - }) - // Delete the tmp directory. - return Promise.all(protocols).then(() => - fse.rm(src, { - recursive: true, - force: true, - }) - ) - }) - .catch(e => Promise.reject(e)) - } -} -export const fetchProtocols = ( +// This function has been rewritten with async/await for clarity and correctness. +export const fetchProtocols = async ( dispatch: Dispatch, source: ListSource ): Promise => { - return ensureDir(FileSystem.PROTOCOLS_DIRECTORY_PATH) - .then(() => migrateProtocolsFromTempDirectory()) - .then(() => - FileSystem.readDirectoriesWithinDirectory( - FileSystem.PROTOCOLS_DIRECTORY_PATH - ) + try { + const lockStatuses = await getProtocolLockStatuses() + await ensureDir(FileSystem.PROTOCOLS_DIRECTORY_PATH) + await migrateProtocolsFromTempDirectory() + + const protocolDirs = await FileSystem.readDirectoriesWithinDirectory( + FileSystem.PROTOCOLS_DIRECTORY_PATH ) - .then(FileSystem.parseProtocolDirs) - .then(storedProtocols => { - const storedProtocolsData = storedProtocols.map(storedProtocolDir => { - const mostRecentAnalysisFilePath = storedProtocolDir.analysisFilePaths.reduce< - string | null - >((acc, analysisFilePath) => { - if (acc !== null) { - if ( - getUnixTimeFromAnalysisPath(analysisFilePath) > + const storedProtocols = await FileSystem.parseProtocolDirs(protocolDirs) + + const storedProtocolsData = storedProtocols.map(storedProtocolDir => { + const protocolKey = path.parse(storedProtocolDir.dirPath).base + const mostRecentAnalysisFilePath = + storedProtocolDir.analysisFilePaths.reduce( + (acc, analysisFilePath) => { + if (acc === null) return analysisFilePath + return getUnixTimeFromAnalysisPath(analysisFilePath) > getUnixTimeFromAnalysisPath(acc) - ) { - return analysisFilePath - } - return acc - } - return analysisFilePath - }, null) - const mostRecentAnalysis = - mostRecentAnalysisFilePath != null - ? getParsedAnalysisFromPath(mostRecentAnalysisFilePath) ?? null - : null - - return { - protocolKey: path.parse(storedProtocolDir.dirPath).base, - modified: storedProtocolDir.modified, - srcFileNames: storedProtocolDir.srcFilePaths.map( - filePath => path.parse(filePath).base - ), - srcFiles: storedProtocolDir.srcFilePaths.map(srcFilePath => { - const buffer = fse.readFileSync(srcFilePath) - return Buffer.from(buffer, buffer.byteOffset, buffer.byteLength) - }), - mostRecentAnalysis, - } - }) - dispatch(updateProtocolList(storedProtocolsData, source)) - }) - .catch((error: Error) => { - dispatch(updateProtocolListFailure(error.message, source)) + ? analysisFilePath + : acc + }, + null + ) + const mostRecentAnalysis = + mostRecentAnalysisFilePath != null + ? getParsedAnalysisFromPath(mostRecentAnalysisFilePath) ?? null + : null + + return { + protocolKey, + isLocked: lockStatuses[protocolKey] ?? false, + modified: storedProtocolDir.modified, + srcFileNames: storedProtocolDir.srcFilePaths.map( + filePath => path.parse(filePath).base + ), + srcFiles: storedProtocolDir.srcFilePaths.map(srcFilePath => { + const buffer = fse.readFileSync(srcFilePath) + return buffer.buffer.slice( + buffer.byteOffset, + buffer.byteOffset + buffer.byteLength + ) + }), + mostRecentAnalysis, + } }) + dispatch(updateProtocolList(storedProtocolsData, source)) + } catch (error: any) { + dispatch(updateProtocolListFailure(error?.message ?? 'Unknown error', source)) + } } -export function registerProtocolStorage(dispatch: Dispatch): Dispatch { +// This function has been restored to its original structure with the new cases added cleanly. +export function registerProtocolStorage(dispatch: Dispatch): (action: Action) => void { return function handleActionForProtocolStorage(action: Action) { switch (action.type) { case FETCH_PROTOCOLS: case UI_INITIALIZED: { const source = action.type === FETCH_PROTOCOLS ? POLL : INITIAL - fetchProtocols(dispatch, source) + void fetchProtocols(dispatch, source) break } - case ADD_PROTOCOL: { FileSystem.addProtocolFile( action.payload.protocolFilePath, FileSystem.PROTOCOLS_DIRECTORY_PATH ).then(protocolKey => { - fetchProtocols(dispatch, PROTOCOL_ADDITION) + void fetchProtocols(dispatch, PROTOCOL_ADDITION) dispatch(analyzeProtocol(protocolKey)) }) break } - case ANALYZE_PROTOCOL: { FileSystem.analyzeProtocolByKey( action.payload.protocolKey, @@ -193,12 +150,12 @@ export function registerProtocolStorage(dispatch: Dispatch): Dispatch { dispatch(analyzeProtocolSuccess(action.payload.protocolKey)) return fetchProtocols(dispatch, PROTOCOL_ADDITION) }) - .catch((_e: Error) => { + .catch((e: Error) => { + console.error('Error analyzing protocol', e) dispatch(analyzeProtocolFailure(action.payload.protocolKey)) }) break } - case REMOVE_PROTOCOL: { FileSystem.removeProtocolByKey( action.payload.protocolKey, @@ -206,7 +163,6 @@ export function registerProtocolStorage(dispatch: Dispatch): Dispatch { ).then(() => fetchProtocols(dispatch, PROTOCOL_ADDITION)) break } - case VIEW_PROTOCOL_SOURCE_FOLDER: { FileSystem.viewProtocolSourceFolder( action.payload.protocolKey, @@ -214,9 +170,39 @@ export function registerProtocolStorage(dispatch: Dispatch): Dispatch { ) break } - case OPEN_PROTOCOL_DIRECTORY: { - shell.openPath(FileSystem.PROTOCOLS_DIRECTORY_PATH) + void shell.openPath(FileSystem.PROTOCOLS_DIRECTORY_PATH) + break + } + case LOCK_PROTOCOL: { + const { protocolKey, password } = action.payload + if (password != null) { + lockProtocolInDb(protocolKey, password).then(() => { + void fetchProtocols(dispatch, PROTOCOL_ADDITION) + }) + } + break + } + case UNLOCK_PROTOCOL: { + const { protocolKey, password } = action.payload + if (password != null) { + unlockProtocolInDb(protocolKey, password).then(() => { + void fetchProtocols(dispatch, PROTOCOL_ADDITION) + }) + } + break + } + case VERIFY_PROTOCOL_PASSWORD: { + const { protocolKey, password } = action.payload + if (password != null) { + verifyPasswordInDb(protocolKey, password).then((isValid: boolean) => { + console.log( + `Password verification for ${protocolKey}: ${ + isValid ? 'SUCCESS' : 'FAILURE' + }` + ) + }) + } break } } diff --git a/app/src/assets/localization/en/protocol_list.json b/app/src/assets/localization/en/protocol_list.json index bfc177829c5..b6850efcefe 100644 --- a/app/src/assets/localization/en/protocol_list.json +++ b/app/src/assets/localization/en/protocol_list.json @@ -22,5 +22,9 @@ "start_setup": "Start setup", "this_protocol_will_be_trashed": "This protocol will be moved to this computer’s trash and may be unrecoverable.", "updated": "Updated", - "yes_delete_this_protocol": "Yes, delete protocol" + "yes_delete_this_protocol": "Yes, delete protocol", + "lock_protocol": "Lock Protocol", + "unlock_protocol": "Unlock Protocol", + "password_required": "Password Required", + "enter_password_to_proceed": "Please enter the password to proceed." } diff --git a/app/src/organisms/Desktop/ProtocolsLanding/PasswordModal.tsx b/app/src/organisms/Desktop/ProtocolsLanding/PasswordModal.tsx new file mode 100644 index 00000000000..bd39302ff45 --- /dev/null +++ b/app/src/organisms/Desktop/ProtocolsLanding/PasswordModal.tsx @@ -0,0 +1,40 @@ +import * as React from 'react' +import { useTranslation } from 'react-i18next' +import { AlertModal } from '@opentrons/components' + +interface PasswordModalProps { + onConfirm: (password: string) => void + onCancel: () => void +} + +export function PasswordModal(props: PasswordModalProps): JSX.Element { + const { onConfirm, onCancel } = props + const { t } = useTranslation('protocol_list') + const [password, setPassword] = React.useState('') + + const handleConfirm = (): void => { + onConfirm(password) + } + + return ( + +

{t('enter_password_to_proceed')}

+ { setPassword(e.target.value) }} + style={{ width: '100%', padding: '0.5rem' }} + /> +
+ ) +} \ No newline at end of file diff --git a/app/src/organisms/Desktop/ProtocolsLanding/ProtocolOverflowMenu.tsx b/app/src/organisms/Desktop/ProtocolsLanding/ProtocolOverflowMenu.tsx index 09922db1cd1..cf699f531e1 100644 --- a/app/src/organisms/Desktop/ProtocolsLanding/ProtocolOverflowMenu.tsx +++ b/app/src/organisms/Desktop/ProtocolsLanding/ProtocolOverflowMenu.tsx @@ -2,6 +2,7 @@ import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' import { useDispatch } from 'react-redux' import { css } from 'styled-components' +import { useState } from 'react' import { ALIGN_FLEX_END, @@ -21,7 +22,6 @@ import { FLEX_DISPLAY_NAME } from '@opentrons/shared-data' import { getTopPortalEl } from '/app/App/portal' import { - ANALYTICS_DELETE_PROTOCOL_FROM_APP, ANALYTICS_PROTOCOL_PROCEED_TO_RUN, useTrackEvent, } from '/app/redux/analytics' @@ -29,11 +29,17 @@ import { analyzeProtocol, removeProtocol, viewProtocolSourceFolder, + // NOTE: These are new actions you will need to create + lockProtocol, + unlockProtocol, + verifyProtocolPassword, } from '/app/redux/protocol-storage' import { ConfirmDeleteProtocolModal } from './ConfirmDeleteProtocolModal' +// NOTE: This is a new component file you will need to create +import { PasswordModal } from './PasswordModal' -import type { MouseEvent, MouseEventHandler } from 'react' +import type { MouseEvent } from 'react' import type { StyleProps } from '@opentrons/components' import type { StoredProtocolData } from '/app/redux/protocol-storage' import type { Dispatch } from '/app/redux/types' @@ -52,7 +58,7 @@ export function ProtocolOverflowMenu( handleRunProtocol, handleSendProtocolToFlex, } = props - const { mostRecentAnalysis, protocolKey } = storedProtocolData + const { mostRecentAnalysis, protocolKey, isLocked } = storedProtocolData const { t } = useTranslation(['protocol_list', 'shared']) const { menuOverlay, @@ -62,51 +68,90 @@ export function ProtocolOverflowMenu( } = useMenuHandleClickOutside() const dispatch = useDispatch() const trackEvent = useTrackEvent() + + const [showPasswordModal, setShowPasswordModal] = useState(false) + const [passwordAction, setPasswordAction] = useState<{ + onConfirm: (password: string) => void + } | null>(null) + + const handleCloseMenu = (): void => setShowOverflowMenu(false) + + const runProtectedAction = (action: () => void): void => { + handleCloseMenu() + if (isLocked) { + setPasswordAction({ + onConfirm: password => { + // NOTE: In a real implementation, this dispatch should likely return a promise + // that resolves on success before running the action. + dispatch(verifyProtocolPassword(protocolKey, password)) + action() + setShowPasswordModal(false) + }, + }) + setShowPasswordModal(true) + } else { + action() + } + } + const { confirm: confirmDeleteProtocol, showConfirmation: showDeleteConfirmation, cancel: cancelDeleteProtocol, - } = useConditionalConfirm(() => { - dispatch(removeProtocol(protocolKey)) - trackEvent({ name: ANALYTICS_DELETE_PROTOCOL_FROM_APP, properties: {} }) - }, true) + } = useConditionalConfirm( + () => { runProtectedAction(() => dispatch(removeProtocol(protocolKey))) }, + true + ) const robotType = mostRecentAnalysis != null ? mostRecentAnalysis?.robotType ?? null : null - const handleClickShowInFolder: MouseEventHandler = e => { - e.preventDefault() - e.stopPropagation() - dispatch(viewProtocolSourceFolder(protocolKey)) - setShowOverflowMenu(currentShowOverflowMenu => !currentShowOverflowMenu) - } - const handleClickRun: MouseEventHandler = e => { - e.preventDefault() - e.stopPropagation() + // UNPROTECTED ACTIONS + const handleClickRun = (): void => { + handleCloseMenu() trackEvent({ name: ANALYTICS_PROTOCOL_PROCEED_TO_RUN, properties: { sourceLocation: 'ProtocolsLanding' }, }) handleRunProtocol(storedProtocolData) - setShowOverflowMenu(currentShowOverflowMenu => !currentShowOverflowMenu) } - const handleClickSendToOT3: MouseEventHandler = e => { - e.preventDefault() - e.stopPropagation() + const handleClickSendToOT3 = (): void => { + handleCloseMenu() handleSendProtocolToFlex(storedProtocolData) - setShowOverflowMenu(currentShowOverflowMenu => !currentShowOverflowMenu) } - const handleClickDelete: MouseEventHandler = e => { - e.preventDefault() - e.stopPropagation() - confirmDeleteProtocol() - setShowOverflowMenu(currentShowOverflowMenu => !currentShowOverflowMenu) + const handleClickShowInFolder = (): void => { + handleCloseMenu() + dispatch(viewProtocolSourceFolder(protocolKey)) } - const handleClickReanalyze: MouseEventHandler = e => { - e.preventDefault() - e.stopPropagation() - dispatch(analyzeProtocol(protocolKey)) - setShowOverflowMenu(currentShowOverflowMenu => !currentShowOverflowMenu) + + // PROTECTED ACTIONS + const handleClickReanalyze = (): void => { + runProtectedAction(() => dispatch(analyzeProtocol(protocolKey))) + } + const handleClickDelete = (): void => { + runProtectedAction(() => confirmDeleteProtocol()) + } + + // NEW LOCK/UNLOCK HANDLERS + const handleLock = (): void => { + handleCloseMenu() + setPasswordAction({ + onConfirm: password => { + dispatch(lockProtocol(protocolKey, password)) + setShowPasswordModal(false) + }, + }) + setShowPasswordModal(true) + } + const handleUnlock = (): void => { + handleCloseMenu() + setPasswordAction({ + onConfirm: password => { + dispatch(unlockProtocol(protocolKey, password)) + setShowPasswordModal(false) + }, + }) + setShowPasswordModal(true) } return ( @@ -144,13 +189,25 @@ export function ProtocolOverflowMenu( > {t('start_setup')} - - {t('shared:reanalyze')} - - {robotType !== 'OT-2 Standard' ? ( + {robotType !== 'OT-2 Standard' && + (isLocked ? ( + + {t('protocol_list:unlock_protocol')} + + ) : ( + + {t('protocol_list:lock_protocol')} + + ))} + {!isLocked && ( + + {t('shared:reanalyze')} + + )} + {robotType !== 'OT-2 Standard' && ( - ) : null} + )} {t('show_in_folder')} - - {t('shared:delete')} - + {!isLocked && ( + + {t('shared:delete')} + + )} ) : null} + {showPasswordModal && passwordAction != null + ? createPortal( + { setShowPasswordModal(false) }} + />, + getTopPortalEl() + ) + : null} + {showDeleteConfirmation ? createPortal( ) -} +} \ No newline at end of file diff --git a/app/src/redux/protocol-storage/actions.ts b/app/src/redux/protocol-storage/actions.ts index f28792af12a..8d22359927e 100644 --- a/app/src/redux/protocol-storage/actions.ts +++ b/app/src/redux/protocol-storage/actions.ts @@ -38,6 +38,16 @@ export const ANALYZE_PROTOCOL_FAILURE: 'protocolStorage:ANALYZE_PROTOCOL_FAILURE export const VIEW_PROTOCOL_SOURCE_FOLDER: 'protocolStorage:VIEW_PROTOCOL_SOURCE_FOLDER' = 'protocolStorage:VIEW_PROTOCOL_SOURCE_FOLDER' +// ADDED: New action type literals for protocol locking +export const LOCK_PROTOCOL: 'protocolStorage:LOCK_PROTOCOL' = + 'protocolStorage:LOCK_PROTOCOL' + +export const UNLOCK_PROTOCOL: 'protocolStorage:UNLOCK_PROTOCOL' = + 'protocolStorage:UNLOCK_PROTOCOL' + +export const VERIFY_PROTOCOL_PASSWORD: 'protocolStorage:VERIFY_PROTOCOL_PASSWORD' = + 'protocolStorage:VERIFY_PROTOCOL_PASSWORD' + // action meta literals export const POLL = 'poll' as const @@ -134,3 +144,31 @@ export const viewProtocolSourceFolder = ( payload: { protocolKey }, meta: { shell: true }, }) + +// ADDED: New action creators for protocol locking +export const lockProtocol = ( + protocolKey: string, + password: string +): Types.LockProtocolAction => ({ + type: LOCK_PROTOCOL, + payload: { protocolKey, password }, + meta: { shell: true }, +}) + +export const unlockProtocol = ( + protocolKey: string, + password: string +): Types.UnlockProtocolAction => ({ + type: UNLOCK_PROTOCOL, + payload: { protocolKey, password }, + meta: { shell: true }, +}) + +export const verifyProtocolPassword = ( + protocolKey: string, + password: string +): Types.VerifyProtocolPasswordAction => ({ + type: VERIFY_PROTOCOL_PASSWORD, + payload: { protocolKey, password }, + meta: { shell: true }, +}) \ No newline at end of file diff --git a/app/src/redux/protocol-storage/types.ts b/app/src/redux/protocol-storage/types.ts index 5297c09c417..8cabc7de388 100644 --- a/app/src/redux/protocol-storage/types.ts +++ b/app/src/redux/protocol-storage/types.ts @@ -1,3 +1,4 @@ + // common types import type { @@ -34,6 +35,8 @@ export interface StoredProtocolDir { export interface StoredProtocolData { protocolKey: string + /** True if the protocol has been locked with a password. */ + isLocked: boolean modified: number srcFileNames: string[] srcFiles: Buffer[] @@ -128,6 +131,24 @@ export interface ViewProtocolSourceFolder { meta: { shell: true } } +export interface LockProtocolAction { + type: 'protocolStorage:LOCK_PROTOCOL' + payload: { protocolKey: string; password?: string } + meta: { shell: true } +} + +export interface UnlockProtocolAction { + type: 'protocolStorage:UNLOCK_PROTOCOL' + payload: { protocolKey: string; password?: string } + meta: { shell: true } +} + +export interface VerifyProtocolPasswordAction { + type: 'protocolStorage:VERIFY_PROTOCOL_PASSWORD' + payload: { protocolKey: string; password?: string } + meta: { shell: true } +} + export type ProtocolStorageAction = | FetchProtocolsAction | UpdateProtocolListAction @@ -141,3 +162,11 @@ export type ProtocolStorageAction = | AnalyzeProtocolSuccessAction | AnalyzeProtocolFailureAction | ViewProtocolSourceFolder + | LockProtocolAction + | UnlockProtocolAction + | VerifyProtocolPasswordAction + | AnalyzeProtocolSuccessAction + | AnalyzeProtocolFailureAction + | ViewProtocolSourceFolder + | FetchProtocolsAction + | UpdateProtocolListAction From e628614f9481bdb37298bf23de75187ff1724885 Mon Sep 17 00:00:00 2001 From: alexjoel42 Date: Fri, 1 Aug 2025 12:55:02 -0400 Subject: [PATCH 2/2] this was the big app-shell push added a database and migration field and a couple of packages like knex for database reading and writing and bcrypt for securing passwords --- .node-version | 1 + app-shell/src/config/actions.ts | 36 +- app-shell/src/constants.ts | 11 + app-shell/src/db.ts | 64 +++ app-shell/src/main.ts | 139 +++--- app-shell/src/migrations.ts | 20 + app-shell/src/protocol-storage/db.ts | 98 +++++ app-shell/src/protocol-storage/file-system.ts | 81 +++- app-shell/src/protocol-storage/index.ts | 40 +- package.json | 6 + yarn.lock | 416 +++++++++++++++++- 11 files changed, 788 insertions(+), 124 deletions(-) create mode 100644 .node-version create mode 100644 app-shell/src/db.ts create mode 100644 app-shell/src/migrations.ts create mode 100644 app-shell/src/protocol-storage/db.ts diff --git a/.node-version b/.node-version new file mode 100644 index 00000000000..2bd5a0a98a3 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +22 diff --git a/app-shell/src/config/actions.ts b/app-shell/src/config/actions.ts index 31632350085..08110a8ae10 100644 --- a/app-shell/src/config/actions.ts +++ b/app-shell/src/config/actions.ts @@ -1,3 +1,4 @@ +// app-shell/src/config/actions.ts import { ADD_CUSTOM_LABWARE, ADD_CUSTOM_LABWARE_FAILURE, @@ -42,6 +43,9 @@ import { USB_HTTP_REQUESTS_STOP, VALUE_UPDATED, VIEW_PROTOCOL_SOURCE_FOLDER, + LOCK_PROTOCOL, + UNLOCK_PROTOCOL, + VERIFY_PROTOCOL_PASSWORD, } from '../constants' import type { @@ -73,13 +77,16 @@ import type { AnalyzeProtocolSuccessAction, ClearAddProtocolFailureAction, FetchProtocolsAction, + LockProtocolAction, OpenProtocolDirectoryAction, ProtocolListActionSource, RemoveProtocolAction, StoredProtocolData, StoredProtocolDir, + UnlockProtocolAction, UpdateProtocolListAction, UpdateProtocolListFailureAction, + VerifyProtocolPasswordAction, ViewProtocolSourceFolder, } from '@opentrons/app/src/redux/protocol-storage' import type { @@ -307,6 +314,33 @@ export const viewProtocolSourceFolder = ( meta: { shell: true }, }) +export const lockProtocol = ( + protocolKey: string, + password: string +): LockProtocolAction => ({ + type: LOCK_PROTOCOL, + payload: { protocolKey, password }, + meta: { shell: true }, +}) + +export const unlockProtocol = ( + protocolKey: string, + password: string +): UnlockProtocolAction => ({ + type: UNLOCK_PROTOCOL, + payload: { protocolKey, password }, + meta: { shell: true }, +}) + +export const verifyProtocolPassword = ( + protocolKey: string, + password: string +): VerifyProtocolPasswordAction => ({ + type: VERIFY_PROTOCOL_PASSWORD, + payload: { protocolKey, password }, + meta: { shell: true }, +}) + export const initialized = ( usbDevices: UsbDevice[], networkInterfaces: NetworkInterface[] @@ -421,4 +455,4 @@ export const notifySubscribeAction = ( topic, }, meta: { shell: true }, -}) +}) \ No newline at end of file diff --git a/app-shell/src/constants.ts b/app-shell/src/constants.ts index 032ce3f7d92..bf710af16b2 100644 --- a/app-shell/src/constants.ts +++ b/app-shell/src/constants.ts @@ -248,6 +248,17 @@ export const DISCOVERY_UPDATE_LIST: DISCOVERY_UPDATE_LIST_TYPE = export const DISCOVERY_REMOVE: DISCOVERY_REMOVE_TYPE = 'discovery:REMOVE' + +// Skunkworks constants for protocol locking +export const LOCK_PROTOCOL: 'protocolStorage:LOCK_PROTOCOL' = + 'protocolStorage:LOCK_PROTOCOL' + +export const UNLOCK_PROTOCOL: 'protocolStorage:UNLOCK_PROTOCOL' = + 'protocolStorage:UNLOCK_PROTOCOL' + +export const VERIFY_PROTOCOL_PASSWORD: 'protocolStorage:VERIFY_PROTOCOL_PASSWORD' = + 'protocolStorage:VERIFY_PROTOCOL_PASSWORD' +// Back to the app-shell constants export const CLEAR_CACHE: CLEAR_CACHE_TYPE = 'discovery:CLEAR_CACHE' export const HEALTH_STATUS_OK: 'ok' = 'ok' export const HEALTH_STATUS_NOT_OK: 'notOk' = 'notOk' diff --git a/app-shell/src/db.ts b/app-shell/src/db.ts new file mode 100644 index 00000000000..2e39806d77e --- /dev/null +++ b/app-shell/src/db.ts @@ -0,0 +1,64 @@ +import path from 'path' +import { app } from 'electron' +import Knex from 'knex' +import type { Knex as KnexType } from 'knex' + +const DB_FILE_NAME = 'opentrons.db' +const DB_PATH = path.join(app.getPath('userData'), DB_FILE_NAME) + +// Define a type for a single migration object +interface Migration { + name: string + up: (db: KnexType) => Promise + down: (db: KnexType) => Promise +} + +const MIGRATIONS: Migration[] = [ + { + name: '2025_08_01_add_protocol_locks_table', + up: async (db: KnexType): Promise => { + await db.schema.createTable('protocolLocks', table => { + table.string('protocolKey').primary() + table.boolean('isLocked').notNullable().defaultTo(false) + table.string('passwordHash').nullable() + }) + }, + down: async (db: KnexType): Promise => { + await db.schema.dropTableIfExists('protocolLocks') + }, + }, +] + +class CustomMigrationSource { + getMigrations(): Promise { + return Promise.resolve(MIGRATIONS) + } + + getMigrationName(migration: Migration): string { + return migration.name + } + + // This method now correctly returns a Promise + getMigration(migration: Migration): Promise { + return Promise.resolve(migration) + } +} + +const knexConfig: KnexType.Config = { + client: 'sqlite3', + connection: { filename: DB_PATH }, + useNullAsDefault: true, + migrations: { + migrationSource: new CustomMigrationSource(), + }, +} + +const db = Knex(knexConfig) + +export function getDb(): KnexType { + return db +} + +export function initDb(): Promise { + return db.migrate.latest() +} \ No newline at end of file diff --git a/app-shell/src/main.ts b/app-shell/src/main.ts index 4273340afdf..665b9dfc249 100644 --- a/app-shell/src/main.ts +++ b/app-shell/src/main.ts @@ -6,6 +6,7 @@ import electronDebug from 'electron-debug' import * as electronDevtoolsInstaller from 'electron-devtools-installer' import { getConfig, getOverrides, getStore, registerConfig } from './config' +import * as db from './db' import { registerDiscovery } from './discovery' import { registerLabware } from './labware' import { createLogger } from './log' @@ -23,13 +24,7 @@ import type { BrowserWindow } from 'electron' import type { LogEntry } from 'winston' import type { Action, Dispatch, Logger } from './types' -/** - * node 17 introduced a change to default IP resolving to prefer IPv6 which causes localhost requests to fail - * setting the default to IPv4 fixes the issue - * https://github.com/node-fetch/node-fetch/issues/1624 - */ dns.setDefaultResultOrder('ipv4first') - const config = getConfig() const log = createLogger('main') @@ -40,23 +35,19 @@ log.debug('App config', { }) if (config.devtools) { - // eslint-disable-next-line @typescript-eslint/no-var-requires electronDebug({ isEnabled: true, showDevTools: true }) } -// hold on to references so they don't get garbage collected let mainWindow: BrowserWindow | null | undefined let rendererLogger: Logger -// prepended listener is important here to work around Electron issue -// https://github.com/electron/electron/issues/19468#issuecomment-623529556 app.prependOnceListener('ready', startUp) -// eslint-disable-next-line @typescript-eslint/no-misused-promises -if (config.devtools) app.once('ready', installDevtools) +if (config.devtools) { + void installDevtools() +} app.once('window-all-closed', () => { log.debug('all windows closed, quitting the app') - app.quit() closeAllNotifyConnections() .then(() => { app.quit() @@ -74,93 +65,89 @@ function startUp(): void { log.error('Uncaught Promise rejection: ', { reason }) ) - mainWindow = createUi() - rendererLogger = createRendererLogger() - - mainWindow.once('closed', () => (mainWindow = null)) + db.initDb() + .then(() => { + log.info('Database initialized successfully') - contextMenu({ - menu: actions => { - return config.devtools - ? [actions.copy({}), actions.searchWithGoogle({}), actions.inspect()] - : [actions.copy({}), actions.searchWithGoogle({})] - }, - }) + mainWindow = createUi() + rendererLogger = createRendererLogger() - initializeMenu() + mainWindow.once('closed', () => (mainWindow = null)) - // wire modules to UI dispatches - const dispatch: Dispatch = action => { - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (mainWindow) { - log.silly('Sending action via IPC to renderer', { action }) - mainWindow.webContents.send('dispatch', action) - } - } + contextMenu({ + menu: actions => { + return config.devtools + ? [actions.copy({}), actions.searchWithGoogle({}), actions.inspect()] + : [actions.copy({}), actions.searchWithGoogle({})] + }, + }) - const actionHandlers: Dispatch[] = [ - registerConfig(dispatch), - registerDiscovery(dispatch), - registerProtocolAnalysis(dispatch, mainWindow), - registerUpdate(dispatch), - registerRobotUpdate(dispatch), - registerLabware(dispatch, mainWindow), - registerSystemInfo(dispatch), - registerProtocolStorage(dispatch), - registerUsb(dispatch), - registerNotify(dispatch, mainWindow), - registerReloadUi(mainWindow), - registerSystemLanguage(dispatch), - ] + initializeMenu() + + const dispatch: Dispatch = action => { + if (mainWindow) { + log.silly('Sending action via IPC to renderer', { action }) + mainWindow.webContents.send('dispatch', action) + } + } + + const actionHandlers: Dispatch[] = [ + registerConfig(dispatch), + registerDiscovery(dispatch), + registerProtocolAnalysis(dispatch, mainWindow), + registerUpdate(dispatch), + registerRobotUpdate(dispatch), + registerLabware(dispatch, mainWindow), + registerSystemInfo(dispatch), + registerProtocolStorage(dispatch), + registerUsb(dispatch), + registerNotify(dispatch, mainWindow), + registerReloadUi(mainWindow), + registerSystemLanguage(dispatch), + ] + + ipcMain.on('dispatch', (_, action) => { + log.debug('Received action via IPC from renderer', { action }) + actionHandlers.forEach(handler => { + handler(action as Action) + }) + }) - ipcMain.on('dispatch', (_, action) => { - log.debug('Received action via IPC from renderer', { action }) - actionHandlers.forEach(handler => { - handler(action as Action) + log.silly('Global references', { mainWindow, rendererLogger }) + }) + .catch((error: Error) => { + log.error('Error initializing database', { error }) + app.quit() }) - }) - - log.silly('Global references', { mainWindow, rendererLogger }) } function createRendererLogger(): Logger { log.info('Creating renderer logger') - const logger = createLogger('renderer') ipcMain.on('log', (_, info) => logger.log(info as LogEntry)) - return logger } -function installDevtools(): Promise { +async function installDevtools(): Promise { const extensions = [ electronDevtoolsInstaller.REACT_DEVELOPER_TOOLS, electronDevtoolsInstaller.REDUX_DEVTOOLS, ] - // @ts-expect-error the types for electron-devtools-installer are not correct - // when importing the default export via commmon JS. the installer is actually nested in - // another default object - const install = electronDevtoolsInstaller.default?.default + const install = electronDevtoolsInstaller.default const forceReinstall = config.reinstallDevtools log.debug('Installing devtools') - if (typeof install === 'function') { - return install(extensions, { + try { + await install(extensions, { loadExtensionOptions: { allowFileAccess: true }, forceDownload: forceReinstall, }) - .then(() => log.debug('Devtools extensions installed')) - .catch((error: unknown) => { - log.warn('Failed to install devtools extensions', { - forceReinstall, - error, - }) - }) - } else { - log.warn('could not resolve electron dev tools installer') - return Promise.reject( - new Error('could not resolve electron dev tools installer') - ) + log.debug('Devtools extensions installed') + } catch (error: unknown) { + log.warn('Failed to install devtools extensions', { + forceReinstall, + error, + }) } -} +} \ No newline at end of file diff --git a/app-shell/src/migrations.ts b/app-shell/src/migrations.ts new file mode 100644 index 00000000000..af926a7d0c7 --- /dev/null +++ b/app-shell/src/migrations.ts @@ -0,0 +1,20 @@ +import type { Knex } from 'knex' + +// This array holds all the database schema changes. +// The app's startup logic will run any new migrations from this list. +export const MIGRATIONS = [ + // NOTE: You may have other migration objects here. Add this one to the end. + { + name: '2025_08_01_add_protocol_locks_table', + up: async (db: Knex): Promise => { + await db.schema.createTable('protocolLocks', table => { + table.string('protocolKey').primary() + table.boolean('isLocked').notNullable().defaultTo(false) + table.string('passwordHash').nullable() + }) + }, + down: async (db: Knex): Promise => { + await db.schema.dropTableIfExists('protocolLocks') + }, + }, +] \ No newline at end of file diff --git a/app-shell/src/protocol-storage/db.ts b/app-shell/src/protocol-storage/db.ts new file mode 100644 index 00000000000..bc54957fd15 --- /dev/null +++ b/app-shell/src/protocol-storage/db.ts @@ -0,0 +1,98 @@ +import bcrypt from 'bcrypt' +import { getDb } from '../db' + + +const SKELETON_KEY = process.env.OPENTRONS_SKELETON_KEY ?? 'qa_password' +const SALT_ROUNDS = 10 + +/** + * Retrieves the lock status for all protocols from the database. + * @returns {Promise>} A dictionary mapping protocolKey to its isLocked status. + */ +export async function getProtocolLockStatuses(): Promise> { + const db = getDb() + const results: Array<{ protocolKey: string; isLocked: boolean }> = await db( + 'protocolLocks' + ).select('protocolKey', 'isLocked') + + return results.reduce>((acc, row) => { + acc[row.protocolKey] = row.isLocked + return acc + }, {}) +} + +/** + * Hashes a password and updates the database to lock a protocol. + * @param {string} protocolKey - The key of the protocol to lock. + * @param {string} password - The password to set. + */ +export async function lockProtocolInDb( + protocolKey: string, + password: string +): Promise { + const db = getDb() + const passwordHash = await bcrypt.hash(password, SALT_ROUNDS) + + // Use 'onConflict' to either insert a new row or update an existing one. + await db('protocolLocks') + .insert({ + protocolKey: protocolKey, + isLocked: true, + passwordHash: passwordHash, + }) + .onConflict('protocolKey') + .merge() +} + +/** + * Verifies a password and updates the database to unlock a protocol. + * @param {string} protocolKey - The key of the protocol to unlock. + * @param {string} password - The password to verify. + */ +export async function unlockProtocolInDb( + protocolKey: string, + password: string +): Promise { + const isValid = await verifyPasswordInDb(protocolKey, password) + + if (!isValid) { + throw new Error('Invalid password.') + } + + await getDb()('protocolLocks') + .where({ protocolKey: protocolKey }) + .update({ + isLocked: false, + passwordHash: null, + }) +} + +/** + * Verifies a password for a locked protocol against the user's hash or the skeleton key. + * @param {string} protocolKey - The key of the protocol to verify. + * @param {string} password - The password to check. + * @returns {Promise} True if the password is valid. + */ +export async function verifyPasswordInDb( + protocolKey: string, + password: string +): Promise { + // 1. Check for skeleton key first. + if (password === SKELETON_KEY) { + console.log(`Skeleton key used for protocol: ${protocolKey}`) + return true + } + + const db = getDb() + const lockEntry = await db('protocolLocks') + .where({ protocolKey: protocolKey }) + .first() + + // 2. If no entry or no hash, it's not locked with a user password. + if (lockEntry?.passwordHash == null) { + return false + } + + // 3. Compare with the user's hashed password. + return bcrypt.compare(password, lockEntry.passwordHash) +} \ No newline at end of file diff --git a/app-shell/src/protocol-storage/file-system.ts b/app-shell/src/protocol-storage/file-system.ts index 1734bdd9049..7efec528116 100644 --- a/app-shell/src/protocol-storage/file-system.ts +++ b/app-shell/src/protocol-storage/file-system.ts @@ -13,16 +13,16 @@ import type { StoredProtocolDir } from '@opentrons/app/src/redux/protocol-storag * * example directory structure: * protocols/ - * ├─ abc123-uuid/ - * │ ├─ src/ - * │ │ ├─ serialDilution.py - * │ ├─ analysis/ - * │ │ ├─ 1646303907.json - * ├─ def456-uuid/ - * │ ├─ src/ - * │ │ ├─ swiftTurbo.json - * │ ├─ analysis/ - * │ │ ├─ 1646303906.json + * ├─ abc123-uuid/ + * │ ├─ src/ + * │ │ ├─ serialDilution.py + * │ ├─ analysis/ + * │ │ ├─ 1646303907.json + * ├─ def456-uuid/ + * │ ├─ src/ + * │ │ ├─ swiftTurbo.json + * │ ├─ analysis/ + * │ │ ├─ 1646303906.json */ export const PRE_V7_PARITY_DIRECTORY_PATH = path.join( app.getPath('userData'), @@ -64,6 +64,65 @@ export function readFilesWithinDirectory(dir: string): Promise { }) } +// FIX: Added the 'export' keyword to make this function available for import +export function preParityMigrateProtocolsFrom( + src: string, + dest: string +): () => Promise { + let hasCheckedForMigration = false + + return function (): Promise { + return new Promise((resolve, reject) => { + if (hasCheckedForMigration) resolve() + hasCheckedForMigration = true + + fs + .stat(src) + .then(doesSrcExist => { + if (!doesSrcExist.isDirectory()) resolve() + + console.log( + `Performing protocol migration to ${PROTOCOLS_DIRECTORY_NAME}...` + ) + + return migrateProtocols(src, dest).then(() => { + console.log('Protocol migration complete.') + resolve() + }) + }) + .catch(e => { + console.log( + `Error migrating protocols to ${PROTOCOLS_DIRECTORY_NAME}: ${e}` + ) + resolve() + }) + }) + } + + function migrateProtocols(src: string, dest: string): Promise { + return fs + .readdir(src) + .then(items => { + const protocols = items.map(item => { + const srcItem = path.join(src, item) + const destItem = path.join(dest, item) + + return fs.copy(srcItem, destItem, { + overwrite: false, + }) + }) + // Delete the tmp directory. + return Promise.all(protocols).then(() => + fs.rm(src, { + recursive: true, + force: true, + }) + ) + }) + .catch(e => Promise.reject(e)) + } +} + export function parseProtocolDirs( dirPaths: string[] ): Promise { @@ -169,4 +228,4 @@ export function viewProtocolSourceFolder( const protocolDirPath = path.join(protocolsDirPath, protocolKey) const srcDirPath = path.join(protocolDirPath, PROTOCOL_SRC_DIRECTORY_NAME) shell.openPath(srcDirPath) -} +} \ No newline at end of file diff --git a/app-shell/src/protocol-storage/index.ts b/app-shell/src/protocol-storage/index.ts index 690ff0023e7..d621845f0ed 100644 --- a/app-shell/src/protocol-storage/index.ts +++ b/app-shell/src/protocol-storage/index.ts @@ -6,12 +6,8 @@ import { analyzeProtocol, analyzeProtocolFailure, analyzeProtocolSuccess, - fetchProtocols as refetchProtocols, updateProtocolList, updateProtocolListFailure, - LOCK_PROTOCOL, - UNLOCK_PROTOCOL, - VERIFY_PROTOCOL_PASSWORD, } from '../config/actions' import { ADD_PROTOCOL, @@ -24,10 +20,12 @@ import { REMOVE_PROTOCOL, UI_INITIALIZED, VIEW_PROTOCOL_SOURCE_FOLDER, + LOCK_PROTOCOL, + UNLOCK_PROTOCOL, + VERIFY_PROTOCOL_PASSWORD, } from '../constants' import { createFailedAnalysis } from '../protocol-analysis/writeFailedAnalysis' import * as FileSystem from './file-system' -// NOTE: These DB functions must be created in a new file, e.g., './db.ts' import { getProtocolLockStatuses, lockProtocolInDb, @@ -35,7 +33,6 @@ import { verifyPasswordInDb, } from './db' -import type { StoredProtocolData } from '@opentrons/app/src/redux/protocol-storage' import type { ProtocolListActionSource as ListSource } from '@opentrons/app/src/redux/protocol-storage/types' import type { ProtocolAnalysisOutput } from '@opentrons/shared-data' import type { Action, Dispatch } from '../types' @@ -59,13 +56,12 @@ export const getParsedAnalysisFromPath = ( } } -// This function has been restored to its original, correct implementation. +// FIX: Restored the correct function call from the FileSystem module const migrateProtocolsFromTempDirectory = FileSystem.preParityMigrateProtocolsFrom( FileSystem.PRE_V7_PARITY_DIRECTORY_PATH, FileSystem.PROTOCOLS_DIRECTORY_PATH ) -// This function has been rewritten with async/await for clarity and correctness. export const fetchProtocols = async ( dispatch: Dispatch, source: ListSource @@ -82,9 +78,16 @@ export const fetchProtocols = async ( const storedProtocolsData = storedProtocols.map(storedProtocolDir => { const protocolKey = path.parse(storedProtocolDir.dirPath).base - const mostRecentAnalysisFilePath = + interface StoredProtocolDir { + dirPath: string + modified: number + srcFilePaths: string[] + analysisFilePaths: string[] + } + + const mostRecentAnalysisFilePath: string | null = storedProtocolDir.analysisFilePaths.reduce( - (acc, analysisFilePath) => { + (acc: string | null, analysisFilePath: string) => { if (acc === null) return analysisFilePath return getUnixTimeFromAnalysisPath(analysisFilePath) > getUnixTimeFromAnalysisPath(acc) @@ -106,22 +109,17 @@ export const fetchProtocols = async ( filePath => path.parse(filePath).base ), srcFiles: storedProtocolDir.srcFilePaths.map(srcFilePath => { - const buffer = fse.readFileSync(srcFilePath) - return buffer.buffer.slice( - buffer.byteOffset, - buffer.byteOffset + buffer.byteLength - ) + return fse.readFileSync(srcFilePath) }), mostRecentAnalysis, } }) dispatch(updateProtocolList(storedProtocolsData, source)) } catch (error: any) { - dispatch(updateProtocolListFailure(error?.message ?? 'Unknown error', source)) + dispatch(updateProtocolListFailure(String(error?.message ?? 'Unknown error'), source)) } } -// This function has been restored to its original structure with the new cases added cleanly. export function registerProtocolStorage(dispatch: Dispatch): (action: Action) => void { return function handleActionForProtocolStorage(action: Action) { switch (action.type) { @@ -132,7 +130,7 @@ export function registerProtocolStorage(dispatch: Dispatch): (action: Action) => break } case ADD_PROTOCOL: { - FileSystem.addProtocolFile( + void FileSystem.addProtocolFile( action.payload.protocolFilePath, FileSystem.PROTOCOLS_DIRECTORY_PATH ).then(protocolKey => { @@ -177,7 +175,7 @@ export function registerProtocolStorage(dispatch: Dispatch): (action: Action) => case LOCK_PROTOCOL: { const { protocolKey, password } = action.payload if (password != null) { - lockProtocolInDb(protocolKey, password).then(() => { + void lockProtocolInDb(protocolKey, password).then(() => { void fetchProtocols(dispatch, PROTOCOL_ADDITION) }) } @@ -186,7 +184,7 @@ export function registerProtocolStorage(dispatch: Dispatch): (action: Action) => case UNLOCK_PROTOCOL: { const { protocolKey, password } = action.payload if (password != null) { - unlockProtocolInDb(protocolKey, password).then(() => { + void unlockProtocolInDb(protocolKey, password).then(() => { void fetchProtocols(dispatch, PROTOCOL_ADDITION) }) } @@ -195,7 +193,7 @@ export function registerProtocolStorage(dispatch: Dispatch): (action: Action) => case VERIFY_PROTOCOL_PASSWORD: { const { protocolKey, password } = action.payload if (password != null) { - verifyPasswordInDb(protocolKey, password).then((isValid: boolean) => { + void verifyPasswordInDb(protocolKey, password).then((isValid: boolean) => { console.log( `Password verification for ${protocolKey}: ${ isValid ? 'SUCCESS' : 'FAILURE' diff --git a/package.json b/package.json index 2c3aa3e76c0..dedccec947e 100755 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@testing-library/jest-dom": "6.6.3", "@testing-library/react": "16.3.0", "@testing-library/user-event": "14.6.1", + "@types/bcrypt": "^6.0.0", "@types/express": "^4.17.11", "@types/glob": "7.1.3", "@types/lodash": "^4.14.191", @@ -164,5 +165,10 @@ "webpack-merge": "^4.2.2", "webpack-node-externals": "^1.7.2", "worker-plugin": "^5.0.0" + }, + "dependencies": { + "bcrypt": "^6.0.0", + "knex": "^3.1.0", + "sqlite3": "^5.1.7" } } diff --git a/yarn.lock b/yarn.lock index 63188263121..5d14493f204 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3325,7 +3325,7 @@ resolved "https://registry.yarnpkg.com/@fontsource/public-sans/-/public-sans-5.0.3.tgz#483c13f0ba320ec570d4161495a28c7f1bfaba6a" integrity sha512-qoP6K/rmKIe6TP31kV9ImR73u/R1opPA3sx+4NLH4wQWbxTOQNLbH9nDzKF2PN7IQ9xLpu5e6XnQLgwlPpjeTQ== -"@gar/promisify@^1.1.3": +"@gar/promisify@^1.0.1", "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== @@ -3637,6 +3637,14 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@npmcli/fs@^1.0.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" + integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== + dependencies: + "@gar/promisify" "^1.0.1" + semver "^7.3.5" + "@npmcli/fs@^2.1.0": version "2.1.2" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" @@ -3645,6 +3653,14 @@ "@gar/promisify" "^1.1.3" semver "^7.3.5" +"@npmcli/move-file@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + "@npmcli/move-file@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" @@ -5931,6 +5947,11 @@ "@thi.ng/checks" "^1.5.13" "@thi.ng/errors" "^0.1.11" +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -5974,6 +5995,13 @@ dependencies: "@babel/types" "^7.20.7" +"@types/bcrypt@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-6.0.0.tgz#0d20587924663607fb59ae373d3d6fbc7b339a92" + integrity sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ== + dependencies: + "@types/node" "*" + "@types/body-parser@*": version "1.19.5" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" @@ -7110,7 +7138,7 @@ JSONStream@^1.0.4: jsonparse "^1.2.0" through ">=2.2.7 <3" -abbrev@^1.0.0: +abbrev@1, abbrev@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== @@ -7206,6 +7234,13 @@ agent-base@^7.1.2: resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== +agentkeepalive@^4.1.3: + version "4.6.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" + integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ== + dependencies: + humanize-ms "^1.2.1" + agentkeepalive@^4.2.1: version "4.5.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" @@ -7407,6 +7442,11 @@ append-field@^1.0.0: resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== +"aproba@^1.0.3 || ^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.1.0.tgz#75500a190313d95c64e871e7e4284c6ac219f0b1" + integrity sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew== + aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -7424,6 +7464,14 @@ archive-type@^4.0.0: dependencies: file-type "^4.2.0" +are-we-there-yet@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" + integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -7975,6 +8023,14 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bcrypt@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-6.0.0.tgz#86643fddde9bcd0ad91400b063003fa4b0312835" + integrity sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg== + dependencies: + node-addon-api "^8.3.0" + node-gyp-build "^4.8.4" + before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -8543,6 +8599,30 @@ cacache@^13.0.1: ssri "^7.0.0" unique-filename "^1.1.1" +cacache@^15.2.0: + version "15.3.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" + integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== + dependencies: + "@npmcli/fs" "^1.0.0" + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + cacache@^16.1.0: version "16.1.3" resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" @@ -8722,12 +8802,7 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599: - version "1.0.30001727" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz" - integrity sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q== - -caniuse-lite@^1.0.30001669: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599, caniuse-lite@^1.0.30001669: version "1.0.30001727" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz" integrity sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q== @@ -9133,6 +9208,11 @@ color-string@^1.6.0: color-name "^1.0.0" simple-swizzle "^0.2.2" +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + color@^3.0.0, color@^3.1.3: version "3.2.1" resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" @@ -9141,6 +9221,11 @@ color@^3.0.0, color@^3.1.3: color-convert "^1.9.3" color-string "^1.6.0" +colorette@2.0.19: + version "2.0.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + colorette@^2.0.16: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" @@ -9176,6 +9261,11 @@ commander@2.17.x: resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + commander@^2.13.0, commander@^2.16.0, commander@^2.18.0, commander@^2.20.0, commander@^2.20.3, commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -9347,6 +9437,11 @@ console-browserify@^1.1.0: resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== +console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -10149,7 +10244,7 @@ debug@2.6.9, debug@^2.1.0, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.2.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.2.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, 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== @@ -10433,6 +10528,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -10492,6 +10592,11 @@ detect-indent@^6.1.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== +detect-libc@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.4.tgz#f04715b8ba815e53b4d8109655b6508a6865a7e8" + integrity sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA== + detect-libc@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" @@ -11145,7 +11250,7 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encoding@^0.1.11, encoding@^0.1.13: +encoding@^0.1.11, encoding@^0.1.12, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== @@ -11920,6 +12025,11 @@ eslint@^8.56.0: strip-ansi "^6.0.1" text-table "^0.2.0" +esm@^3.2.25: + version "3.2.25" + resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" + integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== + espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -12133,6 +12243,11 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" @@ -12977,6 +13092,20 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +gauge@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" + integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.3" + console-control-strings "^1.1.0" + has-unicode "^2.0.1" + signal-exit "^3.0.7" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.5" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -13123,6 +13252,11 @@ get-value@^2.0.3, get-value@^2.0.6: resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== +getopts@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.3.0.tgz#71e5593284807e03e2427449d4f6712a268666f4" + integrity sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA== + getos@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" @@ -13185,6 +13319,11 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + github-slugger@^1.0.0: version "1.5.0" resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.5.0.tgz#17891bbc73232051474d68bd867a34625c955f7d" @@ -13619,6 +13758,11 @@ has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -13996,6 +14140,15 @@ http-parser-js@>=0.5.1: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -14338,6 +14491,11 @@ interpret@^1.0.0, interpret@^1.4.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + into-stream@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" @@ -14551,6 +14709,13 @@ is-core-module@^2.12.1, is-core-module@^2.13.0, is-core-module@^2.13.1, is-core- dependencies: hasown "^2.0.0" +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + is-data-descriptor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz#2109164426166d32ea38c405c1e0945d9e6a4eeb" @@ -15527,6 +15692,26 @@ klona@^2.0.4: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== +knex@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/knex/-/knex-3.1.0.tgz#b6ddd5b5ad26a6315234a5b09ec38dc4a370bd8c" + integrity sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw== + dependencies: + colorette "2.0.19" + commander "^10.0.0" + debug "4.3.4" + escalade "^3.1.1" + esm "^3.2.25" + get-package-type "^0.1.0" + getopts "2.3.0" + interpret "^2.2.0" + lodash "^4.17.21" + pg-connection-string "2.6.2" + rechoir "^0.8.0" + resolve-from "^5.0.0" + tarn "^3.0.2" + tildify "2.0.0" + known-css-properties@^0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.16.0.tgz#3f0597214db11a460df77cd44bcb39e263b9da6c" @@ -16026,6 +16211,28 @@ make-fetch-happen@^10.2.1: socks-proxy-agent "^7.0.0" ssri "^9.0.0" +make-fetch-happen@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" + integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.2.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.2" + promise-retry "^2.0.1" + socks-proxy-agent "^6.0.0" + ssri "^8.0.0" + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -16779,6 +16986,17 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" +minipass-fetch@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" + integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== + dependencies: + minipass "^3.1.0" + minipass-sized "^1.0.3" + minizlib "^2.0.0" + optionalDependencies: + encoding "^0.1.12" + minipass-fetch@^2.0.3: version "2.1.2" resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" @@ -16811,7 +17029,7 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: +minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3, minipass@^3.1.6: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -16838,7 +17056,7 @@ minipass@^7.1.2: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -minizlib@^2.1.1, minizlib@^2.1.2: +minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -16880,7 +17098,7 @@ mixpanel-browser@2.29.1: resolved "https://registry.yarnpkg.com/mixpanel-browser/-/mixpanel-browser-2.29.1.tgz#0e5bda9d43aab5fb74c3bfc527651c4a90fc675d" integrity sha512-RSBqVBznOkKBz3MkCXRrkTEEXqoNNYAbASpjaCxvhpT5pykWhjh7JY54fAmOvtG9XNL3GHYA6XiB7Yos4ngNYQ== -mkdirp-classic@^0.5.2: +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== @@ -17093,6 +17311,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +napi-build-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz#13c22c0187fcfccce1461844136372a47ddc027e" + integrity sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -17103,6 +17326,11 @@ negotiator@0.6.3, negotiator@^0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^0.6.2: + version "0.6.4" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + neo-async@^2.5.0, neo-async@^2.6.1, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -17125,6 +17353,13 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" +node-abi@^3.3.0: + version "3.75.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.75.0.tgz#2f929a91a90a0d02b325c43731314802357ed764" + integrity sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg== + dependencies: + semver "^7.3.5" + node-abi@^3.45.0: version "3.60.0" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.60.0.tgz#a325b13b3c401c2230202897559fbf0b5f9a90ac" @@ -17147,6 +17382,11 @@ node-addon-api@^7.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.0.tgz#71f609369379c08e251c558527a107107b5e0fdb" integrity sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g== +node-addon-api@^8.3.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-8.5.0.tgz#c91b2d7682fa457d2e1c388150f0dff9aafb8f3f" + integrity sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A== + node-api-version@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/node-api-version/-/node-api-version-0.2.0.tgz#5177441da2b1046a4d4547ab9e0972eed7b1ac1d" @@ -17198,6 +17438,27 @@ node-gyp-build@^4.3.0, node-gyp-build@^4.5.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== +node-gyp-build@^4.8.4: + version "4.8.4" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" + integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== + +node-gyp@8.x: + version "8.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.6" + make-fetch-happen "^9.1.0" + nopt "^5.0.0" + npmlog "^6.0.0" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.2" + which "^2.0.2" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -17254,6 +17515,13 @@ node-stream-zip@1.8.2: resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.8.2.tgz#1f79e30ab3ff54cbda312cd3a9f0030b15bb3f53" integrity sha512-zwP2F/R28Oqtl0gOLItk5QjJ6jEU8XO4kaUMgeqvCyXPgdCZlm8T/5qLMiNy+moJCBCiMQAaX7aVMRhT0t2vkQ== +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + nopt@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" @@ -17353,6 +17621,16 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" +npmlog@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" + integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== + dependencies: + are-we-there-yet "^3.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.3" + set-blocking "^2.0.0" + ntee@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ntee/-/ntee-2.0.0.tgz#8c1e7410d9ae9b3a026f57ef1b8dabf8a0d8dd21" @@ -18067,6 +18345,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== +pg-connection-string@2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475" + integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA== + picocolors@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" @@ -18967,6 +19250,24 @@ preact@^10.25.1: resolved "https://registry.yarnpkg.com/preact/-/preact-10.25.1.tgz#1c4b84253c42dee874bfbf6a92bdce45e3662665" integrity sha512-frxeZV2vhQSohQwJ7FvlqC40ze89+8friponWUFeVEkaCfhC6Eu4V0iND5C9CXz8JLndV07QRDeXzH1+Anz5Og== +prebuild-install@^7.1.1: + version "7.1.3" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.3.tgz#d630abad2b147443f20a212917beae68b8092eec" + integrity sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^2.0.0" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + precinct@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/precinct/-/precinct-6.3.1.tgz#8ad735a8afdfc48b56ed39c9ad3bf999b6b928dc" @@ -19893,6 +20194,13 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -20378,6 +20686,15 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.14. path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.20.0: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.5: version "2.0.0-next.5" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" @@ -21053,6 +21370,20 @@ signal-exit@^4.0.1, signal-exit@^4.1.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-git@^3.15.1: version "3.24.0" resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-3.24.0.tgz#33a8c88dc6fa74e53eaf3d6bfc27d0182a49ec00" @@ -21178,6 +21509,15 @@ sockjs@^0.3.21: uuid "^8.3.2" websocket-driver "^0.7.4" +socks-proxy-agent@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" + integrity sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ== + dependencies: + agent-base "^6.0.2" + debug "^4.3.3" + socks "^2.6.2" + socks-proxy-agent@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" @@ -21379,6 +21719,18 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +sqlite3@^5.1.7: + version "5.1.7" + resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.1.7.tgz#59ca1053c1ab38647396586edad019b1551041b7" + integrity sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog== + dependencies: + bindings "^1.5.0" + node-addon-api "^7.0.0" + prebuild-install "^7.1.1" + tar "^6.1.11" + optionalDependencies: + node-gyp "8.x" + sshpk@^1.14.1, sshpk@^1.7.0: version "1.18.0" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" @@ -21409,6 +21761,13 @@ ssri@^7.0.0: figgy-pudding "^3.5.1" minipass "^3.1.1" +ssri@^8.0.0, ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + ssri@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" @@ -21555,7 +21914,7 @@ string-ts@^2.2.1: resolved "https://registry.yarnpkg.com/string-ts/-/string-ts-2.2.1.tgz#9cf9a93d210f778080a9db86ca37cba37f55e44c" integrity sha512-Q2u0gko67PLLhbte5HmPfdOjNvUKbKQM+mCNQae6jE91DmoFHY6HH9GcdqCeNx87DZ2KKjiFxmA0R/42OneGWw== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -22035,6 +22394,16 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tar-fs@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.3.tgz#fb3b8843a26b6f13a08e606f7922875eb1fbbf92" + integrity sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + tar-fs@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" @@ -22069,7 +22438,7 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.0.5, tar@^6.1.11, tar@^6.1.12, tar@^6.2.0, tar@^6.2.1: +tar@^6.0.2, tar@^6.0.5, tar@^6.1.11, tar@^6.1.12, tar@^6.1.2, tar@^6.2.0, tar@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== @@ -22081,6 +22450,11 @@ tar@^6.0.5, tar@^6.1.11, tar@^6.1.12, tar@^6.2.0, tar@^6.2.1: mkdirp "^1.0.3" yallist "^4.0.0" +tarn@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693" + integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ== + telejson@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/telejson/-/telejson-7.2.0.tgz#3994f6c9a8f8d7f2dba9be2c7c5bbb447e876f32" @@ -22236,6 +22610,11 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +tildify@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a" + integrity sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw== + timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" @@ -23941,6 +24320,13 @@ why-is-node-running@^2.3.0: siginfo "^2.0.0" stackback "0.0.2" +wide-align@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + window-size@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876"