1
1
mirror of https://github.com/Eugeny/tabby.git synced 2024-11-22 03:26:09 +03:00

Compare commits

...

4 Commits

Author SHA1 Message Date
dependabot[bot]
0a7b665f79
Merge 01a600f44c into ac6f60f1ae 2024-11-20 17:10:09 +08:00
Snuupy
ac6f60f1ae
add ubuntu 24.10 to build.yml (#10047) 2024-11-07 16:58:06 +01:00
Andy Law
aa67130e37
Fix Issue #10012 - better ssh config parsing (#10043) 2024-11-06 12:41:04 +01:00
dependabot[bot]
01a600f44c
Bump @babel/traverse from 7.20.13 to 7.23.2
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.20.13 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-18 19:00:24 +00:00
4 changed files with 382 additions and 138 deletions

View File

@ -251,7 +251,7 @@ jobs:
repo: 'eugeny/tabby'
dir: 'dist'
rpmvers: 'el/9 el/8 ol/6 ol/7'
debvers: 'ubuntu/bionic ubuntu/focal ubuntu/hirsute ubuntu/impish ubuntu/jammy ubuntu/kinetic ubuntu/noble debian/jessie debian/stretch debian/buster'
debvers: 'ubuntu/bionic ubuntu/focal ubuntu/hirsute ubuntu/impish ubuntu/jammy ubuntu/kinetic ubuntu/noble ubuntu/oracular debian/jessie debian/stretch debian/buster'
- uses: actions/upload-artifact@master
name: Upload AppImage (${{matrix.arch}})

View File

@ -24,6 +24,7 @@
"devDependencies": {
"electron-promise-ipc": "^2.2.4",
"ps-node": "^0.1.6",
"ssh-config": "^5.0.0",
"tmp-promise": "^3.0.2",
"which": "^3.0.0",
"winston": "^3.3.3"

View File

@ -1,4 +1,3 @@
import at from 'core-js-pure/actual/array/at'
import * as fs from 'fs/promises'
import * as fsSync from 'fs'
import * as path from 'path'
@ -6,125 +5,297 @@ import slugify from 'slugify'
import * as yaml from 'js-yaml'
import { Injectable } from '@angular/core'
import { PartialProfile } from 'tabby-core'
import { SSHProfileImporter, PortForwardType, SSHProfile, SSHProfileOptions, AutoPrivateKeyLocator } from 'tabby-ssh'
import {
SSHProfileImporter,
PortForwardType,
SSHProfile,
AutoPrivateKeyLocator,
ForwardedPortConfig,
} from 'tabby-ssh'
import { ElectronService } from './services/electron.service'
import SSHConfig, { LineType } from 'ssh-config'
// Enum to delineate the properties in SSHProfile options
enum SSHProfilePropertyNames {
Host = 'host',
Port = 'port',
User = 'user',
X11 = 'x11',
PrivateKeys = 'privateKeys',
KeepaliveInterval = 'keepaliveInterval',
KeepaliveCountMax = 'keepaliveCountMax',
ReadyTimeout = 'readyTimeout',
JumpHost = 'jumpHost',
AgentForward = 'agentForward',
ProxyCommand = 'proxyCommand',
ForwardedPorts = 'forwardedPorts',
}
// Data structure to map the (lowercase) ssh-config attributes (as keys) to a tuple
// containing the name of the corresponding SSHProfile attribute
const decodeFields: Record<string, SSHProfilePropertyNames> = {
hostname: SSHProfilePropertyNames.Host,
user: SSHProfilePropertyNames.User,
port: SSHProfilePropertyNames.Port,
forwardx11: SSHProfilePropertyNames.X11,
serveraliveinterval: SSHProfilePropertyNames.KeepaliveInterval,
serveralivecountmax: SSHProfilePropertyNames.KeepaliveCountMax,
connecttimeout: SSHProfilePropertyNames.ReadyTimeout,
proxyjump: SSHProfilePropertyNames.JumpHost,
forwardagent: SSHProfilePropertyNames.AgentForward,
identityfile: SSHProfilePropertyNames.PrivateKeys,
proxycommand: SSHProfilePropertyNames.ProxyCommand,
localforward: SSHProfilePropertyNames.ForwardedPorts,
remoteforward: SSHProfilePropertyNames.ForwardedPorts,
dynamicforward: SSHProfilePropertyNames.ForwardedPorts,
}
// Function to use the above to return details corresponding to the supplied SSHProperty name.
// If the name of the supplied SSH Config file Property is valid, and one that we process,
// then we get back the name of the corresponding Property in the SSHProfile object
function decodeTarget (SSHProperty: string): string {
const lower = SSHProperty.toLowerCase()
if (lower in decodeFields) {
return decodeFields[lower]
}
return ''
}
// Function to combine SSHConfig values into a single string. This is used to smash
// together the proxyCommand values which are split on whitespace and presented as
// an array of objects in the SSHConfig object
function convertSSHConfigValuesToString (arg: string | string[] | object[]): string {
if (typeof arg === 'string') { return arg }
let allStrings = true
for (const item of arg) {
if (typeof item !== 'string') {
allStrings = false
break
}
}
if (allStrings) {
return arg.join(' ')
}
// Have to explicitly unwrap the arg into a list of objects to avoid Typescript grumbles
const objList: object[] = []
for (const item of arg) {
if ( typeof item === 'object' && 'val' in item ) {
objList.push(item)
}
}
return objList.filter(obj => 'val' in obj)
.map(obj => 'val' in obj ? obj.val as string: '')
.join(' ')
}
// Function to read in the SSH config file and return it as a string
async function readSSHConfigFile (filePath: string): Promise<string> {
try {
return await fs.readFile(filePath, 'utf8')
} catch (err) {
console.error('Error reading SSH config file:', err)
return ''
}
}
// Function to take an ssh-config entry and convert it into an SSHProfile
function convertHostToSSHProfile (host: string, settings: Record<string, string | string[] | object[] >): PartialProfile<SSHProfile> {
// inline function to generate an id for this profile
const deriveID = (name: string) => 'openssh-config:' + slugify(name)
// Start point of the profile, with an ID, name, type and group
const thisProfile: PartialProfile<SSHProfile> = {
id: deriveID(host),
name: `${host} (.ssh/config)`,
type: 'ssh',
group: 'Imported from .ssh/config',
}
const options = {}
function convertToForwardedPortDescriptor (forwardType: PortForwardType.Local | PortForwardType.Remote | PortForwardType.Dynamic, details: string): ForwardedPortConfig {
const detailsParts = details.split(/\s/)
const bindParts = detailsParts[0].trim().split(':')
if (bindParts.length === 1) {
bindParts.unshift('127.0.0.1')
}
let tgtParts = ['', '22']
if ( detailsParts.length > 1 ) {
tgtParts = detailsParts[1].trim().split(':')
}
return {
host: bindParts[0],
port: parseInt(bindParts[1]),
targetAddress: tgtParts[0],
targetPort: parseInt(tgtParts[1]),
type: forwardType,
description: details,
}
}
// for each ssh-config key in turn...
for (const key in settings) {
// decode a target attribute and an action
const targetName = decodeTarget(key)
switch (targetName) {
// The following have single string values
case SSHProfilePropertyNames.User:
case SSHProfilePropertyNames.Host:
case SSHProfilePropertyNames.JumpHost:
const basicString = settings[key]
if (typeof basicString === 'string') {
if (targetName === SSHProfilePropertyNames.JumpHost) {
options[targetName] = deriveID(basicString)
} else {
options[targetName] = basicString
}
} else {
console.log('Unexpected value in settings for ' + key)
}
break
// The following have single integer values
case SSHProfilePropertyNames.Port:
case SSHProfilePropertyNames.KeepaliveInterval:
case SSHProfilePropertyNames.KeepaliveCountMax:
case SSHProfilePropertyNames.ReadyTimeout:
const numberString = settings[key]
if (typeof numberString === 'string') {
options[targetName] = parseInt(numberString, 10)
} else {
console.log('Unexpected value in settings for ' + key)
}
break
// The following have single yes/no values
case SSHProfilePropertyNames.X11:
case SSHProfilePropertyNames.AgentForward:
let booleanString = settings[key]
booleanString = typeof booleanString === 'string' ? booleanString.toLowerCase() : ''
if ( booleanString === 'yes' || booleanString === 'no' ) {
options[targetName] = booleanString === 'yes'
} else {
console.log('Unexpected value in settings for ' + key)
}
break
// ProxyCommand will be an array if unquoted and containing multiple words,
// or a simple string otherwise
case SSHProfilePropertyNames.ProxyCommand:
const proxyCommand = convertSSHConfigValuesToString(settings[key])
options[targetName] = proxyCommand
break
// IdentityFile may have multiple values and the need to have '~' converted to the
// path to the HOME directory
case SSHProfilePropertyNames.PrivateKeys:
const processedKeys: string [] = (settings[key] as string[]).map( s => {
let retVal: string = s
if (s.startsWith('~/')) {
retVal = path.join(process.env.HOME ?? '~', s.slice(2))
}
return retVal
})
options[targetName] = processedKeys
break
// The port forwarding directives all end up in the same space, but with a different value
// in the SSHProfileOptions object
case SSHProfilePropertyNames.ForwardedPorts:
const forwardTypeString = key.toLowerCase()
let forwardType: PortForwardType | null = null
switch (forwardTypeString) {
case 'localforward':
forwardType = PortForwardType.Local
break
case 'remoteforward':
forwardType = PortForwardType.Remote
break
case 'dynamicforward':
forwardType = PortForwardType.Dynamic
break
}
if (forwardType) {
options[targetName] ??= []
for (const forwarderDetails of settings[key]) {
if (typeof forwarderDetails === 'string') {
options[targetName].push(convertToForwardedPortDescriptor(forwardType, forwarderDetails))
}
}
}
break
}
}
thisProfile.options = options
return thisProfile
}
function convertToSSHProfiles (config: SSHConfig): PartialProfile<SSHProfile>[] {
const myMap = new Map<string, PartialProfile<SSHProfile>>()
function noWildCardsInName (name: string) {
return !/[?*]/.test(name)
}
for (const entry of config) {
// Each entry represents a line in the SSH Config. If the line is a 'Host' line,
// then it will also contain the configuration for that identifiable Host.
// There may be more than one host per line and some 'Hosts' have wildcards in their
// names
// If this is a genuine entry rather than a Comment...
// ... and there is a 'Host' Parameter
if (entry.type === LineType.DIRECTIVE && entry.param === 'Host') {
// for each Name in this entry
const hostList: string[] = []
// if there is more than one host specified on this line, then the names will be
// in an array
if (typeof entry.value === 'string') {
hostList.push(entry.value)
} else if (Array.isArray(entry.value)) {
for (const item of entry.value) {
hostList.push(item.val)
}
}
// for each Host identified on this line, check that there are no wildcards in the
// name and that we've not seen the name before.
// If that is the case, then get the full configuration for this name.
// If that has a 'Hostname' property (if that's missing, the name is not usable
// for our purposes) then convert the configuration into an SSHProfile and stash it
for (const host of hostList) {
if (noWildCardsInName(host)) {
if (!(host in myMap)) {
// NOTE: SSHConfig.compute() lies about the return types
const configuration: Record<string, string | string[] | object[]> = config.compute(host)
if (configuration['HostName']) {
myMap[host] = convertHostToSSHProfile(host, configuration)
}
}
}
}
}
}
// Convert the values from the map into a list of Partial SSHProfiles sorted
// by Hostname
return Object.keys(myMap).sort().map(key => myMap[key])
}
@Injectable({ providedIn: 'root' })
export class OpenSSHImporter extends SSHProfileImporter {
async getProfiles (): Promise<PartialProfile<SSHProfile>[]> {
const deriveID = name => 'openssh-config:' + slugify(name)
const results: PartialProfile<SSHProfile>[] = []
const configPath = path.join(process.env.HOME ?? '~', '.ssh', 'config')
try {
const lines = (await fs.readFile(configPath, 'utf8')).split('\n')
const globalOptions: Partial<SSHProfileOptions> = {}
let currentProfile: PartialProfile<SSHProfile>|null = null
for (let line of lines) {
if (line.trim().startsWith('#') || !line.trim()) {
continue
}
if (line.toLowerCase().startsWith('host ')) {
if (currentProfile) {
results.push(currentProfile)
}
const name = line.substr(5).trim()
currentProfile = {
id: deriveID(name),
name: `${name} (.ssh/config)`,
type: 'ssh',
group: 'Imported from .ssh/config',
options: {
...globalOptions,
host: name,
},
}
} else {
const target: Partial<SSHProfileOptions> = currentProfile?.options ?? globalOptions
line = line.trim()
const idx = /\s/.exec(line)?.index ?? -1
if (idx === -1) {
continue
}
const key = line.substr(0, idx).trim()
const value = line.substr(idx + 1).trim()
if (key === 'IdentityFile') {
target.privateKeys = value.split(',').map(s => s.trim()).map(s => {
if (s.startsWith('~')) {
s = path.join(process.env.HOME ?? '~', s.slice(2))
}
return s
})
} else if (key === 'RemoteForward') {
const bind = value.split(/\s/)[0].trim()
const tgt = value.split(/\s/)[1].trim()
target.forwardedPorts ??= []
target.forwardedPorts.push({
type: PortForwardType.Remote,
description: value,
host: bind.split(':')[0] ?? '127.0.0.1',
port: parseInt(bind.split(':')[1] ?? bind),
targetAddress: tgt.split(':')[0],
targetPort: parseInt(tgt.split(':')[1]),
})
} else if (key === 'LocalForward') {
const bind = value.split(/\s/)[0].trim()
const tgt = value.split(/\s/)[1].trim()
target.forwardedPorts ??= []
target.forwardedPorts.push({
type: PortForwardType.Local,
description: value,
host: bind.includes(':') ? bind.split(':')[0] : '127.0.0.1',
port: parseInt(at(bind.split(':'), -1)),
targetAddress: tgt.split(':')[0],
targetPort: parseInt(tgt.split(':')[1]),
})
} else if (key === 'DynamicForward') {
const bind = value.trim()
target.forwardedPorts ??= []
target.forwardedPorts.push({
type: PortForwardType.Dynamic,
description: value,
host: bind.includes(':') ? bind.split(':')[0] : '127.0.0.1',
port: parseInt(at(bind.split(':'), -1)),
targetAddress: '',
targetPort: 22,
})
} else {
const mappedKey = {
hostname: 'host',
host: 'host',
port: 'port',
user: 'user',
forwardx11: 'x11',
serveraliveinterval: 'keepaliveInterval',
serveralivecountmax: 'keepaliveCountMax',
proxycommand: 'proxyCommand',
proxyjump: 'jumpHost',
}[key.toLowerCase()]
if (mappedKey) {
target[mappedKey] = value
}
}
}
}
if (currentProfile) {
results.push(currentProfile)
}
for (const p of results) {
if (p.options?.proxyCommand) {
p.options.proxyCommand = p.options.proxyCommand
.replace('%h', p.options.host ?? '')
.replace('%p', (p.options.port ?? 22).toString())
}
if (p.options?.jumpHost) {
p.options.jumpHost = deriveID(p.options.jumpHost)
}
}
return results
try {
const sshConfigContent = await readSSHConfigFile(configPath)
const config: SSHConfig = SSHConfig.parse(sshConfigContent)
return convertToSSHProfiles(config)
} catch (e) {
if (e.code === 'ENOENT') {
return []

126
yarn.lock
View File

@ -110,6 +110,14 @@
dependencies:
"@babel/highlight" "^7.18.6"
"@babel/code-frame@^7.22.13":
version "7.22.13"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
dependencies:
"@babel/highlight" "^7.22.13"
chalk "^2.4.2"
"@babel/compat-data@^7.20.5":
version "7.20.14"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8"
@ -136,7 +144,7 @@
json5 "^2.2.1"
semver "^6.3.0"
"@babel/generator@^7.19.3", "@babel/generator@^7.20.7":
"@babel/generator@^7.19.3":
version "7.20.14"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce"
integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==
@ -145,6 +153,16 @@
"@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1"
"@babel/generator@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
dependencies:
"@babel/types" "^7.23.0"
"@jridgewell/gen-mapping" "^0.3.2"
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/helper-compilation-targets@^7.19.3":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb"
@ -161,20 +179,25 @@
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
"@babel/helper-function-name@^7.19.0":
version "7.19.0"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c"
integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==
dependencies:
"@babel/template" "^7.18.10"
"@babel/types" "^7.19.0"
"@babel/helper-environment-visitor@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
"@babel/helper-hoist-variables@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==
"@babel/helper-function-name@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies:
"@babel/types" "^7.18.6"
"@babel/template" "^7.22.15"
"@babel/types" "^7.23.0"
"@babel/helper-hoist-variables@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-module-imports@^7.18.6":
version "7.18.6"
@ -211,11 +234,23 @@
dependencies:
"@babel/types" "^7.18.6"
"@babel/helper-split-export-declaration@^7.22.6":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
dependencies:
"@babel/types" "^7.22.5"
"@babel/helper-string-parser@^7.19.4":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
"@babel/helper-string-parser@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
"@babel/helper-validator-identifier@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
@ -226,6 +261,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
"@babel/helper-validator-identifier@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@babel/helper-validator-option@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
@ -249,11 +289,25 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/parser@^7.19.3", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7":
"@babel/highlight@^7.22.13":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54"
integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
dependencies:
"@babel/helper-validator-identifier" "^7.22.20"
chalk "^2.4.2"
js-tokens "^4.0.0"
"@babel/parser@^7.19.3", "@babel/parser@^7.20.7":
version "7.20.15"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89"
integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg==
"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
"@babel/parser@^7.6.0", "@babel/parser@^7.9.6":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.7.tgz#d372dda9c89fcec340a82630a9f533f2fe15877e"
@ -268,23 +322,32 @@
"@babel/parser" "^7.20.7"
"@babel/types" "^7.20.7"
"@babel/traverse@^7.19.3", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.13":
version "7.20.13"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473"
integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==
"@babel/template@^7.22.15":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
dependencies:
"@babel/code-frame" "^7.18.6"
"@babel/generator" "^7.20.7"
"@babel/helper-environment-visitor" "^7.18.9"
"@babel/helper-function-name" "^7.19.0"
"@babel/helper-hoist-variables" "^7.18.6"
"@babel/helper-split-export-declaration" "^7.18.6"
"@babel/parser" "^7.20.13"
"@babel/types" "^7.20.7"
"@babel/code-frame" "^7.22.13"
"@babel/parser" "^7.22.15"
"@babel/types" "^7.22.15"
"@babel/traverse@^7.19.3", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.13":
version "7.23.2"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
dependencies:
"@babel/code-frame" "^7.22.13"
"@babel/generator" "^7.23.0"
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.23.0"
"@babel/helper-hoist-variables" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
"@babel/parser" "^7.23.0"
"@babel/types" "^7.23.0"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.19.3", "@babel/types@^7.20.2", "@babel/types@^7.20.7":
"@babel/types@^7.18.6", "@babel/types@^7.19.3", "@babel/types@^7.20.2", "@babel/types@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f"
integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==
@ -293,6 +356,15 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@babel/types@^7.6.1", "@babel/types@^7.9.6":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.7.tgz#4ed19d51f840ed4bd5645be6ce40775fecf03159"