You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@serverless/cli

Package Overview
Dependencies
Maintainers
4
Versions
76
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@serverless/cli - npm Package Compare versions

Comparing version
2.0.22
to
2.0.23
+1
-1
package.json
{
"name": "@serverless/cli",
"version": "2.0.22",
"version": "2.0.23",
"description": "Serverless Components CLI",

@@ -5,0 +5,0 @@ "main": "./src/index.js",

+179
-163

@@ -7,6 +7,4 @@ const os = require('os')

const prettyoutput = require('prettyoutput')
const { sleep } = require('./utils')
const packageJson = require('../package.json')
// Serverless Components CLI Colors
// CLI Colors
const grey = chalk.dim

@@ -16,70 +14,54 @@ const green = chalk.rgb(0, 253, 88)

/**
* CLI
* - Controls the CLI experience in the framework.
* - Once instantiated, it starts a single, long running process.
*/
class CLI {
constructor(config = {}) {
this.command = config.command
this.version = packageJson.version
this.debugMode = config.debug || false
constructor() {}
/**
* Start
* - Starts the CLI process
*/
start(config = {}) {
// Defaults
this._ = {}
this._.entity = 'Serverless'
this._.useTimer = true
this._.startTime = Date.now()
this._.seconds = 0
// Status defaults
this._.status = {}
this._.status.running = false
this._.status.message = 'Running'
this._.status.loadingDots = ''
this._.status.loadingDotCount = 0
// Hide cursor always, to keep it clean
this._.status = 'Initializing'
this._.lastStatus = null
this._.debug = config.debug || false
this._.timer = config.timer || false
this._.timerStarted = Date.now()
this._.timerSeconds = 0
this._.loadingDots = ''
this._.loadingDotCount = 0
// Hide cursor, to keep it clean
process.stdout.write(ansiEscapes.cursorHide)
// Event Handler: Control + C
process.on('SIGINT', async () => {
if (this.isStatusEngineActive()) {
return this.statusEngineStop('cancel')
}
process.exit(1)
})
if (this._.debug) {
// Create a white space immediately
this.log()
}
// Count seconds
// Start counting seconds
setInterval(() => {
this._.seconds = Math.floor((Date.now() - this._.startTime) / 1000)
this._.timerSeconds = Math.floor((Date.now() - this._.timerStarted) / 1000)
}, 1000)
}
getRelativeVerticalCursorPosition(contentString) {
const base = 1
const terminalWidth = process.stdout.columns
const contentWidth = stripAnsi(contentString).length
const nudges = Math.ceil(Number(contentWidth) / Number(terminalWidth))
return base + nudges
}
// Set Event Handler: Control + C to cancel session
process.on('SIGINT', async () => {
return this.close('cancel')
})
async statusEngine() {
this.renderStatus()
await sleep(100)
if (this.isStatusEngineActive()) {
return this.statusEngine()
}
// Start render engine
return this._renderEngine()
}
isStatusEngineActive() {
return this._.status.running
}
statusEngineStart() {
if (this.debugMode) {
this.log()
}
this._.status.running = true
// Start Status engine
return this.statusEngine()
}
statusEngineStop(reason, message) {
this._.status.running = false
/**
* Close
* - Closes the CLI process with relevant, clean information.
*/
close(reason, message) {
if (reason === 'error') {

@@ -89,6 +71,6 @@ message = red(message)

if (reason === 'cancel') {
message = red('canceled')
message = red('Canceled')
}
if (reason === 'done') {
message = green(message || 'done')
message = green(message || 'Done')
}

@@ -102,9 +84,9 @@

this.log()
let content = ' '
if (this._.useTimer) {
content += ` ${grey(this._.seconds + 's')}`
content += ` ${grey(figures.pointerSmall)}`
let content = ''
if (this._.timer) {
content += `${grey(this._.timerSeconds + 's')}`
content += ` ${grey(figures.pointerSmall)} `
}
content += ` ${this._.entity}`
content += ` ${grey(figures.pointerSmall)} ${message}`
content += `${this._.entity} `
content += `${grey(figures.pointerSmall)} ${message}`
process.stdout.write(content)

@@ -124,59 +106,25 @@

renderStatus(status, entity) {
// Start Status engine, if it isn't running yet
if (!this.isStatusEngineActive()) {
this.statusEngineStart()
}
/**
* Debug Mode
* - Is debug mode enabled
*/
debugMode() {
return this._.debug
}
// Set global status
if (status) {
this._.status.message = status
}
// Set global status
if (entity) {
this._.entity = entity
}
// Loading dots
if (this._.status.loadingDotCount === 0) {
this._.status.loadingDots = `.`
} else if (this._.status.loadingDotCount === 2) {
this._.status.loadingDots = `..`
} else if (this._.status.loadingDotCount === 4) {
this._.status.loadingDots = `...`
} else if (this._.status.loadingDotCount === 6) {
this._.status.loadingDots = ''
}
this._.status.loadingDotCount++
if (this._.status.loadingDotCount > 8) {
this._.status.loadingDotCount = 0
}
// Clear any existing content
process.stdout.write(ansiEscapes.eraseDown)
// Write content
console.log() // eslint-disable-line
let content = ' '
if (this._.useTimer) {
content += ` ${grey(this._.seconds + 's')}`
content += ` ${grey(figures.pointerSmall)}`
}
content += ` ${this._.entity}`
content += ` ${grey(figures.pointerSmall)} ${grey(this._.status.message)}`
content += ` ${grey(this._.status.loadingDots)}`
process.stdout.write(content)
console.log() // eslint-disable-line
// Get cursor starting position according to terminal & content width
const startingPosition = this.getRelativeVerticalCursorPosition(content)
// Put cursor to starting position for next view
process.stdout.write(ansiEscapes.cursorUp(startingPosition))
process.stdout.write(ansiEscapes.cursorLeft)
/**
* Status
* - Update status in the CLI session
* - Renders every 100ms
*/
status(status, entity) {
this._.status = status || this._.status
this._.entity = entity || this._.entity
}
renderLog(msg) {
/**
* Log
* - Render log statements cleanly
*/
log(msg) {
if (!msg || msg == '') {

@@ -189,5 +137,5 @@ console.log() // eslint-disable-line

process.stdout.write(ansiEscapes.eraseDown)
// console.log() // eslint-disable-line
console.log(` ${msg}`) // eslint-disable-line
// Write log
console.log(`${msg}`) // eslint-disable-line

@@ -198,4 +146,8 @@ // Put cursor to starting position for next view

renderDebug(msg) {
if (!this.debugMode || !msg || msg == '') {
/**
* Debug
* - Render debug statements cleanly
*/
debug(msg) {
if (!this._.debug || !msg || msg == '') {
return

@@ -207,3 +159,3 @@ }

console.log(` ${msg}`) // eslint-disable-line
console.log(`${msg}`) // eslint-disable-line

@@ -214,3 +166,7 @@ // Put cursor to starting position for next view

renderError(error, entity) {
/**
* Error
* - Render and error and close a long-running CLI process.
*/
error(error, simple = false) {
// If no argument, skip

@@ -227,20 +183,25 @@ if (!error || error === '') {

process.stdout.write(ansiEscapes.eraseDown)
console.log() // eslint-disable-line
// Write Error
if (entity) {
entity = `${red(entity)} ${red(figures.pointerSmall)} ${red(`error:`)}`
console.log(` ${entity}`) // eslint-disable-line
// Render stack trace
if (!this._.debug && simple) {
// Put cursor to starting position for next view
process.stdout.write(ansiEscapes.cursorLeft)
return this.close('error', `Error: ${error.message}`)
} else {
console.log(` ${red('error:')}`) // eslint-disable-line
}
console.log() // eslint-disable-line
console.log(``, red(error.stack)) // eslint-disable-line
delete error.name
console.log(` `, error) // eslint-disable-line
// Put cursor to starting position for next view
process.stdout.write(ansiEscapes.cursorLeft)
// Put cursor to starting position for next view
process.stdout.write(ansiEscapes.cursorLeft)
return this.close('error', `Error: ${error.message}`)
}
}
renderOutputs(outputs) {
/**
* Outputs
* - Render outputs cleanly.
*/
outputs(outputs) {
if (typeof outputs !== 'object' || Object.keys(outputs).length === 0) {

@@ -252,39 +213,94 @@ return

console.log() // eslint-disable-line
process.stdout.write(prettyoutput(outputs, {}, 2)) // eslint-disable-line
process.stdout.write(
prettyoutput(
outputs,
{
colors: {}
},
0
)
) // eslint-disable-line
}
close(reason, message) {
// Skip if not active
process.stdout.write(ansiEscapes.cursorShow)
if (!this.isStatusEngineActive()) {
console.log() // eslint-disable-line
process.exit(0)
return
/**
* Render Engine
* Repetitively renders status and more on a regular interval
*/
async _renderEngine() {
/**
* Debug Mode
*/
if (this._.debug) {
// Print Status
if (this._.status !== this._.lastStatus) {
let content = `${this._.timerSeconds}s - Status - ${this._.status}`
process.stdout.write(content + os.EOL)
this._.lastStatus = '' + this._.status
}
}
return this.statusEngineStop(reason, message)
}
// basic CLI utilities
log(msg) {
this.renderLog(msg)
}
/**
* Non-Debug Mode
*/
if (!this._.debug) {
// Update active dots
if (this._.loadingDotCount === 0) {
this._.loadingDots = `.`
} else if (this._.loadingDotCount === 2) {
this._.loadingDots = `..`
} else if (this._.loadingDotCount === 4) {
this._.loadingDots = `...`
} else if (this._.loadingDotCount === 6) {
this._.loadingDots = ''
}
this._.loadingDotCount++
if (this._.loadingDotCount > 8) {
this._.loadingDotCount = 0
}
debug(msg) {
this.renderDebug(msg)
}
// Clear any existing content
process.stdout.write(ansiEscapes.eraseDown)
status(status, entity) {
this.renderStatus(status, entity)
}
// Write status content
console.log() // eslint-disable-line
let content = ''
if (this._.timer) {
content += `${grey(this._.timerSeconds + 's')} `
content += `${grey(figures.pointerSmall)} `
}
content += `${this._.entity} `
content += `${grey(figures.pointerSmall)} ${grey(this._.status)}`
content += ` ${grey(this._.loadingDots)}`
process.stdout.write(content)
console.log() // eslint-disable-line
error(e) {
this.renderError(e)
this.close('error', e)
// Put cursor to starting position for next view
const startingPosition = this._getRelativeVerticalCursorPosition(content)
process.stdout.write(ansiEscapes.cursorUp(startingPosition))
process.stdout.write(ansiEscapes.cursorLeft)
}
await sleep(100)
return this._renderEngine()
}
outputs(outputs) {
return this.renderOutputs(outputs)
/**
* Get Relative Vertical Cursor Position
* Get cursor starting position according to terminal & content width
*/
_getRelativeVerticalCursorPosition(contentString) {
const base = 1
const terminalWidth = process.stdout.columns
const contentWidth = stripAnsi(contentString).length
const nudges = Math.ceil(Number(contentWidth) / Number(terminalWidth))
return base + nudges
}
}
module.exports = CLI
/**
* Sleep
* - Because our "utils" contains business logic (and isn't exclusive to utils), circular dependencies are created and therefore "utils" cannot be required in this module. Hence copying this here...
*/
const sleep = async (wait) => new Promise((resolve) => setTimeout(() => resolve(), wait))
module.exports = new CLI()

@@ -0,7 +1,11 @@

const cli = require('../cli')
const { connect, engine, getComponentInstanceData } = require('../utils')
const { runComponent } = engine
module.exports = async (cli) => {
const res = await Promise.all([connect(cli), getComponentInstanceData(cli)])
module.exports = async (config) => {
cli.status('Initializing')
const res = await Promise.all([
connect(config),
getComponentInstanceData(config)])
const socket = res[0]

@@ -20,4 +24,3 @@ const componentInstanceData = res[1]

cli.outputs(outputs)
cli.close('done', 'Done')
}

@@ -0,16 +1,17 @@

const cli = require('../cli')
const { login } = require('@serverless/platform-sdk')
module.exports = async (cli) => {
module.exports = async (config) => {
process.env.DISPLAY = true
cli.status('browser login', 'serverless')
// Disable timer
config.timer = false
cli.status('Logging in via browser')
const res = await login()
const { username } = res.users[res.userId]
// console.log(JSON.stringify(res, null, 4))
cli.status('logged in', username)
cli.close('done', `logged in`)
cli.status('Logged in')
cli.close('done', `Successfully logged into org "${username}"`)
}
const args = require('minimist')(process.argv.slice(2))
const path = require('path')
const { tmpdir } = require('os')
const cli = require('../cli')
const {

@@ -12,3 +13,3 @@ getConfig,

module.exports = async (cli) => {
module.exports = async () => {
const serverlessComponentFile = getConfig('serverless.component')

@@ -27,2 +28,3 @@

}
if (!serverlessComponentFile.version || args.dev) {

@@ -35,3 +37,3 @@ serverlessComponentFile.version = 'dev'

const componentDirectoryPath = process.cwd()
// Get Component path and temporary path for packaging
const componentPackagePath = path.join(

@@ -43,2 +45,8 @@ tmpdir(),

)
let componentDirectoryPath
if (serverlessComponentFile.main) {
componentDirectoryPath = path.resolve(process.cwd(), serverlessComponentFile.main)
} else {
componentDirectoryPath = process.cwd()
}

@@ -45,0 +53,0 @@ cli.debug(`Packaging component from ${componentDirectoryPath}`)

@@ -5,20 +5,24 @@ const args = require('minimist')(process.argv.slice(2))

const commands = require('./commands')
const CLI = require('./CLI')
const cli = require('./cli')
// keeping it backward compatible
// Keeping it backward compatible
const runningComponents = () => isComponentsProject()
const runComponents = async () => {
const command = args._[0]
const debug = args.debug ? true : false
const cli = new CLI({ debug, command })
const config = {}
config.command = args._[0] || 'deploy'
config.debug = args.debug ? true : false
config.timer = commands[config.command] ? false : true
// Start CLI process
cli.start(config)
try {
if (commands[command]) {
await commands[command](cli)
if (commands[config.command]) {
await commands[config.command](config)
} else {
await commands.custom(cli)
await commands.custom(config)
}
} catch (e) {
cli.error(e)
return cli.error(e)
}

@@ -25,0 +29,0 @@ }

@@ -21,5 +21,5 @@ const args = require('minimist')(process.argv.slice(2))

const { merge, endsWith, contains, isNil, last, split } = require('ramda')
const cli = require('./cli')
const getEndpoints = () => {
// todo change the default stage to be prod
let stage = 'prod'

@@ -97,3 +97,3 @@ if (process.env.SERVERLESS_PLATFORM_STAGE && process.env.SERVERLESS_PLATFORM_STAGE !== 'prod') {

const connect = async (cli) => {
const connect = async () => {
if (!cli.debugMode) {

@@ -103,3 +103,3 @@ return

cli.status('Connecting')
cli.debug('Establishing streaming connection')

@@ -354,3 +354,3 @@ const endpoints = getEndpoints()

const uploadComponentSrc = async (src, accessKey, org, cli) => {
const uploadComponentSrc = async (src, accessKey, org) => {
const { getPackageUrls } = engine

@@ -365,3 +365,3 @@

cli.debug(`packaging from ${src} into ${packagePath}`)
cli.debug(`Packaging from ${src} into ${packagePath}`)
cli.status('Packaging')

@@ -374,5 +374,5 @@

cli.status('Uploading')
cli.debug(`uploading ${packagePath} to ${packageUrls.upload.split('?')[0]}`)
cli.debug(`Uploading ${packagePath} to ${packageUrls.upload.split('?')[0]}`)
await putPackage(packagePath, packageUrls.upload)
cli.debug(`upload completed`)
cli.debug(`Upload completed`)

@@ -382,3 +382,3 @@ return packageUrls.download

const resolveComponentSrcInput = async (inputs, accessKey, org, cli) => {
const resolveComponentSrcInput = async (inputs, accessKey, org) => {
let uploadDirectoryPath

@@ -407,3 +407,3 @@

inputs.src = await uploadComponentSrc(uploadDirectoryPath, accessKey, org, cli)
inputs.src = await uploadComponentSrc(uploadDirectoryPath, accessKey, org)

@@ -414,4 +414,11 @@ return inputs

const getOrCreateAccessKey = async (org) => {
cli.status('Preparing')
const userConfigFile = readConfigFile()
// Verify config file
if (!userConfigFile || !userConfigFile.users || !userConfigFile.users[userConfigFile.userId]) {
cli.error(`Run 'serverless login' first to rapidly deploy your serverless application.`, true)
}
const user = userConfigFile.users[userConfigFile.userId]

@@ -430,3 +437,3 @@

const getComponentInstanceData = async (cli) => {
const getComponentInstanceData = async (config) => {
const serverlessFile = getConfig('serverless')

@@ -440,3 +447,3 @@

const { app, stage, name, component, inputs } = resolvedServerlessFile
const { org, app, stage, name, component, inputs } = resolvedServerlessFile

@@ -455,18 +462,22 @@ if (typeof app === 'undefined') {

if (typeof app !== 'string' || app.split('/').length !== 2) {
throw new Error(`"${app}" is not a valid org/app`)
}
const data = {
org: app.split('/')[0],
app: app.split('/')[1],
stage: stage,
org,
app,
stage: stage || 'dev', // Default to "dev" stage
name,
method: cli.command,
debugMode: cli.debugMode,
method: config.command,
debugMode: config.debug,
credentials: getCredentials(),
accessKey: await getOrCreateAccessKey(app.split('/')[0]),
inputs
inputs: config.command === 'deploy' ? inputs : {} // Inputs are only for the "deploy" command
}
// Support for specifying "org" and "app" like: app: "myOrg/myApp"
if (data.app.includes('/')) {
data.org = data.app.split('/')[0]
data.app = data.app.split('/')[1]
}
// Get Serverless Framework access key
data.accessKey = await getOrCreateAccessKey(data.org)
if (component.split('@').length === 2) {

@@ -484,4 +495,4 @@ data.componentName = component.split('@')[0]

if (inputs && inputs.src) {
data.inputs = await resolveComponentSrcInput(inputs, data.accessKey, data.org, cli)
if (data.inputs && data.inputs.src) {
data.inputs = await resolveComponentSrcInput(inputs, data.accessKey, data.org)
}

@@ -488,0 +499,0 @@