native-run
Advanced tools
Comparing version 1.1.0 to 1.2.0
@@ -0,1 +1,18 @@ | ||
# [1.2.0](https://github.com/ionic-team/native-run/compare/v1.1.0...v1.2.0) (2020-09-28) | ||
### Bug Fixes | ||
* **iOS:** implement iOS 14 compatibility ([#157](https://github.com/ionic-team/native-run/issues/157)) ([6f242fd](https://github.com/ionic-team/native-run/commit/6f242fd9aa1dea2cd96db13f21b981b21953f3ea)) | ||
### Features | ||
* **android:** gracefully handle when device is offline ([aa6688d](https://github.com/ionic-team/native-run/commit/aa6688d257127c5cf6b24279a6eb506cf5b8c258)) | ||
* **android:** gracefully handle when device is out of space ([9da9f59](https://github.com/ionic-team/native-run/commit/9da9f5968cebdc7887230f3085dfd7c2d5a4f3ec)) | ||
* **android:** handle INSTALL_FAILED_INSUFFICIENT_STORAGE adb error ([bcf2369](https://github.com/ionic-team/native-run/commit/bcf2369b51e6afcd3230eb68db965fe2a89300e1)) | ||
* **android:** kill unresponsive adb server after 5s and retry ([9e1bbc7](https://github.com/ionic-team/native-run/commit/9e1bbc7d636a266ed472e6b43553781bc7e90896)) | ||
* **list:** show model, then ID if no name ([d56415d](https://github.com/ionic-team/native-run/commit/d56415d00c68ce288d6575ebf4cb0386f6070801)) | ||
* columnize `--list` output ([5b7da72](https://github.com/ionic-team/native-run/commit/5b7da7235c23b01185d8317bf5e4cdad878a9845)) | ||
# [1.1.0](https://github.com/ionic-team/native-run/compare/v1.0.0...v1.1.0) (2020-09-10) | ||
@@ -2,0 +19,0 @@ |
@@ -42,4 +42,10 @@ "use strict"; | ||
API Level: ${platform.apiLevel} | ||
Packages: ${platform.packages.map(p => formatPackage(p)).join('\n' + ' '.repeat(22))} | ||
${platform.missingPackages.length > 0 ? `(!) Missing Packages: ${platform.missingPackages.map(p => formatPackage(p)).join('\n' + ' '.repeat(22))}` : ''} | ||
Packages: ${platform.packages | ||
.map(p => formatPackage(p)) | ||
.join('\n' + ' '.repeat(22))} | ||
${platform.missingPackages.length > 0 | ||
? `(!) Missing Packages: ${platform.missingPackages | ||
.map(p => formatPackage(p)) | ||
.join('\n' + ' '.repeat(22))}` | ||
: ''} | ||
`.trim(); | ||
@@ -46,0 +52,0 @@ } |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.unforwardPorts = exports.forwardPorts = exports.parseAdbDevices = exports.startActivity = exports.parseAdbInstallOutput = exports.ADBEvent = exports.uninstallApp = exports.closeApp = exports.installApk = exports.waitForClose = exports.waitForBoot = exports.waitForDevice = exports.getDeviceProperties = exports.getDeviceProperty = exports.getDevices = void 0; | ||
exports.execAdb = exports.unforwardPorts = exports.forwardPorts = exports.parseAdbDevices = exports.startActivity = exports.parseAdbInstallOutput = exports.ADBEvent = exports.uninstallApp = exports.closeApp = exports.installApk = exports.waitForClose = exports.waitForBoot = exports.waitForDevice = exports.getDeviceProperties = exports.getDeviceProperty = exports.getDevices = void 0; | ||
const child_process_1 = require("child_process"); | ||
@@ -22,7 +22,5 @@ const Debug = require("debug"); | ||
const debug = Debug(`${modulePrefix}:${getDevices.name}`); | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const args = ['devices', '-l']; | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
const { stdout } = await process_1.execFile(adbBin, args, { env: sdk_1.supplementProcessEnv(sdk) }); | ||
debug('Invoking adb with args: %O', args); | ||
const stdout = await execAdb(sdk, args, { timeout: 5000 }); | ||
const devices = parseAdbDevices(stdout); | ||
@@ -44,7 +42,5 @@ await Promise.all(devices.map(async (device) => { | ||
const debug = Debug(`${modulePrefix}:${getDeviceProperty.name}`); | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const args = ['-s', device.serial, 'shell', 'getprop', property]; | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
const { stdout } = await process_1.execFile(adbBin, args, { env: sdk_1.supplementProcessEnv(sdk) }); | ||
debug('Invoking adb with args: %O', args); | ||
const stdout = await execAdb(sdk, args, { timeout: 5000 }); | ||
return stdout.trim(); | ||
@@ -55,9 +51,7 @@ } | ||
const debug = Debug(`${modulePrefix}:${getDeviceProperties.name}`); | ||
const re = /^\[([a-z0-9\.]+)\]: \[(.*)\]$/; | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const args = ['-s', device.serial, 'shell', 'getprop']; | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
debug('Invoking adb with args: %O', args); | ||
const stdout = await execAdb(sdk, args, { timeout: 5000 }); | ||
const re = /^\[([a-z0-9.]+)\]: \[(.*)\]$/; | ||
const propAllowList = [...ADB_GETPROP_MAP.keys()]; | ||
const { stdout } = await process_1.execFile(adbBin, args, { env: sdk_1.supplementProcessEnv(sdk) }); | ||
const properties = {}; | ||
@@ -78,7 +72,5 @@ for (const line of stdout.split(os.EOL)) { | ||
const debug = Debug(`${modulePrefix}:${waitForDevice.name}`); | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const args = ['-s', serial, 'wait-for-any-device']; | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
await process_1.execFile(adbBin, args, { env: sdk_1.supplementProcessEnv(sdk) }); | ||
debug('Invoking adb with args: %O', args); | ||
await execAdb(sdk, args); | ||
debug('Device %s is connected to ADB!', serial); | ||
@@ -103,10 +95,8 @@ } | ||
const debug = Debug(`${modulePrefix}:${waitForClose.name}`); | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const args = ['-s', device.serial, 'shell', `ps | grep ${app}`]; | ||
return new Promise(resolve => { | ||
const interval = setInterval(async () => { | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
try { | ||
await process_1.execFile(adbBin, args, { env: sdk_1.supplementProcessEnv(sdk) }); | ||
debug('Invoking adb with args: %O', args); | ||
await execAdb(sdk, args); | ||
} | ||
@@ -128,4 +118,7 @@ catch (e) { | ||
const args = ['-s', device.serial, 'install', '-r', '-t', apk]; | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
const p = child_process_1.spawn(adbBin, args, { stdio: 'pipe', env: sdk_1.supplementProcessEnv(sdk) }); | ||
debug('Invoking adb with args: %O', args); | ||
const p = child_process_1.spawn(adbBin, args, { | ||
stdio: 'pipe', | ||
env: sdk_1.supplementProcessEnv(sdk), | ||
}); | ||
return new Promise((resolve, reject) => { | ||
@@ -160,2 +153,8 @@ p.on('close', code => { | ||
} | ||
else if (event === ADBEvent.NotEnoughSpace) { | ||
reject(new errors_1.ADBException(`Encountered adb error: ${ADBEvent[event]}.`, errors_1.ERR_NOT_ENOUGH_SPACE)); | ||
} | ||
else if (event === ADBEvent.DeviceOffline) { | ||
reject(new errors_1.ADBException(`Encountered adb error: ${ADBEvent[event]}.`, errors_1.ERR_DEVICE_OFFLINE)); | ||
} | ||
cb(); | ||
@@ -168,7 +167,5 @@ })); | ||
const debug = Debug(`${modulePrefix}:${closeApp.name}`); | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const args = ['-s', device.serial, 'shell', 'am', 'force-stop', app]; | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
await process_1.execFile(adbBin, args, { env: sdk_1.supplementProcessEnv(sdk) }); | ||
debug('Invoking adb with args: %O', args); | ||
await execAdb(sdk, args); | ||
} | ||
@@ -178,7 +175,5 @@ exports.closeApp = closeApp; | ||
const debug = Debug(`${modulePrefix}:${uninstallApp.name}`); | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const args = ['-s', device.serial, 'uninstall', app]; | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
await process_1.execFile(adbBin, args, { env: sdk_1.supplementProcessEnv(sdk) }); | ||
debug('Invoking adb with args: %O', args); | ||
await execAdb(sdk, args); | ||
} | ||
@@ -192,2 +187,4 @@ exports.uninstallApp = uninstallApp; | ||
ADBEvent[ADBEvent["NoCertificates"] = 3] = "NoCertificates"; | ||
ADBEvent[ADBEvent["NotEnoughSpace"] = 4] = "NotEnoughSpace"; | ||
ADBEvent[ADBEvent["DeviceOffline"] = 5] = "DeviceOffline"; | ||
})(ADBEvent = exports.ADBEvent || (exports.ADBEvent = {})); | ||
@@ -209,2 +206,9 @@ function parseAdbInstallOutput(line) { | ||
} | ||
else if (line.includes('INSTALL_FAILED_INSUFFICIENT_STORAGE') || | ||
line.includes('not enough space')) { | ||
event = ADBEvent.NotEnoughSpace; | ||
} | ||
else if (line.includes('device offline')) { | ||
event = ADBEvent.DeviceOffline; | ||
} | ||
if (typeof event !== 'undefined') { | ||
@@ -218,7 +222,14 @@ debug('Parsed event from adb install output: %s', ADBEvent[event]); | ||
const debug = Debug(`${modulePrefix}:${startActivity.name}`); | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const args = ['-s', device.serial, 'shell', 'am', 'start', '-W', '-n', `${packageName}/${activityName}`]; | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
await process_1.execFile(adbBin, args, { env: sdk_1.supplementProcessEnv(sdk) }); | ||
const args = [ | ||
'-s', | ||
device.serial, | ||
'shell', | ||
'am', | ||
'start', | ||
'-W', | ||
'-n', | ||
`${packageName}/${activityName}`, | ||
]; | ||
debug('Invoking adb with args: %O', args); | ||
await execAdb(sdk, args, { timeout: 5000 }); | ||
} | ||
@@ -240,3 +251,3 @@ exports.startActivity = startActivity; | ||
.split(/\s+/) | ||
.map(prop => prop.includes(':') ? prop.split(':') : undefined) | ||
.map(prop => (prop.includes(':') ? prop.split(':') : undefined)) | ||
.filter((kv) => typeof kv !== 'undefined' && kv.length >= 2) | ||
@@ -251,3 +262,8 @@ .reduce((acc, [k, v]) => { | ||
const isGenericDevice = (properties['device'] || '').startsWith('generic'); | ||
const type = 'usb' in properties || isIP || !serial.startsWith('emulator') || !isGenericDevice ? 'hardware' : 'emulator'; | ||
const type = 'usb' in properties || | ||
isIP || | ||
!serial.startsWith('emulator') || | ||
!isGenericDevice | ||
? 'hardware' | ||
: 'emulator'; | ||
const connection = 'usb' in properties ? 'usb' : isIP ? 'tcpip' : null; | ||
@@ -277,17 +293,62 @@ devices.push({ | ||
const debug = Debug(`${modulePrefix}:${forwardPorts.name}`); | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const args = ['-s', device.serial, 'reverse', `tcp:${ports.device}`, `tcp:${ports.host}`]; | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
await process_1.execFile(adbBin, args, { env: sdk_1.supplementProcessEnv(sdk) }); | ||
const args = [ | ||
'-s', | ||
device.serial, | ||
'reverse', | ||
`tcp:${ports.device}`, | ||
`tcp:${ports.host}`, | ||
]; | ||
debug('Invoking adb with args: %O', args); | ||
await execAdb(sdk, args, { timeout: 5000 }); | ||
} | ||
exports.forwardPorts = forwardPorts; | ||
async function unforwardPorts(sdk, device, ports) { | ||
const debug = Debug(`${modulePrefix}:${forwardPorts.name}`); | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const args = ['-s', device.serial, 'reverse', '--remove', `tcp:${ports.device}`]; | ||
debug('Invoking adb: %O %O', adbBin, args); | ||
await process_1.execFile(adbBin, args, { env: sdk_1.supplementProcessEnv(sdk) }); | ||
const debug = Debug(`${modulePrefix}:${unforwardPorts.name}`); | ||
const args = [ | ||
'-s', | ||
device.serial, | ||
'reverse', | ||
'--remove', | ||
`tcp:${ports.device}`, | ||
]; | ||
debug('Invoking adb with args: %O', args); | ||
await execAdb(sdk, args, { timeout: 5000 }); | ||
} | ||
exports.unforwardPorts = unforwardPorts; | ||
async function execAdb(sdk, args, options = {}) { | ||
const debug = Debug(`${modulePrefix}:${execAdb.name}`); | ||
let timer; | ||
const retry = async () => { | ||
process.stdout.write(`ADB is unresponsive after ${options.timeout}ms, killing server and retrying...\n`); | ||
debug('ADB timeout of %O reached, killing server and retrying...', options.timeout); | ||
debug('Invoking adb with args: %O', ['kill-server']); | ||
await execAdb(sdk, ['kill-server']); | ||
debug('Invoking adb with args: %O', ['start-server']); | ||
await execAdb(sdk, ['start-server']); | ||
debug('Retrying...'); | ||
return run(); | ||
}; | ||
const run = async () => { | ||
const platformTools = await sdk_1.getSDKPackage(path.join(sdk.root, 'platform-tools')); | ||
const adbBin = path.join(platformTools.location, 'adb'); | ||
const { stdout } = await process_1.execFile(adbBin, args, { | ||
env: sdk_1.supplementProcessEnv(sdk), | ||
}); | ||
if (timer) { | ||
clearTimeout(timer); | ||
timer = undefined; | ||
} | ||
return stdout; | ||
}; | ||
return new Promise((resolve, reject) => { | ||
if (options.timeout) { | ||
timer = setTimeout(() => retry().then(resolve, reject), options.timeout); | ||
} | ||
run().then(resolve, err => { | ||
if (!timer) { | ||
reject(err); | ||
} | ||
}); | ||
}); | ||
} | ||
exports.execAdb = execAdb; |
@@ -12,3 +12,3 @@ "use strict"; | ||
const readStream = await openReadStream(entry); | ||
readStream.on('error', (err) => error = err); | ||
readStream.on('error', (err) => (error = err)); | ||
readStream.on('data', (chunk) => chunks.push(chunk)); | ||
@@ -34,5 +34,6 @@ readStream.on('end', () => zipfile.close()); | ||
const activity = application.childNodes.find((n) => n.nodeName === 'activity'); | ||
const activityName = activity.attributes.find((a) => a.name === 'name').value; | ||
const activityName = activity.attributes.find((a) => a.name === 'name') | ||
.value; | ||
return { appId, activityName }; | ||
} | ||
exports.getApkInfo = getApkInfo; |
@@ -14,13 +14,18 @@ "use strict"; | ||
const modulePrefix = 'native-run:android:utils:avd'; | ||
exports.isAVDINI = (o) => o | ||
&& typeof o['avd.ini.encoding'] === 'string' | ||
&& typeof o['path'] === 'string' | ||
&& typeof o['path.rel'] === 'string' | ||
&& typeof o['target'] === 'string'; | ||
exports.isAVDConfigINI = (o) => o | ||
&& (typeof o['avd.ini.displayname'] === 'undefined' || typeof o['avd.ini.displayname'] === 'string') | ||
&& (typeof o['hw.lcd.density'] === 'undefined' || typeof o['hw.lcd.density'] === 'string') | ||
&& (typeof o['hw.lcd.height'] === 'undefined' || typeof o['hw.lcd.height'] === 'string') | ||
&& (typeof o['hw.lcd.width'] === 'undefined' || typeof o['hw.lcd.width'] === 'string') | ||
&& (typeof o['image.sysdir.1'] === 'undefined' || typeof o['image.sysdir.1'] === 'string'); | ||
exports.isAVDINI = (o) => o && | ||
typeof o['avd.ini.encoding'] === 'string' && | ||
typeof o['path'] === 'string' && | ||
typeof o['path.rel'] === 'string' && | ||
typeof o['target'] === 'string'; | ||
exports.isAVDConfigINI = (o) => o && | ||
(typeof o['avd.ini.displayname'] === 'undefined' || | ||
typeof o['avd.ini.displayname'] === 'string') && | ||
(typeof o['hw.lcd.density'] === 'undefined' || | ||
typeof o['hw.lcd.density'] === 'string') && | ||
(typeof o['hw.lcd.height'] === 'undefined' || | ||
typeof o['hw.lcd.height'] === 'string') && | ||
(typeof o['hw.lcd.width'] === 'undefined' || | ||
typeof o['hw.lcd.width'] === 'string') && | ||
(typeof o['image.sysdir.1'] === 'undefined' || | ||
typeof o['image.sysdir.1'] === 'string'); | ||
async function getAVDINIs(sdk) { | ||
@@ -33,5 +38,7 @@ const debug = Debug(`${modulePrefix}:${getAVDINIs.name}`); | ||
debug('Discovered AVD ini files: %O', iniFilePaths); | ||
const iniFiles = await Promise.all(iniFilePaths.map(async (f) => [f, await ini_1.readINI(f, exports.isAVDINI)])); | ||
const avdInis = iniFiles | ||
.filter((c) => typeof c[1] !== 'undefined'); | ||
const iniFiles = await Promise.all(iniFilePaths.map(async (f) => [ | ||
f, | ||
await ini_1.readINI(f, exports.isAVDINI), | ||
])); | ||
const avdInis = iniFiles.filter((c) => typeof c[1] !== 'undefined'); | ||
return avdInis; | ||
@@ -46,5 +53,11 @@ } | ||
: id.replace(/_/g, ' '); | ||
const screenDPI = configini['hw.lcd.density'] ? Number(configini['hw.lcd.density']) : null; | ||
const screenWidth = configini['hw.lcd.width'] ? Number(configini['hw.lcd.width']) : null; | ||
const screenHeight = configini['hw.lcd.height'] ? Number(configini['hw.lcd.height']) : null; | ||
const screenDPI = configini['hw.lcd.density'] | ||
? Number(configini['hw.lcd.density']) | ||
: null; | ||
const screenWidth = configini['hw.lcd.width'] | ||
? Number(configini['hw.lcd.width']) | ||
: null; | ||
const screenHeight = configini['hw.lcd.height'] | ||
? Number(configini['hw.lcd.height']) | ||
: null; | ||
return { | ||
@@ -106,3 +119,5 @@ id, | ||
if (missingPackages.length > 0) { | ||
throw new errors_1.AVDException(`Unsatisfied packages within API ${api.apiLevel}: ${missingPackages.map(pkg => pkg.path).join(', ')}`, errors_1.ERR_SDK_UNSATISFIED_PACKAGES, 1); | ||
throw new errors_1.AVDException(`Unsatisfied packages within API ${api.apiLevel}: ${missingPackages | ||
.map(pkg => pkg.path) | ||
.join(', ')}`, errors_1.ERR_SDK_UNSATISFIED_PACKAGES, 1); | ||
} | ||
@@ -178,3 +193,3 @@ return createAVDSchematic(sdk, await schema.loadPartialAVDSchematic()); | ||
const stat = await utils_fs_1.statSafe(p); | ||
if (stat && stat.isFile()) { | ||
if (stat === null || stat === void 0 ? void 0 : stat.isFile()) { | ||
return; | ||
@@ -189,3 +204,3 @@ } | ||
const stat = await utils_fs_1.statSafe(skinsrc); | ||
if (stat && stat.isDirectory()) { | ||
if (stat === null || stat === void 0 ? void 0 : stat.isDirectory()) { | ||
debug('Copying skin from %s to %s', skinsrc, skinpath); | ||
@@ -192,0 +207,0 @@ try { |
@@ -245,3 +245,3 @@ "use strict"; | ||
break; | ||
case TypedValue.TYPE_STRING: | ||
case TypedValue.TYPE_STRING: { | ||
const ref = this.readS32(); | ||
@@ -251,3 +251,4 @@ typedValue.value = ref > 0 ? this.strings[ref] : ''; | ||
break; | ||
case TypedValue.TYPE_REFERENCE: | ||
} | ||
case TypedValue.TYPE_REFERENCE: { | ||
const id = this.readU32(); | ||
@@ -257,2 +258,3 @@ typedValue.value = `resourceId:0x${id.toString(16)}`; | ||
break; | ||
} | ||
case TypedValue.TYPE_INT_BOOLEAN: | ||
@@ -313,4 +315,4 @@ typedValue.value = this.readS32() !== 0; | ||
const buf = new ArrayBuffer(4); | ||
(new Int32Array(buf))[0] = int; | ||
return (new Float32Array(buf))[0]; | ||
new Int32Array(buf)[0] = int; | ||
return new Float32Array(buf)[0]; | ||
} | ||
@@ -381,5 +383,3 @@ readString(encoding) { | ||
// debug('sorted:', sorted); | ||
const encoding = (header.flags & StringFlags.UTF8) === StringFlags.UTF8 | ||
? 'utf-8' | ||
: 'ucs2'; | ||
const encoding = (header.flags & StringFlags.UTF8) === StringFlags.UTF8 ? 'utf-8' : 'ucs2'; | ||
// debug('encoding:', encoding); | ||
@@ -478,3 +478,3 @@ const stringsStart = header.startOffset + header.stringsStart; | ||
else { | ||
this.document = (this.parent = node); | ||
this.document = this.parent = node; | ||
} | ||
@@ -481,0 +481,0 @@ this.stack.push(node); |
@@ -44,7 +44,17 @@ "use strict"; | ||
debug('Invoking emulator: %O %O', emulatorBin, args); | ||
const p = child_process_1.spawn(emulatorBin, args, { detached: true, stdio: ['ignore', 'pipe', 'pipe'], env: sdk_1.supplementProcessEnv(sdk) }); | ||
const p = child_process_1.spawn(emulatorBin, args, { | ||
detached: true, | ||
stdio: ['ignore', 'pipe', 'pipe'], | ||
env: sdk_1.supplementProcessEnv(sdk), | ||
}); | ||
p.unref(); | ||
return new Promise((_resolve, _reject) => { | ||
const resolve = fn_1.once(() => { _resolve(); cleanup(); }); | ||
const reject = fn_1.once(err => { _reject(err); cleanup(); }); | ||
const resolve = fn_1.once(() => { | ||
_resolve(); | ||
cleanup(); | ||
}); | ||
const reject = fn_1.once(err => { | ||
_reject(err); | ||
cleanup(); | ||
}); | ||
adb_1.waitForDevice(sdk, `emulator-${port}`).then(() => resolve(), err => reject(err)); | ||
@@ -127,4 +137,5 @@ const eventParser = through2((chunk, enc, cb) => { | ||
debug('Connected to %s:%d', host, port); | ||
utils_fs_1.readFile(path.resolve(os.homedir(), '.emulator_console_auth_token'), { encoding: 'utf8' }) | ||
.then(contents => resolve(contents.trim()), err => reject(err)); | ||
utils_fs_1.readFile(path.resolve(os.homedir(), '.emulator_console_auth_token'), { | ||
encoding: 'utf8', | ||
}).then(contents => resolve(contents.trim()), err => reject(err)); | ||
}); | ||
@@ -158,3 +169,4 @@ }); | ||
debug('Android Console: %O', line); | ||
if (stage === Stage.Initial && line.includes('Authentication required')) { | ||
if (stage === Stage.Initial && | ||
line.includes('Authentication required')) { | ||
stage = Stage.Auth; | ||
@@ -161,0 +173,0 @@ } |
@@ -27,5 +27,2 @@ "use strict"; | ||
id: device.serial, | ||
format() { | ||
return `${this.model} (API ${this.sdkVersion}) ${this.id}`; | ||
}, | ||
}; | ||
@@ -40,7 +37,4 @@ } | ||
id: avd.id, | ||
format() { | ||
return `${this.name} (API ${this.sdkVersion}) ${this.id}`; | ||
}, | ||
}; | ||
} | ||
exports.avdToTarget = avdToTarget; |
@@ -81,3 +81,4 @@ "use strict"; | ||
if (e instanceof errors_1.ADBException) { | ||
if (e.code === errors_1.ERR_INCOMPATIBLE_UPDATE || e.code === errors_1.ERR_VERSION_DOWNGRADE) { | ||
if (e.code === errors_1.ERR_INCOMPATIBLE_UPDATE || | ||
e.code === errors_1.ERR_VERSION_DOWNGRADE) { | ||
process.stdout.write(`${e.message} Uninstalling and trying again...\n`); | ||
@@ -84,0 +85,0 @@ await adb_1.uninstallApp(sdk, device, appId); |
@@ -12,3 +12,3 @@ "use strict"; | ||
.filter((apiLevel) => typeof apiLevel !== 'undefined')), | ||
].sort((a, b) => a <= b ? 1 : -1); | ||
].sort((a, b) => (a <= b ? 1 : -1)); | ||
const apis = levels.map(apiLevel => ({ | ||
@@ -56,3 +56,7 @@ apiLevel, | ||
{ name: 'Android Emulator', path: 'emulator', version: /.+/ }, | ||
{ name: 'Android SDK Platform 29', path: 'platforms;android-29', version: /.+/ }, | ||
{ | ||
name: 'Android SDK Platform 29', | ||
path: 'platforms;android-29', | ||
version: /.+/, | ||
}, | ||
]; | ||
@@ -76,3 +80,7 @@ const missingPackages = findUnsatisfiedPackages(packages, schemas); | ||
{ name: 'Android Emulator', path: 'emulator', version: /.+/ }, | ||
{ name: 'Android SDK Platform 28', path: 'platforms;android-28', version: /.+/ }, | ||
{ | ||
name: 'Android SDK Platform 28', | ||
path: 'platforms;android-28', | ||
version: /.+/, | ||
}, | ||
]; | ||
@@ -96,3 +104,7 @@ const missingPackages = findUnsatisfiedPackages(packages, schemas); | ||
{ name: 'Android Emulator', path: 'emulator', version: /.+/ }, | ||
{ name: 'Android SDK Platform 27', path: 'platforms;android-27', version: /.+/ }, | ||
{ | ||
name: 'Android SDK Platform 27', | ||
path: 'platforms;android-27', | ||
version: /.+/, | ||
}, | ||
]; | ||
@@ -116,3 +128,7 @@ const missingPackages = findUnsatisfiedPackages(packages, schemas); | ||
{ name: 'Android Emulator', path: 'emulator', version: /.+/ }, | ||
{ name: 'Android SDK Platform 26', path: 'platforms;android-26', version: /.+/ }, | ||
{ | ||
name: 'Android SDK Platform 26', | ||
path: 'platforms;android-26', | ||
version: /.+/, | ||
}, | ||
]; | ||
@@ -136,3 +152,7 @@ const missingPackages = findUnsatisfiedPackages(packages, schemas); | ||
{ name: 'Android Emulator', path: 'emulator', version: /.+/ }, | ||
{ name: 'Android SDK Platform 25', path: 'platforms;android-25', version: /.+/ }, | ||
{ | ||
name: 'Android SDK Platform 25', | ||
path: 'platforms;android-25', | ||
version: /.+/, | ||
}, | ||
]; | ||
@@ -156,3 +176,7 @@ const missingPackages = findUnsatisfiedPackages(packages, schemas); | ||
{ name: 'Android Emulator', path: 'emulator', version: /.+/ }, | ||
{ name: 'Android SDK Platform 24', path: 'platforms;android-24', version: /.+/ }, | ||
{ | ||
name: 'Android SDK Platform 24', | ||
path: 'platforms;android-24', | ||
version: /.+/, | ||
}, | ||
]; | ||
@@ -159,0 +183,0 @@ const missingPackages = findUnsatisfiedPackages(packages, schemas); |
@@ -16,3 +16,8 @@ "use strict"; | ||
['linux', [pathlib.join(homedir, 'Android', 'sdk')]], | ||
['win32', [pathlib.join(process.env.LOCALAPPDATA || pathlib.join(homedir, 'AppData', 'Local'), 'Android', 'Sdk')]], | ||
[ | ||
'win32', | ||
[ | ||
pathlib.join(process.env.LOCALAPPDATA || pathlib.join(homedir, 'AppData', 'Local'), 'Android', 'Sdk'), | ||
], | ||
], | ||
]); | ||
@@ -62,6 +67,4 @@ async function getSDK() { | ||
}); | ||
sdk.packages = await Promise.all(contents | ||
.map(p => pathlib.dirname(p)) | ||
.map(p => getSDKPackage(p))); | ||
sdk.packages.sort((a, b) => a.name >= b.name ? 1 : -1); | ||
sdk.packages = await Promise.all(contents.map(p => pathlib.dirname(p)).map(p => getSDKPackage(p))); | ||
sdk.packages.sort((a, b) => (a.name >= b.name ? 1 : -1)); | ||
return sdk.packages; | ||
@@ -107,3 +110,3 @@ } | ||
// defined and valid. | ||
if (process.env.ANDROID_HOME && await fs_1.isDir(process.env.ANDROID_HOME)) { | ||
if (process.env.ANDROID_HOME && (await fs_1.isDir(process.env.ANDROID_HOME))) { | ||
debug('Using $ANDROID_HOME at %s', process.env.ANDROID_HOME); | ||
@@ -114,3 +117,4 @@ return process.env.ANDROID_HOME; | ||
// No valid $ANDROID_HOME, try $ANDROID_SDK_ROOT. | ||
if (process.env.ANDROID_SDK_ROOT && await fs_1.isDir(process.env.ANDROID_SDK_ROOT)) { | ||
if (process.env.ANDROID_SDK_ROOT && | ||
(await fs_1.isDir(process.env.ANDROID_SDK_ROOT))) { | ||
debug('Using $ANDROID_SDK_ROOT at %s', process.env.ANDROID_SDK_ROOT); | ||
@@ -136,3 +140,4 @@ return process.env.ANDROID_SDK_ROOT; | ||
debug('Looking for $ANDROID_EMULATOR_HOME'); | ||
if (process.env.ANDROID_EMULATOR_HOME && await fs_1.isDir(process.env.ANDROID_EMULATOR_HOME)) { | ||
if (process.env.ANDROID_EMULATOR_HOME && | ||
(await fs_1.isDir(process.env.ANDROID_EMULATOR_HOME))) { | ||
debug('Using $ANDROID_EMULATOR_HOME at %s', process.env.$ANDROID_EMULATOR_HOME); | ||
@@ -153,3 +158,4 @@ return process.env.ANDROID_EMULATOR_HOME; | ||
debug('Looking for $ANDROID_AVD_HOME'); | ||
if (process.env.ANDROID_AVD_HOME && await fs_1.isDir(process.env.ANDROID_AVD_HOME)) { | ||
if (process.env.ANDROID_AVD_HOME && | ||
(await fs_1.isDir(process.env.ANDROID_AVD_HOME))) { | ||
debug('Using $ANDROID_AVD_HOME at %s', process.env.$ANDROID_AVD_HOME); | ||
@@ -156,0 +162,0 @@ return process.env.ANDROID_AVD_HOME; |
@@ -7,4 +7,5 @@ "use strict"; | ||
function getAPILevelFromPackageXml(packageXml) { | ||
var _a; | ||
const apiLevel = packageXml.find('./localPackage/type-details/api-level'); | ||
return apiLevel && apiLevel.text ? apiLevel.text.toString() : undefined; | ||
return (_a = apiLevel === null || apiLevel === void 0 ? void 0 : apiLevel.text) === null || _a === void 0 ? void 0 : _a.toString(); | ||
} | ||
@@ -45,3 +46,3 @@ exports.getAPILevelFromPackageXml = getAPILevelFromPackageXml; | ||
]; | ||
const textFromElement = (e) => e && e.text ? e.text.toString() : ''; | ||
const textFromElement = (e) => (e === null || e === void 0 ? void 0 : e.text) ? e.text.toString() : ''; | ||
const versions = []; | ||
@@ -48,0 +49,0 @@ for (const version of versionElements.map(textFromElement)) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.serializeError = exports.IOSRunException = exports.SDKException = exports.AndroidRunException = exports.EmulatorException = exports.AVDException = exports.ADBException = exports.CLIException = exports.ERR_UNSUPPORTED_API_LEVEL = exports.ERR_UNKNOWN_AVD = exports.ERR_NO_TARGET = exports.ERR_NO_DEVICE = exports.ERR_TARGET_NOT_FOUND = exports.ERR_SDK_UNSATISFIED_PACKAGES = exports.ERR_SDK_PACKAGE_NOT_FOUND = exports.ERR_SDK_NOT_FOUND = exports.ERR_UNSUITABLE_API_INSTALLATION = exports.ERR_MISSING_SYSTEM_IMAGE = exports.ERR_NO_AVDS_FOUND = exports.ERR_NON_ZERO_EXIT = exports.ERR_INVALID_SYSTEM_IMAGE = exports.ERR_INVALID_SKIN = exports.ERR_INVALID_SERIAL = exports.ERR_INVALID_SDK_PACKAGE = exports.ERR_NO_CERTIFICATES = exports.ERR_MIN_SDK_VERSION = exports.ERR_VERSION_DOWNGRADE = exports.ERR_INCOMPATIBLE_UPDATE = exports.ERR_EMULATOR_HOME_NOT_FOUND = exports.ERR_AVD_HOME_NOT_FOUND = exports.ERR_ALREADY_RUNNING = exports.ERR_BAD_INPUT = exports.AndroidException = exports.Exception = void 0; | ||
exports.serializeError = exports.IOSRunException = exports.SDKException = exports.AndroidRunException = exports.EmulatorException = exports.AVDException = exports.ADBException = exports.CLIException = exports.ERR_UNSUPPORTED_API_LEVEL = exports.ERR_UNKNOWN_AVD = exports.ERR_NO_TARGET = exports.ERR_NO_DEVICE = exports.ERR_TARGET_NOT_FOUND = exports.ERR_SDK_UNSATISFIED_PACKAGES = exports.ERR_SDK_PACKAGE_NOT_FOUND = exports.ERR_SDK_NOT_FOUND = exports.ERR_UNSUITABLE_API_INSTALLATION = exports.ERR_MISSING_SYSTEM_IMAGE = exports.ERR_NO_AVDS_FOUND = exports.ERR_NON_ZERO_EXIT = exports.ERR_INVALID_SYSTEM_IMAGE = exports.ERR_INVALID_SKIN = exports.ERR_INVALID_SERIAL = exports.ERR_INVALID_SDK_PACKAGE = exports.ERR_DEVICE_OFFLINE = exports.ERR_NOT_ENOUGH_SPACE = exports.ERR_NO_CERTIFICATES = exports.ERR_MIN_SDK_VERSION = exports.ERR_VERSION_DOWNGRADE = exports.ERR_INCOMPATIBLE_UPDATE = exports.ERR_EMULATOR_HOME_NOT_FOUND = exports.ERR_AVD_HOME_NOT_FOUND = exports.ERR_ALREADY_RUNNING = exports.ERR_BAD_INPUT = exports.AndroidException = exports.Exception = void 0; | ||
const json_1 = require("./utils/json"); | ||
@@ -41,2 +41,4 @@ class Exception extends Error { | ||
exports.ERR_NO_CERTIFICATES = 'ERR_NO_CERTIFICATES'; | ||
exports.ERR_NOT_ENOUGH_SPACE = 'ERR_NOT_ENOUGH_SPACE'; | ||
exports.ERR_DEVICE_OFFLINE = 'ERR_DEVICE_OFFLINE'; | ||
exports.ERR_INVALID_SDK_PACKAGE = 'ERR_INVALID_SDK_PACKAGE'; | ||
@@ -43,0 +45,0 @@ exports.ERR_INVALID_SERIAL = 'ERR_INVALID_SERIAL'; |
@@ -31,3 +31,7 @@ "use strict"; | ||
else { | ||
if (!platform || platform === 'help' || args.includes('--help') || args.includes('-h') || platform.startsWith('-')) { | ||
if (!platform || | ||
platform === 'help' || | ||
args.includes('--help') || | ||
args.includes('-h') || | ||
platform.startsWith('-')) { | ||
cmd = await Promise.resolve().then(() => require('./help')); | ||
@@ -34,0 +38,0 @@ return cmd.run(args); |
@@ -24,3 +24,2 @@ "use strict"; | ||
--app <path> ......... Deploy specified .app or .ipa file | ||
@@ -27,0 +26,0 @@ --device ............. Use a device if available |
@@ -53,3 +53,6 @@ "use strict"; | ||
toCString(path).copy(data, 8); | ||
const resp = await this.protocolClient.sendMessage({ operation: afc_1.AFC_OPS.FILE_OPEN, data }); | ||
const resp = await this.protocolClient.sendMessage({ | ||
operation: afc_1.AFC_OPS.FILE_OPEN, | ||
data, | ||
}); | ||
if (resp.operation === afc_1.AFC_OPS.FILE_OPEN_RES) { | ||
@@ -120,3 +123,4 @@ return resp.data; | ||
numOpenFiles++; | ||
_this.uploadFile(filePath, remotePath) | ||
_this | ||
.uploadFile(filePath, remotePath) | ||
.then(() => { | ||
@@ -123,0 +127,0 @@ resolve(); |
@@ -26,10 +26,15 @@ "use strict"; | ||
async lookupApp(bundleIds, options = { | ||
'ReturnAttributes': ['Path', 'Container', 'CFBundleExecutable', 'CFBundleIdentifier'], | ||
'ApplicationsType': 'Any', | ||
ReturnAttributes: [ | ||
'Path', | ||
'Container', | ||
'CFBundleExecutable', | ||
'CFBundleIdentifier', | ||
], | ||
ApplicationsType: 'Any', | ||
}) { | ||
debug(`lookupApp, options: ${JSON.stringify(options)}`); | ||
const resp = await this.protocolClient.sendMessage({ | ||
'Command': 'Lookup', | ||
'ClientOptions': { | ||
'BundleIDs': bundleIds, | ||
Command: 'Lookup', | ||
ClientOptions: { | ||
BundleIDs: bundleIds, | ||
...options, | ||
@@ -46,11 +51,11 @@ }, | ||
async installApp(packagePath, bundleId, options = { | ||
'ApplicationsType': 'Any', | ||
'PackageType': 'Developer', | ||
ApplicationsType: 'Any', | ||
PackageType: 'Developer', | ||
}) { | ||
debug(`installApp, packagePath: ${packagePath}, bundleId: ${bundleId}`); | ||
return this.protocolClient.sendMessage({ | ||
'Command': 'Install', | ||
'PackagePath': packagePath, | ||
'ClientOptions': { | ||
'CFBundleIdentifier': bundleId, | ||
Command: 'Install', | ||
PackagePath: packagePath, | ||
ClientOptions: { | ||
CFBundleIdentifier: bundleId, | ||
...options, | ||
@@ -57,0 +62,0 @@ }, |
@@ -10,3 +10,5 @@ "use strict"; | ||
function isLockdowndServiceResponse(resp) { | ||
return resp.Request === 'StartService' && resp.Service !== undefined && resp.Port !== undefined; | ||
return (resp.Request === 'StartService' && | ||
resp.Service !== undefined && | ||
resp.Port !== undefined); | ||
} | ||
@@ -20,3 +22,5 @@ function isLockdowndSessionResponse(resp) { | ||
function isLockdowndValueResponse(resp) { | ||
return resp.Request === 'GetValue' && resp.Key !== undefined && typeof resp.Value === 'string'; | ||
return (resp.Request === 'GetValue' && | ||
resp.Key !== undefined && | ||
typeof resp.Value === 'string'); | ||
} | ||
@@ -34,4 +38,4 @@ function isLockdowndQueryTypeResponse(resp) { | ||
const resp = await this.protocolClient.sendMessage({ | ||
'Request': 'StartService', | ||
'Service': name, | ||
Request: 'StartService', | ||
Service: name, | ||
}); | ||
@@ -48,5 +52,5 @@ if (isLockdowndServiceResponse(resp)) { | ||
const resp = await this.protocolClient.sendMessage({ | ||
'Request': 'StartSession', | ||
'HostID': pairRecord.HostID, | ||
'SystemBUID': pairRecord.SystemBUID, | ||
Request: 'StartSession', | ||
HostID: pairRecord.HostID, | ||
SystemBUID: pairRecord.SystemBUID, | ||
}); | ||
@@ -72,3 +76,3 @@ if (isLockdowndSessionResponse(resp)) { | ||
debug(`getAllValues`); | ||
const resp = await this.protocolClient.sendMessage({ 'Request': 'GetValue' }); | ||
const resp = await this.protocolClient.sendMessage({ Request: 'GetValue' }); | ||
if (isLockdowndAllValuesResponse(resp)) { | ||
@@ -84,4 +88,4 @@ return resp.Value; | ||
const resp = await this.protocolClient.sendMessage({ | ||
'Request': 'GetValue', | ||
'Key': val, | ||
Request: 'GetValue', | ||
Key: val, | ||
}); | ||
@@ -97,3 +101,5 @@ if (isLockdowndValueResponse(resp)) { | ||
debug('queryType'); | ||
const resp = await this.protocolClient.sendMessage({ 'Request': 'QueryType' }); | ||
const resp = await this.protocolClient.sendMessage({ | ||
Request: 'QueryType', | ||
}); | ||
if (isLockdowndQueryTypeResponse(resp)) { | ||
@@ -100,0 +106,0 @@ return resp.Type; |
@@ -22,6 +22,6 @@ "use strict"; | ||
const resp = await this.protocolClient.sendMessage({ | ||
'Command': 'MountImage', | ||
'ImagePath': imagePath, | ||
'ImageSignature': imageSig, | ||
'ImageType': 'Developer', | ||
Command: 'MountImage', | ||
ImagePath: imagePath, | ||
ImageSignature: imageSig, | ||
ImageType: 'Developer', | ||
}); | ||
@@ -36,6 +36,6 @@ if (!lockdown_1.isLockdownResponse(resp) || resp.Status !== 'Complete') { | ||
return this.protocolClient.sendMessage({ | ||
'Command': 'ReceiveBytes', | ||
'ImageSize': imageSize, | ||
'ImageSignature': imageSig, | ||
'ImageType': 'Developer', | ||
Command: 'ReceiveBytes', | ||
ImageSize: imageSize, | ||
ImageSignature: imageSig, | ||
ImageType: 'Developer', | ||
}, (resp, resolve, reject) => { | ||
@@ -58,4 +58,4 @@ if (isMIMUploadReceiveBytesResponse(resp)) { | ||
return this.protocolClient.sendMessage({ | ||
'Command': 'LookupImage', | ||
'ImageType': 'Developer', | ||
Command: 'LookupImage', | ||
ImageType: 'Developer', | ||
}); | ||
@@ -62,0 +62,0 @@ } |
@@ -38,4 +38,4 @@ "use strict"; | ||
extraFields: { | ||
'DeviceID': device.DeviceID, | ||
'PortNumber': htons(port), | ||
DeviceID: device.DeviceID, | ||
PortNumber: htons(port), | ||
}, | ||
@@ -52,3 +52,5 @@ }); | ||
debug('getDevices'); | ||
const resp = await this.protocolClient.sendMessage({ messageType: 'ListDevices' }); | ||
const resp = await this.protocolClient.sendMessage({ | ||
messageType: 'ListDevices', | ||
}); | ||
if (isUsbmuxdDeviceResponse(resp)) { | ||
@@ -81,3 +83,3 @@ return resp.DeviceList; | ||
messageType: 'ReadPairRecord', | ||
extraFields: { 'PairRecordID': udid }, | ||
extraFields: { PairRecordID: udid }, | ||
}); | ||
@@ -84,0 +86,0 @@ if (isUsbmuxdPairRecordResponse(resp)) { |
@@ -55,6 +55,13 @@ "use strict"; | ||
async getDebugserverClient() { | ||
return this.getServiceClient('com.apple.debugserver', debugserver_1.DebugserverClient, true); | ||
try { | ||
// iOS 14 added support for a secure debug service so try to connect to that first | ||
return await this.getServiceClient('com.apple.debugserver.DVTSecureSocketProxy', debugserver_1.DebugserverClient); | ||
} | ||
catch { | ||
// otherwise, fall back to the previous implementation | ||
return this.getServiceClient('com.apple.debugserver', debugserver_1.DebugserverClient, true); | ||
} | ||
} | ||
async getServiceClient(name, ServiceType, disableSSL = false) { | ||
const { port: servicePort, enableServiceSSL } = await this.lockdowndClient.startService(name); | ||
const { port: servicePort, enableServiceSSL, } = await this.lockdowndClient.startService(name); | ||
const usbmuxClient = new usbmuxd_1.UsbmuxdClient(usbmuxd_1.UsbmuxdClient.connectUsbmuxdSocket()); | ||
@@ -80,3 +87,5 @@ let usbmuxdSocket = await usbmuxClient.connect(this.device, servicePort); | ||
await new Promise((res, rej) => { | ||
const timeoutId = setTimeout(() => { rej('The TLS handshake failed to complete after 5s.'); }, 5000); | ||
const timeoutId = setTimeout(() => { | ||
rej('The TLS handshake failed to complete after 5s.'); | ||
}, 5000); | ||
tls.connect(tlsOptions, function () { | ||
@@ -83,0 +92,0 @@ clearTimeout(timeoutId); |
@@ -14,44 +14,167 @@ "use strict"; | ||
(function (AFC_OPS) { | ||
/** | ||
* Invalid | ||
*/ | ||
AFC_OPS[AFC_OPS["INVALID"] = 0] = "INVALID"; | ||
/** | ||
* Status | ||
*/ | ||
AFC_OPS[AFC_OPS["STATUS"] = 1] = "STATUS"; | ||
/** | ||
* Data | ||
*/ | ||
AFC_OPS[AFC_OPS["DATA"] = 2] = "DATA"; | ||
/** | ||
* ReadDir | ||
*/ | ||
AFC_OPS[AFC_OPS["READ_DIR"] = 3] = "READ_DIR"; | ||
/** | ||
* ReadFile | ||
*/ | ||
AFC_OPS[AFC_OPS["READ_FILE"] = 4] = "READ_FILE"; | ||
/** | ||
* WriteFile | ||
*/ | ||
AFC_OPS[AFC_OPS["WRITE_FILE"] = 5] = "WRITE_FILE"; | ||
/** | ||
* WritePart | ||
*/ | ||
AFC_OPS[AFC_OPS["WRITE_PART"] = 6] = "WRITE_PART"; | ||
/** | ||
* TruncateFile | ||
*/ | ||
AFC_OPS[AFC_OPS["TRUNCATE"] = 7] = "TRUNCATE"; | ||
/** | ||
* RemovePath | ||
*/ | ||
AFC_OPS[AFC_OPS["REMOVE_PATH"] = 8] = "REMOVE_PATH"; | ||
/** | ||
* MakeDir | ||
*/ | ||
AFC_OPS[AFC_OPS["MAKE_DIR"] = 9] = "MAKE_DIR"; | ||
/** | ||
* GetFileInfo | ||
*/ | ||
AFC_OPS[AFC_OPS["GET_FILE_INFO"] = 10] = "GET_FILE_INFO"; | ||
/** | ||
* GetDeviceInfo | ||
*/ | ||
AFC_OPS[AFC_OPS["GET_DEVINFO"] = 11] = "GET_DEVINFO"; | ||
/** | ||
* WriteFileAtomic (tmp file+rename) | ||
*/ | ||
AFC_OPS[AFC_OPS["WRITE_FILE_ATOM"] = 12] = "WRITE_FILE_ATOM"; | ||
/** | ||
* FileRefOpen | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_OPEN"] = 13] = "FILE_OPEN"; | ||
/** | ||
* FileRefOpenResult | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_OPEN_RES"] = 14] = "FILE_OPEN_RES"; | ||
/** | ||
* FileRefRead | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_READ"] = 15] = "FILE_READ"; | ||
/** | ||
* FileRefWrite | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_WRITE"] = 16] = "FILE_WRITE"; | ||
/** | ||
* FileRefSeek | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_SEEK"] = 17] = "FILE_SEEK"; | ||
/** | ||
* FileRefTell | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_TELL"] = 18] = "FILE_TELL"; | ||
/** | ||
* FileRefTellResult | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_TELL_RES"] = 19] = "FILE_TELL_RES"; | ||
/** | ||
* FileRefClose | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_CLOSE"] = 20] = "FILE_CLOSE"; | ||
/** | ||
* FileRefSetFileSize (ftruncate) | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_SET_SIZE"] = 21] = "FILE_SET_SIZE"; | ||
/** | ||
* GetConnectionInfo | ||
*/ | ||
AFC_OPS[AFC_OPS["GET_CON_INFO"] = 22] = "GET_CON_INFO"; | ||
/** | ||
* SetConnectionOptions | ||
*/ | ||
AFC_OPS[AFC_OPS["SET_CON_OPTIONS"] = 23] = "SET_CON_OPTIONS"; | ||
/** | ||
* RenamePath | ||
*/ | ||
AFC_OPS[AFC_OPS["RENAME_PATH"] = 24] = "RENAME_PATH"; | ||
/** | ||
* SetFSBlockSize (0x800000) | ||
*/ | ||
AFC_OPS[AFC_OPS["SET_FS_BS"] = 25] = "SET_FS_BS"; | ||
/** | ||
* SetSocketBlockSize (0x800000) | ||
*/ | ||
AFC_OPS[AFC_OPS["SET_SOCKET_BS"] = 26] = "SET_SOCKET_BS"; | ||
/** | ||
* FileRefLock | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_LOCK"] = 27] = "FILE_LOCK"; | ||
/** | ||
* MakeLink | ||
*/ | ||
AFC_OPS[AFC_OPS["MAKE_LINK"] = 28] = "MAKE_LINK"; | ||
/** | ||
* GetFileHash | ||
*/ | ||
AFC_OPS[AFC_OPS["GET_FILE_HASH"] = 29] = "GET_FILE_HASH"; | ||
/** | ||
* SetModTime | ||
*/ | ||
AFC_OPS[AFC_OPS["SET_FILE_MOD_TIME"] = 30] = "SET_FILE_MOD_TIME"; | ||
/** | ||
* GetFileHashWithRange | ||
*/ | ||
AFC_OPS[AFC_OPS["GET_FILE_HASH_RANGE"] = 31] = "GET_FILE_HASH_RANGE"; | ||
/* iOS 6+ */ | ||
// iOS 6+ | ||
/** | ||
* FileRefSetImmutableHint | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_SET_IMMUTABLE_HINT"] = 32] = "FILE_SET_IMMUTABLE_HINT"; | ||
/** | ||
* GetSizeOfPathContents | ||
*/ | ||
AFC_OPS[AFC_OPS["GET_SIZE_OF_PATH_CONTENTS"] = 33] = "GET_SIZE_OF_PATH_CONTENTS"; | ||
/** | ||
* RemovePathAndContents | ||
*/ | ||
AFC_OPS[AFC_OPS["REMOVE_PATH_AND_CONTENTS"] = 34] = "REMOVE_PATH_AND_CONTENTS"; | ||
/** | ||
* DirectoryEnumeratorRefOpen | ||
*/ | ||
AFC_OPS[AFC_OPS["DIR_OPEN"] = 35] = "DIR_OPEN"; | ||
/** | ||
* DirectoryEnumeratorRefOpenResult | ||
*/ | ||
AFC_OPS[AFC_OPS["DIR_OPEN_RESULT"] = 36] = "DIR_OPEN_RESULT"; | ||
/** | ||
* DirectoryEnumeratorRefRead | ||
*/ | ||
AFC_OPS[AFC_OPS["DIR_READ"] = 37] = "DIR_READ"; | ||
/** | ||
* DirectoryEnumeratorRefClose | ||
*/ | ||
AFC_OPS[AFC_OPS["DIR_CLOSE"] = 38] = "DIR_CLOSE"; | ||
/* iOS 7+ */ | ||
// iOS 7+ | ||
/** | ||
* FileRefReadWithOffset | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_READ_OFFSET"] = 39] = "FILE_READ_OFFSET"; | ||
/** | ||
* FileRefWriteWithOffset | ||
*/ | ||
AFC_OPS[AFC_OPS["FILE_WRITE_OFFSET"] = 40] = "FILE_WRITE_OFFSET"; | ||
@@ -96,11 +219,31 @@ })(AFC_OPS = exports.AFC_OPS || (exports.AFC_OPS = {})); | ||
(function (AFC_FILE_OPEN_FLAGS) { | ||
/** | ||
* r (O_RDONLY) | ||
*/ | ||
AFC_FILE_OPEN_FLAGS[AFC_FILE_OPEN_FLAGS["RDONLY"] = 1] = "RDONLY"; | ||
/** | ||
* r+ (O_RDWR | O_CREAT) | ||
*/ | ||
AFC_FILE_OPEN_FLAGS[AFC_FILE_OPEN_FLAGS["RW"] = 2] = "RW"; | ||
/** | ||
* w (O_WRONLY | O_CREAT | O_TRUNC) | ||
*/ | ||
AFC_FILE_OPEN_FLAGS[AFC_FILE_OPEN_FLAGS["WRONLY"] = 3] = "WRONLY"; | ||
/** | ||
* w+ (O_RDWR | O_CREAT | O_TRUNC) | ||
*/ | ||
AFC_FILE_OPEN_FLAGS[AFC_FILE_OPEN_FLAGS["WR"] = 4] = "WR"; | ||
/** | ||
* a (O_WRONLY | O_APPEND | O_CREAT) | ||
*/ | ||
AFC_FILE_OPEN_FLAGS[AFC_FILE_OPEN_FLAGS["APPEND"] = 5] = "APPEND"; | ||
/** | ||
* a+ (O_RDWR | O_APPEND | O_CREAT) | ||
*/ | ||
AFC_FILE_OPEN_FLAGS[AFC_FILE_OPEN_FLAGS["RDAPPEND"] = 6] = "RDAPPEND"; | ||
})(AFC_FILE_OPEN_FLAGS = exports.AFC_FILE_OPEN_FLAGS || (exports.AFC_FILE_OPEN_FLAGS = {})); | ||
function isAFCResponse(resp) { | ||
return AFC_OPS[resp.operation] !== undefined && resp.id !== undefined && resp.data !== undefined; | ||
return (AFC_OPS[resp.operation] !== undefined && | ||
resp.id !== undefined && | ||
resp.data !== undefined); | ||
} | ||
@@ -107,0 +250,0 @@ function isStatusResponse(resp) { |
@@ -18,2 +18,35 @@ "use strict"; | ||
} | ||
onData(data) { | ||
// the GDB protocol does not support body length in its header so we cannot rely on | ||
// the parent implementation to determine when a payload is complete | ||
try { | ||
// if there's data, add it to the existing buffer | ||
this.buffer = data ? Buffer.concat([this.buffer, data]) : this.buffer; | ||
// do we have enough bytes to proceed | ||
if (this.buffer.length < this.headerSize) { | ||
return; // incomplete header, wait for more | ||
} | ||
// first, check the header | ||
if (this.parseHeader(this.buffer) === -1) { | ||
// we have a valid header so check the body. GDB packets will always be a leading '$', data bytes, | ||
// a trailing '#', and a two digit checksum. minimum valid body is the empty response '$#00' | ||
// https://developer.apple.com/library/archive/documentation/DeveloperTools/gdb/gdb/gdb_33.html | ||
const packetData = this.buffer.toString().match('\\$.*#[0-9a-f]{2}'); | ||
if (packetData == null) { | ||
return; // incomplete body, wait for more | ||
} | ||
// extract the body and update the buffer | ||
const body = Buffer.from(packetData[0]); | ||
this.buffer = this.buffer.slice(this.headerSize + body.length); | ||
// parse the payload and recurse if there is more data to process | ||
this.callback(this.parseBody(body)); | ||
if (this.buffer.length) { | ||
this.onData(); | ||
} | ||
} | ||
} | ||
catch (err) { | ||
this.callback(null, err); | ||
} | ||
} | ||
parseHeader(data) { | ||
@@ -40,3 +73,3 @@ if (data[0] !== ACK_SUCCESS) { | ||
else { | ||
throw new Error('Didn\'t receive checksum'); | ||
throw new Error("Didn't receive checksum"); | ||
} | ||
@@ -51,3 +84,6 @@ } | ||
// hex encode and concat all args | ||
const encodedArgs = args.map(arg => Buffer.from(arg).toString('hex')).join().toUpperCase(); | ||
const encodedArgs = args | ||
.map(arg => Buffer.from(arg).toString('hex')) | ||
.join() | ||
.toUpperCase(); | ||
const checksumStr = calculateChecksum(cmd + encodedArgs); | ||
@@ -54,0 +90,0 @@ const formattedCmd = `$${cmd}${encodedArgs}#${checksumStr}`; |
@@ -93,3 +93,6 @@ "use strict"; | ||
if (callback) { | ||
callback(resp, (...args) => { this.socket.removeListener('data', reader.onData); resolve(...args); }, reject); | ||
callback(resp, (...args) => { | ||
this.socket.removeListener('data', reader.onData); | ||
resolve(...args); | ||
}, reject); | ||
} | ||
@@ -96,0 +99,0 @@ else { |
@@ -34,10 +34,11 @@ "use strict"; | ||
write(socket, msg) { | ||
// TODO Usbmux message type | ||
debug(`socket write: ${JSON.stringify(msg)}`); | ||
const { messageType, extraFields } = msg; | ||
const plistMessage = plist.build({ | ||
'BundleID': 'io.ionic.native-run', | ||
'ClientVersionString': 'usbmux.js', | ||
'MessageType': messageType, | ||
'ProgName': 'native-run', | ||
'kLibUSBMuxVersion': 3, | ||
BundleID: 'io.ionic.native-run', | ||
ClientVersionString: 'usbmux.js', | ||
MessageType: messageType, | ||
ProgName: 'native-run', | ||
kLibUSBMuxVersion: 3, | ||
...extraFields, | ||
@@ -44,0 +45,0 @@ }); |
@@ -46,5 +46,2 @@ "use strict"; | ||
id: device.UniqueDeviceID, | ||
format() { | ||
return `${this.name} ${this.model} ${this.sdkVersion} ${this.id}`; | ||
}, | ||
}; | ||
@@ -58,6 +55,3 @@ } | ||
id: simulator.udid, | ||
format() { | ||
return `${this.name} ${this.sdkVersion} ${this.id}`; | ||
}, | ||
}; | ||
} |
@@ -43,4 +43,6 @@ "use strict"; | ||
const readStream = await openReadStream(entry); | ||
readStream.on('error', (err) => error = err); | ||
readStream.on('end', () => { zipfile.readEntry(); }); | ||
readStream.on('error', (err) => (error = err)); | ||
readStream.on('end', () => { | ||
zipfile.readEntry(); | ||
}); | ||
readStream.pipe(fs_1.createWriteStream(dest)); | ||
@@ -47,0 +49,0 @@ } |
@@ -11,3 +11,5 @@ "use strict"; | ||
async function getSimulators() { | ||
const simctl = child_process_1.spawnSync('xcrun', ['simctl', 'list', '--json'], { encoding: 'utf8' }); | ||
const simctl = child_process_1.spawnSync('xcrun', ['simctl', 'list', '--json'], { | ||
encoding: 'utf8', | ||
}); | ||
if (simctl.status) { | ||
@@ -23,3 +25,4 @@ throw new errors_1.Exception(`Unable to retrieve simulator list: ${simctl.stderr}`); | ||
return output.runtimes | ||
.filter(runtime => runtime.name.indexOf('watch') === -1 && runtime.name.indexOf('tv') === -1) | ||
.filter(runtime => runtime.name.indexOf('watch') === -1 && | ||
runtime.name.indexOf('tv') === -1) | ||
.map(runtime => (output.devices[runtime.identifier] || output.devices[runtime.name]) | ||
@@ -29,3 +32,3 @@ .filter(device => device.isAvailable) | ||
.reduce((prev, next) => prev.concat(next)) // flatten | ||
.sort((a, b) => a.name < b.name ? -1 : 1); | ||
.sort((a, b) => (a.name < b.name ? -1 : 1)); | ||
} | ||
@@ -39,5 +42,8 @@ catch (err) { | ||
debug(`Booting simulator ${udid}`); | ||
const bootResult = child_process_1.spawnSync('xcrun', ['simctl', 'boot', udid], { encoding: 'utf8' }); | ||
const bootResult = child_process_1.spawnSync('xcrun', ['simctl', 'boot', udid], { | ||
encoding: 'utf8', | ||
}); | ||
// TODO: is there a better way to check this? | ||
if (bootResult.status && !bootResult.stderr.includes('Unable to boot device in current state: Booted')) { | ||
if (bootResult.status && | ||
!bootResult.stderr.includes('Unable to boot device in current state: Booted')) { | ||
throw new errors_1.Exception(`There was an error booting simulator: ${bootResult.stderr}`); | ||
@@ -52,3 +58,8 @@ } | ||
debug(`Running simulator ${udid}`); | ||
const openResult = child_process_1.spawnSync('open', [`${xCodePath}/Applications/Simulator.app`, '--args', '-CurrentDeviceUDID', udid], { encoding: 'utf8' }); | ||
const openResult = child_process_1.spawnSync('open', [ | ||
`${xCodePath}/Applications/Simulator.app`, | ||
'--args', | ||
'-CurrentDeviceUDID', | ||
udid, | ||
], { encoding: 'utf8' }); | ||
if (openResult.status) { | ||
@@ -55,0 +66,0 @@ throw new errors_1.Exception(`There was an error opening simulator: ${openResult.stderr}`); |
@@ -9,3 +9,5 @@ "use strict"; | ||
function getXcodeVersionInfo() { | ||
const xcodeVersionInfo = child_process_1.spawnSync('xcodebuild', ['-version'], { encoding: 'utf8' }); | ||
const xcodeVersionInfo = child_process_1.spawnSync('xcodebuild', ['-version'], { | ||
encoding: 'utf8', | ||
}); | ||
if (xcodeVersionInfo.error) { | ||
@@ -25,3 +27,5 @@ throw xcodeVersionInfo.error; | ||
try { | ||
const { stdout } = await process_1.execFile('xcode-select', ['-p'], { encoding: 'utf8' }); | ||
const { stdout } = await process_1.execFile('xcode-select', ['-p'], { | ||
encoding: 'utf8', | ||
}); | ||
if (stdout) { | ||
@@ -28,0 +32,0 @@ return stdout.trim(); |
@@ -7,3 +7,3 @@ "use strict"; | ||
const stats = await utils_fs_1.statSafe(p); | ||
if (stats && stats.isDirectory()) { | ||
if (stats === null || stats === void 0 ? void 0 : stats.isDirectory()) { | ||
return true; | ||
@@ -10,0 +10,0 @@ } |
@@ -5,4 +5,4 @@ "use strict"; | ||
function stringify(obj) { | ||
return JSON.stringify(obj, (k, v) => v instanceof RegExp ? v.toString() : v, '\t'); | ||
return JSON.stringify(obj, (k, v) => (v instanceof RegExp ? v.toString() : v), '\t'); | ||
} | ||
exports.stringify = stringify; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.formatTargets = void 0; | ||
const utils_terminal_1 = require("@ionic/utils-terminal"); | ||
const errors_1 = require("../errors"); | ||
@@ -47,7 +48,23 @@ const json_1 = require("./json"); | ||
else { | ||
for (const target of targets) { | ||
output += ` ${(target.format())}\n`; | ||
} | ||
output += formatTargetTable(targets) + '\n'; | ||
} | ||
return output; | ||
} | ||
function formatTargetTable(targets) { | ||
const spacer = utils_terminal_1.indent(2); | ||
return (spacer + | ||
utils_terminal_1.columnar(targets.map(targetToRow), { | ||
headers: ['Name', 'API', 'Target ID'], | ||
vsep: ' ', | ||
}) | ||
.split('\n') | ||
.join(`\n${spacer}`)); | ||
} | ||
function targetToRow(target) { | ||
var _a, _b, _c, _d; | ||
return [ | ||
(_c = (_b = (_a = target.name) !== null && _a !== void 0 ? _a : target.model) !== null && _b !== void 0 ? _b : target.id) !== null && _c !== void 0 ? _c : '?', | ||
`${target.platform === 'ios' ? 'iOS' : 'API'} ${target.sdkVersion}`, | ||
(_d = target.id) !== null && _d !== void 0 ? _d : '?', | ||
]; | ||
} |
@@ -16,3 +16,8 @@ "use strict"; | ||
exports.onBeforeExit = onBeforeExit; | ||
const BEFORE_EXIT_SIGNALS = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGBREAK']; | ||
const BEFORE_EXIT_SIGNALS = [ | ||
'SIGINT', | ||
'SIGTERM', | ||
'SIGHUP', | ||
'SIGBREAK', | ||
]; | ||
const beforeExitHandlerWrapper = (signal) => fn_1.once(async () => { | ||
@@ -19,0 +24,0 @@ debug('onBeforeExit handler: %s received', signal); |
@@ -7,6 +7,7 @@ "use strict"; | ||
const yauzl = await Promise.resolve().then(() => require('yauzl')); | ||
const open = util_1.promisify(yauzl.open.bind(yauzl)); | ||
return new Promise(async (resolve, reject) => { | ||
try { | ||
const zipfile = await open(srcPath, { lazyEntries: true }); | ||
return new Promise((resolve, reject) => { | ||
yauzl.open(srcPath, { lazyEntries: true }, (err, zipfile) => { | ||
if (!zipfile || err) { | ||
return reject(err); | ||
} | ||
const openReadStream = util_1.promisify(zipfile.openReadStream.bind(zipfile)); | ||
@@ -19,8 +20,5 @@ zipfile.once('error', reject); | ||
zipfile.readEntry(); | ||
} | ||
catch (error) { | ||
reject(error); | ||
} | ||
}); | ||
}); | ||
} | ||
exports.unzip = unzip; |
{ | ||
"name": "native-run", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "A CLI for running apps on iOS/Android devices and simulators/emulators", | ||
@@ -13,3 +13,6 @@ "bin": { | ||
"test": "jest --maxWorkers=4", | ||
"lint": "true", | ||
"lint": "npm run eslint && npm run prettier -- --check", | ||
"fmt": "npm run eslint -- --fix && npm run prettier -- --write", | ||
"prettier": "prettier \"**/*.ts\"", | ||
"eslint": "eslint . --ext .ts", | ||
"publish:ci": "semantic-release", | ||
@@ -39,2 +42,3 @@ "publish:testing": "npm version prerelease --preid=testing --no-git-tag-version && npm publish --tag=testing && git stash", | ||
"@ionic/utils-fs": "^3.0.0", | ||
"@ionic/utils-terminal": "^2.3.0", | ||
"bplist-parser": "0.2.0", | ||
@@ -51,2 +55,4 @@ "debug": "^4.1.1", | ||
"devDependencies": { | ||
"@ionic/eslint-config": "^0.3.0", | ||
"@ionic/prettier-config": "^1.0.1", | ||
"@semantic-release/changelog": "^5.0.0", | ||
@@ -61,7 +67,10 @@ "@semantic-release/git": "^9.0.0", | ||
"@types/plist": "^3.0.2", | ||
"@types/slice-ansi": "^4.0.0", | ||
"@types/split2": "^2.1.6", | ||
"@types/through2": "^2.0.34", | ||
"@types/yauzl": "^2.9.1", | ||
"eslint": "^7.8.1", | ||
"husky": "^4.0.9", | ||
"jest": "^26.4.2", | ||
"prettier": "^2.1.1", | ||
"semantic-release": "^17.1.1", | ||
@@ -71,2 +80,14 @@ "ts-jest": "^26.3.0", | ||
}, | ||
"prettier": "@ionic/prettier-config", | ||
"eslintConfig": { | ||
"extends": "@ionic/eslint-config/recommended", | ||
"rules": { | ||
"@typescript-eslint/explicit-module-boundary-types": [ | ||
"warn", | ||
{ | ||
"allowArgumentsExplicitlyTypedAsAny": true | ||
} | ||
] | ||
} | ||
}, | ||
"release": { | ||
@@ -73,0 +94,0 @@ "branches": "stable", |
@@ -1,8 +0,8 @@ | ||
[![Build Status](https://circleci.com/gh/ionic-team/native-run.svg?style=shield)](https://circleci.com/gh/ionic-team/native-run) | ||
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) | ||
[![npm](https://img.shields.io/npm/v/native-run.svg)](https://www.npmjs.com/package/native-run) | ||
[![github-actions](https://img.shields.io/github/workflow/status/ionic-team/native-run/CI/develop?style=flat-square)](https://github.com/ionic-team/native-run/actions?query=workflow%3ACI) | ||
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](https://github.com/semantic-release/semantic-release) | ||
[![npm](https://img.shields.io/npm/v/native-run.svg?style=flat-square)](https://www.npmjs.com/package/native-run) | ||
# native-run | ||
`native-run` is a cross-platform command-line utility for running native binaries on devices and simulators/emulators. It supports deploying both `.apk` and `.ipa` files to Android and iOS devices. | ||
`native-run` is a cross-platform command-line utility for running native app binaries (`.ipa` and `.apk` files) on iOS and Android devices. It can be used for both hardware and virtual devices. | ||
@@ -13,20 +13,28 @@ This tool is used by the Ionic CLI, but it can be used standalone as part of a development or testing pipeline for launching apps. It doesn't matter whether the `.apk` or `.ipa` is created with Cordova or native IDEs, `native-run` will be able to deploy it. | ||
`native-run` is written entirely in TypeScript/NodeJS and it has no native dependencies. It requires NodeJS 10 or later. To install, run: | ||
`native-run` is written entirely in TypeScript/NodeJS, so there are no native dependencies. | ||
```console | ||
$ npm install -g native-run | ||
To install, run: | ||
``` | ||
npm install -g native-run | ||
``` | ||
:memo: Requires NodeJS 10+ | ||
## Usage | ||
```console | ||
$ native-run <platform> [options] | ||
``` | ||
native-run <platform> [options] | ||
``` | ||
See the help documentation with the `--help` flag. | ||
```console | ||
$ native-run --help | ||
$ native-run ios --help | ||
$ native-run android --help | ||
``` | ||
native-run --help | ||
native-run ios --help | ||
native-run android --help | ||
``` | ||
### Troubleshooting | ||
Much more information can be printed to the screen with the `--verbose` flag. |
2175348
4854
40
11
22
+ Added@ionic/utils-terminal@^2.3.0
+ Added@ionic/utils-terminal@2.3.5(transitive)
+ Added@types/slice-ansi@4.0.0(transitive)
+ Addedansi-regex@5.0.1(transitive)
+ Addedansi-styles@4.3.0(transitive)
+ Addedastral-regex@2.0.0(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedemoji-regex@8.0.0(transitive)
+ Addedis-fullwidth-code-point@3.0.0(transitive)
+ Addedsignal-exit@3.0.7(transitive)
+ Addedslice-ansi@4.0.0(transitive)
+ Addedstring-width@4.2.3(transitive)
+ Addedstrip-ansi@6.0.1(transitive)
+ Addeduntildify@4.0.0(transitive)
+ Addedwrap-ansi@7.0.0(transitive)