diff --git a/automation/build-bin.ts b/automation/build-bin.ts index 30bfa6a0..82aefebf 100755 --- a/automation/build-bin.ts +++ b/automation/build-bin.ts @@ -1,6 +1,22 @@ -import * as path from 'path'; -import * as fs from 'fs-extra'; +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import * as filehound from 'filehound'; +import * as fs from 'fs-extra'; +import * as path from 'path'; import { exec as execPkg } from 'pkg'; const ROOT = path.join(__dirname, '..'); diff --git a/automation/capitanodoc/index.ts b/automation/capitanodoc/index.ts index ced97fd7..16f0527a 100644 --- a/automation/capitanodoc/index.ts +++ b/automation/capitanodoc/index.ts @@ -1,27 +1,46 @@ -import capitanodoc = require('../../capitanodoc'); +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import * as _ from 'lodash'; import * as path from 'path'; + +import capitanodoc = require('../../capitanodoc'); +import { Category, Document } from './doc-types'; import * as markdown from './markdown'; -import { Document, Category } from './doc-types'; -const result = {}; -result.title = capitanodoc.title; -result.introduction = capitanodoc.introduction; -result.categories = []; +const result: Document = { + title: capitanodoc.title, + introduction: capitanodoc.introduction, + categories: [], +}; -for (let commandCategory of capitanodoc.categories) { - const category = {}; - category.title = commandCategory.title; - category.commands = []; +for (const commandCategory of capitanodoc.categories) { + const category: Category = { + title: commandCategory.title, + commands: [], + }; - for (let file of commandCategory.files) { + for (const file of commandCategory.files) { // tslint:disable-next-line:no-var-requires const actions: any = require(path.join(process.cwd(), file)); if (actions.signature) { category.commands.push(_.omit(actions, 'action')); } else { - for (let actionName of Object.keys(actions)) { + for (const actionName of Object.keys(actions)) { const actionCommand = actions[actionName]; category.commands.push(_.omit(actionCommand, 'action')); } diff --git a/automation/capitanodoc/markdown.ts b/automation/capitanodoc/markdown.ts index 4a83809e..10c364bc 100644 --- a/automation/capitanodoc/markdown.ts +++ b/automation/capitanodoc/markdown.ts @@ -1,7 +1,24 @@ -import * as _ from 'lodash'; +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import * as ent from 'ent'; +import * as _ from 'lodash'; + +import { Category, Command, Document } from './doc-types'; import * as utils from './utils'; -import { Document, Category, Command } from './doc-types'; export function renderCommand(command: Command) { let result = `## ${ent.encode(command.signature)}\n\n${command.help}\n`; @@ -9,7 +26,7 @@ export function renderCommand(command: Command) { if (!_.isEmpty(command.options)) { result += '\n### Options'; - for (let option of command.options!) { + for (const option of command.options!) { result += `\n\n#### ${utils.parseSignature(option)}\n\n${ option.description }`; @@ -24,7 +41,7 @@ export function renderCommand(command: Command) { export function renderCategory(category: Category) { let result = `# ${category.title}\n`; - for (let command of category.commands) { + for (const command of category.commands) { result += `\n${renderCommand(command)}`; } @@ -51,10 +68,10 @@ function getAnchor(command: Command) { export function renderToc(categories: Category[]) { let result = `# Table of contents\n`; - for (let category of categories) { + for (const category of categories) { result += `\n- ${category.title}\n\n`; - for (let command of category.commands) { + for (const command of category.commands) { result += `\t- [${ent.encode(command.signature)}](${getAnchor( command, )})\n`; @@ -69,7 +86,7 @@ export function render(doc: Document) { doc.categories, )}`; - for (let category of doc.categories) { + for (const category of doc.categories) { result += `\n${renderCategory(category)}`; } diff --git a/automation/capitanodoc/utils.ts b/automation/capitanodoc/utils.ts index 53725f9c..405b7ec6 100644 --- a/automation/capitanodoc/utils.ts +++ b/automation/capitanodoc/utils.ts @@ -1,6 +1,22 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import { OptionDefinition } from 'capitano'; -import * as _ from 'lodash'; import * as ent from 'ent'; +import * as _ from 'lodash'; export function getOptionPrefix(signature: string) { if (signature.length > 1) { @@ -18,7 +34,7 @@ export function parseSignature(option: OptionDefinition) { let result = getOptionSignature(option.signature); if (_.isArray(option.alias)) { - for (let alias of option.alias) { + for (const alias of option.alias) { result += `, ${getOptionSignature(alias)}`; } } else if (_.isString(option.alias)) { diff --git a/automation/deploy-bin.ts b/automation/deploy-bin.ts index e08eb932..8201f8ae 100644 --- a/automation/deploy-bin.ts +++ b/automation/deploy-bin.ts @@ -1,10 +1,27 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as archiver from 'archiver'; import * as Promise from 'bluebird'; -import * as path from 'path'; -import * as os from 'os'; import * as fs from 'fs-extra'; import * as mkdirp from 'mkdirp'; +import * as os from 'os'; +import * as path from 'path'; import * as publishRelease from 'publish-release'; -import * as archiver from 'archiver'; + import * as packageJSON from '../package.json'; const publishReleaseAsync = Promise.promisify(publishRelease); @@ -26,12 +43,12 @@ mkdirpAsync(path.dirname(outputFile)) new Promise((resolve, reject) => { console.log('Zipping build...'); - let archive = archiver('zip', { + const archive = archiver('zip', { zlib: { level: 7 }, }); archive.directory(path.join(ROOT, 'build-bin'), 'balena-cli'); - let outputStream = fs.createWriteStream(outputFile); + const outputStream = fs.createWriteStream(outputFile); outputStream.on('close', resolve); outputStream.on('error', reject); @@ -48,7 +65,7 @@ mkdirpAsync(path.dirname(outputFile)) console.log('Publishing build...'); return publishReleaseAsync({ - token: GITHUB_TOKEN, + token: GITHUB_TOKEN || '', owner: 'balena-io', repo: 'balena-cli', tag: version, diff --git a/lib/actions/environment-variables.ts b/lib/actions/environment-variables.ts index f95bf999..03b12df7 100644 --- a/lib/actions/environment-variables.ts +++ b/lib/actions/environment-variables.ts @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { ApplicationVariable, DeviceVariable } from 'balena-sdk'; import { CommandDefinition } from 'capitano'; - -import * as commandOptions from './command-options'; -import { normalizeUuidProp } from '../utils/normalization'; -import { DeviceVariable, ApplicationVariable } from 'balena-sdk'; import { stripIndent } from 'common-tags'; +import { normalizeUuidProp } from '../utils/normalization'; +import * as commandOptions from './command-options'; + const getReservedPrefixes = async (): Promise => { const balena = (await import('balena-sdk')).fromSharedOptions(); const settings = await balena.settings.getAll(); diff --git a/lib/actions/local/flash.ts b/lib/actions/local/flash.ts index cb4e1beb..b11c406b 100644 --- a/lib/actions/local/flash.ts +++ b/lib/actions/local/flash.ts @@ -17,17 +17,17 @@ limitations under the License. import { CommandDefinition } from 'capitano'; import chalk from 'chalk'; import { stripIndent } from 'common-tags'; -import * as sdk from 'etcher-sdk'; +import * as SDK from 'etcher-sdk'; async function getDrive(options: { drive?: string; -}): Promise { +}): Promise { const sdk = await import('etcher-sdk'); const adapter = new sdk.scanner.adapters.BlockDeviceAdapter(() => false); const scanner = new sdk.scanner.Scanner([adapter]); await scanner.start(); - let drive: sdk.sourceDestination.BlockDevice; + let drive: SDK.sourceDestination.BlockDevice; if (options.drive !== undefined) { const d = scanner.getBy('device', options.drive); if (d === undefined || !(d instanceof sdk.sourceDestination.BlockDevice)) { @@ -49,7 +49,6 @@ export const flash: CommandDefinition< > = { signature: 'local flash ', description: 'Flash an image to a drive', - //root: true, help: stripIndent` Use this command to flash a balenaOS image to a drive. @@ -111,7 +110,7 @@ export const flash: CommandDefinition< // onFail console.log(chalk.red.bold(error.message)); }, - (progress: sdk.multiWrite.MultiDestinationProgress) => { + (progress: SDK.multiWrite.MultiDestinationProgress) => { // onProgress progressBars[progress.type].update(progress); }, diff --git a/lib/actions/push.ts b/lib/actions/push.ts index 7aa3a0ae..ee634d01 100644 --- a/lib/actions/push.ts +++ b/lib/actions/push.ts @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { BalenaSDK } from 'balena-sdk'; import { CommandDefinition } from 'capitano'; import { stripIndent } from 'common-tags'; -import { BalenaSDK } from 'balena-sdk'; import { registrySecretsHelp } from '../utils/messages'; diff --git a/lib/actions/tunnel.ts b/lib/actions/tunnel.ts index eb215123..be7526ef 100644 --- a/lib/actions/tunnel.ts +++ b/lib/actions/tunnel.ts @@ -14,11 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ import * as Bluebird from 'bluebird'; -import * as _ from 'lodash'; import { CommandDefinition } from 'capitano'; import { stripIndent } from 'common-tags'; +import * as _ from 'lodash'; +import { createServer, Server, Socket } from 'net'; import { isArray } from 'util'; -import { Socket, Server, createServer } from 'net'; + import { tunnelConnectionToDevice } from '../utils/tunnel'; interface Args { @@ -30,7 +31,7 @@ interface Options { } class DeviceIsOfflineError extends Error { - uuid: string; + public uuid: string; constructor(uuid: string) { super(`Device '${uuid}' is offline`); this.uuid = uuid; @@ -68,7 +69,7 @@ export const tunnel: CommandDefinition = { # map remote port 22222 to localhost:22222 $ balena tunnel abcde12345 -p 22222 - + # map remote port 22222 to localhost:222 $ balena tunnel abcde12345 -p 22222:222 @@ -146,29 +147,30 @@ export const tunnel: CommandDefinition = { } // grab the groups + // tslint:disable-next-line:prefer-const let [, remotePort, localAddress, localPort] = regexResult; if ( - !isValidPort(parseInt(localPort)) || - !isValidPort(parseInt(remotePort)) + !isValidPort(parseInt(localPort, undefined)) || + !isValidPort(parseInt(remotePort, undefined)) ) { throw new InvalidPortMappingError(mapping); } // default bind to localAddress - if (localAddress == undefined) { + if (localAddress == null) { localAddress = 'localhost'; } // default use same port number locally as remote - if (localPort == undefined) { + if (localPort == null) { localPort = remotePort; } return { - localPort: parseInt(localPort), + localPort: parseInt(localPort, undefined), localAddress, - remotePort: parseInt(remotePort), + remotePort: parseInt(remotePort, undefined), }; }) .map(({ localPort, localAddress, remotePort }) => { diff --git a/lib/errors.ts b/lib/errors.ts index 1b582d5a..728b65ec 100644 --- a/lib/errors.ts +++ b/lib/errors.ts @@ -14,11 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ +import * as Promise from 'bluebird'; +import { stripIndent } from 'common-tags'; import * as _ from 'lodash'; import * as os from 'os'; import * as Raven from 'raven'; -import * as Promise from 'bluebird'; -import { stripIndent } from 'common-tags'; import * as patterns from './utils/patterns'; diff --git a/lib/events.ts b/lib/events.ts index bc52d0ac..415a9fd6 100644 --- a/lib/events.ts +++ b/lib/events.ts @@ -1,10 +1,26 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import BalenaSdk = require('balena-sdk'); +import Promise = require('bluebird'); import * as Capitano from 'capitano'; - import _ = require('lodash'); import Mixpanel = require('mixpanel'); import Raven = require('raven'); -import Promise = require('bluebird'); -import BalenaSdk = require('balena-sdk'); + import packageJSON = require('../package.json'); const getBalenaSdk = _.once(() => BalenaSdk.fromSharedOptions()); diff --git a/lib/utils/compose.coffee b/lib/utils/compose.coffee index 36a9f1f8..6d886cf5 100644 --- a/lib/utils/compose.coffee +++ b/lib/utils/compose.coffee @@ -333,6 +333,7 @@ tagServiceImages = (docker, images, serviceImages) -> Promise.map images, (d) -> serviceImage = serviceImages[d.serviceName] imageName = serviceImage.is_stored_at__image_location + # coffeelint: disable-next-line=check_scope ("Variable is assigned to but never read") [ _match, registry, repo, tag = 'latest' ] = /(.*?)\/(.*?)(?::([^/]*))?$/.exec(imageName) name = "#{registry}/#{repo}" docker.getImage(d.name).tag({ repo: name, tag, force: true }) diff --git a/lib/utils/compose.d.ts b/lib/utils/compose.d.ts index 53c753c0..ad523597 100644 --- a/lib/utils/compose.d.ts +++ b/lib/utils/compose.d.ts @@ -16,9 +16,10 @@ */ import * as Bluebird from 'bluebird'; -import * as Stream from 'stream'; import { Composition } from 'resin-compose-parse'; +import * as Stream from 'stream'; import { Pack } from 'tar-stream'; + import Logger = require('./logger'); interface Image { diff --git a/lib/utils/compose_ts.ts b/lib/utils/compose_ts.ts index e8394d11..dd747dee 100644 --- a/lib/utils/compose_ts.ts +++ b/lib/utils/compose_ts.ts @@ -14,13 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as _ from 'lodash'; import * as Bluebird from 'bluebird'; -import * as tar from 'tar-stream'; -import { Readable } from 'stream'; - -import * as MultiBuild from 'resin-multibuild'; +import * as _ from 'lodash'; import { Composition } from 'resin-compose-parse'; +import * as MultiBuild from 'resin-multibuild'; +import { Readable } from 'stream'; +import * as tar from 'tar-stream'; import { DeviceInfo } from './device/api'; import Logger = require('./logger'); diff --git a/lib/utils/config.ts b/lib/utils/config.ts index 8bad954c..a7be3c36 100644 --- a/lib/utils/config.ts +++ b/lib/utils/config.ts @@ -13,13 +13,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import Promise = require('bluebird'); import BalenaSdk = require('balena-sdk'); +import Promise = require('bluebird'); import * as semver from 'resin-semver'; const balena = BalenaSdk.fromSharedOptions(); -type ImgConfig = { +interface ImgConfig { applicationName: string; applicationId: number; deviceType: string; @@ -46,7 +46,7 @@ type ImgConfig = { deviceId?: number; uuid?: string; registered_at?: number; -}; +} export function generateBaseConfig( application: BalenaSdk.Application, diff --git a/lib/utils/device/api.ts b/lib/utils/device/api.ts index e42b5ef4..d3877913 100644 --- a/lib/utils/device/api.ts +++ b/lib/utils/device/api.ts @@ -1,9 +1,24 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import * as Bluebird from 'bluebird'; import * as request from 'request'; import * as Stream from 'stream'; import Logger = require('../logger'); - import * as ApiErrors from './errors'; export interface DeviceResponse { @@ -145,10 +160,11 @@ export class DeviceAPI { opts: T, logger?: Logger, ): Promise { - const Bluebird = await import('bluebird'); const _ = await import('lodash'); - type ObjectWithUrl = { url?: string }; + interface ObjectWithUrl { + url?: string; + } if (logger != null) { let url: string | null = null; diff --git a/lib/utils/device/deploy.ts b/lib/utils/device/deploy.ts index 5646076d..146fda78 100644 --- a/lib/utils/device/deploy.ts +++ b/lib/utils/device/deploy.ts @@ -28,15 +28,15 @@ import { import * as semver from 'resin-semver'; import { Readable } from 'stream'; -import Logger = require('../logger'); -import { displayBuildLog } from './logs'; import { makeBuildTasks } from '../compose_ts'; +import Logger = require('../logger'); import { DeviceInfo } from './api'; import * as LocalPushErrors from './errors'; +import { displayBuildLog } from './logs'; // Define the logger here so the debug output // can be used everywhere -const logger = new Logger(); +const globalLogger = new Logger(); export interface DeviceDeployOptions { source: string; @@ -61,7 +61,7 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { exitWithExpectedError(`Could not access source directory: ${opts.source}`); } - const api = new DeviceAPI(logger, opts.deviceHost); + const api = new DeviceAPI(globalLogger, opts.deviceHost); // First check that we can access the device with a ping try { @@ -88,9 +88,9 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { exitWithExpectedError(versionError); } - logger.logInfo(`Starting build on device ${opts.deviceHost}`); + globalLogger.logInfo(`Starting build on device ${opts.deviceHost}`); - const project = await loadProject(logger, opts.source, 'local'); + const project = await loadProject(globalLogger, opts.source, 'local'); // Attempt to attach to the device's docker daemon const docker = connectToDocker( @@ -108,11 +108,11 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { tarStream, docker, deviceInfo, - logger, + globalLogger, opts, ); - logger.logDebug('Setting device state...'); + globalLogger.logDebug('Setting device state...'); // Now set the target state on the device const currentTargetState = await api.getTargetState(); @@ -121,18 +121,18 @@ export async function deployToDevice(opts: DeviceDeployOptions): Promise { currentTargetState, project.composition, ); - logger.logDebug(`Sending target state: ${JSON.stringify(targetState)}`); + globalLogger.logDebug(`Sending target state: ${JSON.stringify(targetState)}`); await api.setTargetState(targetState); - // Print an empty newline to seperate the build output + // Print an empty newline to separate the build output // from the device output console.log(); - logger.logInfo('Streaming device logs...'); + globalLogger.logInfo('Streaming device logs...'); // Now all we need to do is stream back the logs const logStream = await api.getLogStream(); - await displayDeviceLogs(logStream, logger); + await displayDeviceLogs(logStream, globalLogger); } function connectToDocker(host: string, port: number): Docker { @@ -226,7 +226,7 @@ async function assignDockerBuildOpts( // that we can use all of them for cache const images = await getDeviceDockerImages(docker); - logger.logDebug(`Using ${images.length} on-device images for cache...`); + globalLogger.logDebug(`Using ${images.length} on-device images for cache...`); await Bluebird.map(buildTasks, async (task: BuildTask) => { task.dockerOpts = { diff --git a/lib/utils/helpers.ts b/lib/utils/helpers.ts index 7e691797..6406e51e 100644 --- a/lib/utils/helpers.ts +++ b/lib/utils/helpers.ts @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import os = require('os'); -import Bluebird = require('bluebird'); -import _ = require('lodash'); -import chalk from 'chalk'; -import rindle = require('rindle'); -import visuals = require('resin-cli-visuals'); import BalenaSdk = require('balena-sdk'); +import Bluebird = require('bluebird'); +import chalk from 'chalk'; +import _ = require('lodash'); +import os = require('os'); +import visuals = require('resin-cli-visuals'); +import rindle = require('rindle'); import { InitializeEmitter, OperationState } from 'balena-device-init'; @@ -29,7 +29,7 @@ const waitStreamAsync = Bluebird.promisify(rindle.wait); const balena = BalenaSdk.fromSharedOptions(); export function getGroupDefaults(group: { - options: { name: string; default?: string }[]; + options: Array<{ name: string; default?: string }>; }): { [name: string]: string | undefined } { return _.chain(group) .get('options') diff --git a/lib/utils/ignore.ts b/lib/utils/ignore.ts index 111f7e4f..5ba170bc 100644 --- a/lib/utils/ignore.ts +++ b/lib/utils/ignore.ts @@ -120,7 +120,7 @@ export class FileIgnorer { }); return !_.some(ignoreTypes, ({ handle }) => handle.ignores(relFile)); - }; + }; // tslint:disable-line:semicolon private addEntry( pattern: string, diff --git a/lib/utils/logger.ts b/lib/utils/logger.ts index 816e4087..cc9bfb37 100644 --- a/lib/utils/logger.ts +++ b/lib/utils/logger.ts @@ -1,3 +1,19 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import chalk from 'chalk'; import _ = require('lodash'); import { EOL as eol } from 'os'; @@ -45,31 +61,31 @@ class Logger { this.formatMessage = logger.formatWithPrefix.bind(logger); } - logInfo(msg: string) { + public logInfo(msg: string) { return this.streams.info.write(msg + eol); } - logDebug(msg: string) { + public logDebug(msg: string) { return this.streams.debug.write(msg + eol); } - logSuccess(msg: string) { + public logSuccess(msg: string) { return this.streams.success.write(msg + eol); } - logWarn(msg: string) { + public logWarn(msg: string) { return this.streams.warn.write(msg + eol); } - logError(msg: string) { + public logError(msg: string) { return this.streams.error.write(msg + eol); } - logBuild(msg: string) { + public logBuild(msg: string) { return this.streams.build.write(msg + eol); } - logLogs(msg: string) { + public logLogs(msg: string) { return this.streams.logs.write(msg + eol); } } diff --git a/lib/utils/patterns.ts b/lib/utils/patterns.ts index 0789ffb3..3258dadf 100644 --- a/lib/utils/patterns.ts +++ b/lib/utils/patterns.ts @@ -13,15 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import BalenaSdk = require('balena-sdk'); +import Promise = require('bluebird'); +import chalk from 'chalk'; +import _ = require('lodash'); import _form = require('resin-cli-form'); import _visuals = require('resin-cli-visuals'); -import _ = require('lodash'); -import Promise = require('bluebird'); -import BalenaSdk = require('balena-sdk'); -import chalk from 'chalk'; -import validation = require('./validation'); import messages = require('./messages'); +import validation = require('./validation'); const getBalenaSdk = _.once(() => BalenaSdk.fromSharedOptions()); @@ -173,7 +173,9 @@ export function selectOrCreateApplication() { return balena.models.application .hasAny() .then(hasAnyApplications => { - if (!hasAnyApplications) return; + if (!hasAnyApplications) { + return; + } return balena.models.application.getAll().then(applications => { const appOptions = _.map< diff --git a/lib/utils/promote.ts b/lib/utils/promote.ts index 0d788c72..37bfc58f 100644 --- a/lib/utils/promote.ts +++ b/lib/utils/promote.ts @@ -1,9 +1,24 @@ -import { stripIndent } from 'common-tags'; +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import * as BalenaSdk from 'balena-sdk'; - -import Logger = require('./logger'); +import { stripIndent } from 'common-tags'; import { runCommand } from './helpers'; +import Logger = require('./logger'); import { exec, execBuffered } from './ssh'; const MIN_BALENAOS_VERSION = 'v2.14.0'; @@ -34,7 +49,7 @@ export async function join( logger.logDebug('Determining application...'); const app = await getOrSelectApplication(sdk, deviceType, appName); logger.logDebug(`Using application: ${app.app_name} (${app.device_type})`); - if (app.device_type != deviceType) { + if (app.device_type !== deviceType) { logger.logDebug(`Forcing device type to: ${deviceType}`); app.device_type = deviceType; } @@ -206,28 +221,12 @@ async function getOrSelectApplication( .value(); if (!appName) { - const options = { - $filter: { device_type: { $in: compatibleDeviceTypes } }, - }; - - // No application specified, show a list to select one. - const applications = await sdk.models.application.getAll(options); - - if (applications.length === 0) { - const shouldCreateApp = await form.ask({ - message: - 'You have no applications this device can join.\n' + - 'Would you like to create one now?', - type: 'confirm', - default: true, - }); - if (shouldCreateApp) { - return createApplication(sdk, deviceType); - } - process.exit(1); - } - - return selectAppFromList(applications); + return createOrSelectAppOrExit( + form, + sdk, + compatibleDeviceTypes, + deviceType, + ); } const options: BalenaSdk.PineOptionsFor = {}; @@ -279,6 +278,39 @@ async function getOrSelectApplication( return selectAppFromList(applications); } +// TODO: revisit this function's purpose. It was refactored out of +// `getOrSelectApplication` above in order to satisfy some resin-lint v3 +// rules, but it looks like there's a fair amount of duplicate logic. +async function createOrSelectAppOrExit( + form: any, + sdk: BalenaSdk.BalenaSDK, + compatibleDeviceTypes: string[], + deviceType: string, +) { + const options = { + $filter: { device_type: { $in: compatibleDeviceTypes } }, + }; + + // No application specified, show a list to select one. + const applications = await sdk.models.application.getAll(options); + + if (applications.length === 0) { + const shouldCreateApp = await form.ask({ + message: + 'You have no applications this device can join.\n' + + 'Would you like to create one now?', + type: 'confirm', + default: true, + }); + if (shouldCreateApp) { + return createApplication(sdk, deviceType); + } + process.exit(1); + } + + return selectAppFromList(applications); +} + async function createApplication( sdk: BalenaSdk.BalenaSDK, deviceType: string, @@ -294,7 +326,7 @@ async function createApplication( } username = username.toLowerCase(); - const appName = await new Promise(async (resolve, reject) => { + const applicationName = await new Promise(async (resolve, reject) => { while (true) { try { const appName = await form.ask({ @@ -327,7 +359,7 @@ async function createApplication( }); return sdk.models.application.create({ - name: appName, + name: applicationName, deviceType, }); } diff --git a/lib/utils/remote-build.ts b/lib/utils/remote-build.ts index d45d4cfe..c4c4fa4a 100644 --- a/lib/utils/remote-build.ts +++ b/lib/utils/remote-build.ts @@ -13,14 +13,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import { BalenaSDK } from 'balena-sdk'; import * as Bluebird from 'bluebird'; import * as JSONStream from 'JSONStream'; import * as readline from 'readline'; import * as request from 'request'; -import * as Stream from 'stream'; -import { BalenaSDK } from 'balena-sdk'; -import { Pack } from 'tar-stream'; import { RegistrySecrets } from 'resin-multibuild'; +import * as Stream from 'stream'; +import { Pack } from 'tar-stream'; import { TypedError } from 'typed-error'; import { exitWithExpectedError } from '../utils/patterns'; @@ -278,7 +278,7 @@ function createRemoteBuildRequest( ); } } else { - let msgArr = [ + const msgArr = [ 'Remote builder responded with HTTP error:', `${response.statusCode} ${response.statusMessage}`, ]; diff --git a/lib/utils/ssh.ts b/lib/utils/ssh.ts index 8c46a1a6..1542c063 100644 --- a/lib/utils/ssh.ts +++ b/lib/utils/ssh.ts @@ -1,5 +1,21 @@ -import { spawn } from 'child_process'; +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import * as Bluebird from 'bluebird'; +import { spawn } from 'child_process'; import { TypedError } from 'typed-error'; import { getSubShellCommand } from './helpers'; @@ -41,7 +57,7 @@ export async function exec( ps.stdout.pipe(stdout); } }); - if (exitCode != 0) { + if (exitCode !== 0) { throw new ExecError(cmd, exitCode); } } diff --git a/lib/utils/streams.ts b/lib/utils/streams.ts index c06a2e4d..6d2034a1 100644 --- a/lib/utils/streams.ts +++ b/lib/utils/streams.ts @@ -1,3 +1,19 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import Promise = require('bluebird'); import fs = require('fs'); @@ -15,9 +31,9 @@ export function buffer( }).then( () => new Promise(function(resolve, reject) { - const stream = fs.createReadStream(bufferFile); + const fstream = fs.createReadStream(bufferFile); - stream.on('open', () => resolve(stream)).on('error', reject); + fstream.on('open', () => resolve(fstream)).on('error', reject); }), ); } diff --git a/lib/utils/tunnel.ts b/lib/utils/tunnel.ts index 9df7b822..1d02104b 100644 --- a/lib/utils/tunnel.ts +++ b/lib/utils/tunnel.ts @@ -13,16 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import * as Bluebird from 'bluebird'; import { BalenaSDK } from 'balena-sdk'; +import * as Bluebird from 'bluebird'; import { Socket } from 'net'; import { TypedError } from 'typed-error'; const PROXY_CONNECT_TIMEOUT_MS = 10000; class UnableToConnectError extends TypedError { - status: string; - statusCode: string; + public status: string; + public statusCode: string; constructor(statusCode: string, status: string) { super(`Unable to connect: ${statusCode} ${status}`); this.status = status; @@ -31,7 +31,7 @@ class UnableToConnectError extends TypedError { } class RemoteSocketNotListening extends TypedError { - port: number; + public port: number; constructor(port: number) { super(`Device is not listening on port ${port}`); } @@ -104,7 +104,7 @@ const openPortThroughProxy = ( const [httpStatus] = data.toString('utf8').split('\r\n'); const [, httpStatusCode, ...httpMessage] = httpStatus.split(' '); - if (parseInt(httpStatusCode) === 200) { + if (parseInt(httpStatusCode, 10) === 200) { proxyTunnel.setTimeout(0); resolve(proxyTunnel); } else { diff --git a/lib/utils/update.ts b/lib/utils/update.ts index 1ac001b7..a74be8df 100644 --- a/lib/utils/update.ts +++ b/lib/utils/update.ts @@ -14,8 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as UpdateNotifier from 'update-notifier'; import isRoot = require('is-root'); +import * as UpdateNotifier from 'update-notifier'; + import packageJSON = require('../../package.json'); // Check for an update once a day. 1 day granularity should be diff --git a/lib/utils/visuals/custom-dynamic-list.ts b/lib/utils/visuals/custom-dynamic-list.ts index 341fef28..01ae6ce0 100644 --- a/lib/utils/visuals/custom-dynamic-list.ts +++ b/lib/utils/visuals/custom-dynamic-list.ts @@ -1,3 +1,19 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import DynamicList = require('inquirer-dynamic-list'); export abstract class CustomDynamicList extends DynamicList { @@ -9,7 +25,7 @@ export abstract class CustomDynamicList extends DynamicList { protected abstract format(thing: T): string; - refresh(): void { + public refresh(): void { this.opt.choices.choices = []; this.opt.choices.realChoices = []; for (const thing of this.getThings()) { @@ -18,7 +34,7 @@ export abstract class CustomDynamicList extends DynamicList { this.render(); } - async run(): Promise { + public async run(): Promise { this.refresh(); return await super.run(); } diff --git a/package.json b/package.json index 081475d8..1a131923 100644 --- a/package.json +++ b/package.json @@ -66,14 +66,16 @@ "@types/archiver": "2.1.2", "@types/bluebird": "3.5.21", "@types/common-tags": "1.4.0", + "@types/dockerode": "2.5.5", "@types/es6-promise": "0.0.32", "@types/fs-extra": "5.0.4", "@types/is-root": "1.0.0", - "@types/lodash": "4.14.103", + "@types/lodash": "4.14.112", "@types/mkdirp": "0.5.2", "@types/node": "6.14.2", "@types/prettyjson": "0.0.28", "@types/raven": "2.5.1", + "@types/tar-stream": "1.6.0", "catch-uncommitted": "^1.0.0", "ent": "^2.2.0", "filehound": "^1.16.2", @@ -85,10 +87,10 @@ "gulp-shell": "^0.5.2", "mochainon": "^2.0.0", "pkg": "^4.3.0-beta.1", - "prettier": "^1.14.2", + "prettier": "^1.15.3", "publish-release": "^1.3.3", "require-npm4-to-publish": "^1.0.0", - "resin-lint": "^2.0.1", + "resin-lint": "^3.0.1", "rewire": "^3.0.2", "ts-node": "^4.0.1", "typescript": "2.8.1" @@ -113,7 +115,7 @@ "balena-settings-client": "^4.0.0", "balena-sync": "^10.0.2", "bash": "0.0.1", - "bluebird": "^3.3.3", + "bluebird": "^3.5.3", "body-parser": "^1.14.1", "capitano": "^1.9.0", "chalk": "^2.3.0", @@ -123,9 +125,9 @@ "columnify": "^1.5.2", "common-tags": "^1.7.2", "denymount": "^2.2.0", - "docker-progress": "^3.0.4", + "docker-progress": "^3.0.5", "docker-qemu-transpose": "^0.5.3", - "docker-toolbelt": "^3.3.5", + "docker-toolbelt": "^3.3.7", "dockerode": "^2.5.5", "dockerode-options": "^0.2.1", "ejs": "^2.5.7", @@ -157,10 +159,10 @@ "request": "^2.81.0", "resin-cli-form": "^2.0.1", "resin-cli-visuals": "^1.4.0", - "resin-compose-parse": "^2.0.0", + "resin-compose-parse": "^2.0.3", "resin-doodles": "0.0.1", "resin-image-fs": "^5.0.2", - "resin-multibuild": "^2.1.2", + "resin-multibuild": "^2.1.4", "resin-release": "^1.2.0", "resin-semver": "^1.4.0", "resin-stream-logger": "^0.1.2", diff --git a/typings/JSONStream.d.ts b/typings/JSONStream.d.ts index 0c9909f7..0f285853 100644 --- a/typings/JSONStream.d.ts +++ b/typings/JSONStream.d.ts @@ -1,3 +1,20 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + // These are the DefinitelyTyped typings for JSONStream, but because of this // mismatch in case of jsonstream and JSONStream, it is necessary to include // them this way, with an upper case module declaration. They have also @@ -15,22 +32,21 @@ declare module 'JSONStream' { recurse: boolean; } - export function parse(pattern: any): NodeJS.ReadWriteStream; - export function parse(patterns: any[]): NodeJS.ReadWriteStream; + export function parse(pattern: any | any[]): NodeJS.ReadWriteStream; + + type NewlineOnlyIndicator = false; /** * Create a writable stream. * you may pass in custom open, close, and seperator strings. But, by default, * JSONStream.stringify() will create an array, * (with default options open='[\n', sep='\n,\n', close='\n]\n') + * + * If you call JSONStream.stringify(false) the elements will only be separated by a newline. */ - export function stringify(): NodeJS.ReadWriteStream; - - /** If you call JSONStream.stringify(false) the elements will only be seperated by a newline. */ export function stringify( - newlineOnly: NewlineOnlyIndicator, + newlineOnly?: NewlineOnlyIndicator, ): NodeJS.ReadWriteStream; - type NewlineOnlyIndicator = false; /** * Create a writable stream. diff --git a/typings/balena-device-init.d.ts b/typings/balena-device-init.d.ts index 205d1da7..7af56477 100644 --- a/typings/balena-device-init.d.ts +++ b/typings/balena-device-init.d.ts @@ -1,7 +1,23 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ declare module 'balena-device-init' { + import { DeviceType } from 'balena-sdk'; import * as Promise from 'bluebird'; import { EventEmitter } from 'events'; - import { DeviceType } from 'balena-sdk'; interface OperationState { operation: @@ -55,8 +71,7 @@ declare module 'balena-device-init' { } interface InitializeEmitter { - on(event: 'stdout', callback: (msg: string) => void): void; - on(event: 'stderr', callback: (msg: string) => void): void; + on(event: 'stdout' | 'stderr', callback: (msg: string) => void): void; on(event: 'state', callback: (state: OperationState) => void): void; on(event: 'burn', callback: (state: BurnProgress) => void): void; } diff --git a/typings/color-hash.d.ts b/typings/color-hash.d.ts index 8acd5be0..1eb731e8 100644 --- a/typings/color-hash.d.ts +++ b/typings/color-hash.d.ts @@ -1,11 +1,27 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ declare module 'color-hash' { interface Hasher { hex(text: string): string; } class ColorHash { - hex(text: string): string; - rgb(text: string): [number, number, number]; + public hex(text: string): string; + public rgb(text: string): [number, number, number]; } export = ColorHash; diff --git a/typings/inquire-dynamic-list.d.ts b/typings/inquire-dynamic-list.d.ts index ae79b4bc..eac1019c 100644 --- a/typings/inquire-dynamic-list.d.ts +++ b/typings/inquire-dynamic-list.d.ts @@ -1,3 +1,19 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ declare module 'inquirer-dynamic-list' { interface Choice { name: string; @@ -5,7 +21,7 @@ declare module 'inquirer-dynamic-list' { } class DynamicList { - opt: { + public opt: { choices: { choices: Choice[]; realChoices: Choice[]; @@ -17,9 +33,9 @@ declare module 'inquirer-dynamic-list' { emptyMessage?: string; choices: Choice[]; }); - addChoice(choice: Choice): void; - render(): void; - run(): Promise; + public addChoice(choice: Choice): void; + public render(): void; + public run(): Promise; } export = DynamicList; diff --git a/typings/nplugm.d.ts b/typings/nplugm.d.ts index ed56b8dd..a9e983de 100644 --- a/typings/nplugm.d.ts +++ b/typings/nplugm.d.ts @@ -1,4 +1,20 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ declare module 'nplugm' { import Promise = require('bluebird'); - export function list(regexp: RegExp): Promise>; + export function list(regexp: RegExp): Promise; } diff --git a/typings/rindle.d.ts b/typings/rindle.d.ts index 019ad9fd..36e53cd7 100644 --- a/typings/rindle.d.ts +++ b/typings/rindle.d.ts @@ -1,3 +1,19 @@ +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ declare module 'rindle' { export function extract( stream: NodeJS.ReadableStream, @@ -6,7 +22,7 @@ declare module 'rindle' { export function wait( stream: { - on(event: string, callback: Function): void; + on(event: string, callback: (...args: any[]) => void): void; }, callback: (error: Error, data: string) => void, ): void; diff --git a/typings/update-notifier.d.ts b/typings/update-notifier.d.ts index 267b4601..5c45293f 100644 --- a/typings/update-notifier.d.ts +++ b/typings/update-notifier.d.ts @@ -1,4 +1,22 @@ -// Based on the official types at https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/update-notifier/index.d.ts +/** + * @license + * Copyright 2019 Balena Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Based on the official types at: +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/update-notifier/index.d.ts // but fixed to handle options correctly declare module 'update-notifier' { @@ -12,10 +30,10 @@ declare module 'update-notifier' { class UpdateNotifier { constructor(settings?: Settings); - update: UpdateInfo; - check(): void; - checkNpm(): void; - notify(customMessage?: NotifyOptions): void; + public update: UpdateInfo; + public check(): void; + public checkNpm(): void; + public notify(customMessage?: NotifyOptions): void; } interface Settings {