Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@heroku-cli/command

Package Overview
Dependencies
Maintainers
21
Versions
94
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@heroku-cli/command - npm Package Compare versions

Comparing version 7.1.1 to 7.1.2

8

lib/api_client.d.ts

@@ -1,2 +0,2 @@

import * as Config from '@oclif/config';
import { Config } from '@cli-engine/config';
import { HTTP, HTTPError, HTTPRequestOptions } from 'http-call';

@@ -24,3 +24,3 @@ import { Mutex } from './mutex';

export declare class APIClient {
protected config: Config.IConfig;
protected config: Config;
options: IOptions;

@@ -32,3 +32,3 @@ preauthPromises: {

private _twoFactorMutex;
constructor(config: Config.IConfig, options?: IOptions);
constructor(config: Config, options?: IOptions);
readonly twoFactorMutex: Mutex<string>;

@@ -45,3 +45,3 @@ readonly auth: string | undefined;

request(url: string, options?: HTTPRequestOptions): Promise<HTTP>;
readonly defaults: typeof HTTP.defaults;
readonly defaultOptions: HTTPRequestOptions;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const url = require("url");

@@ -41,41 +42,46 @@ const deps_1 = require("./deps");

let self = this;
const opts = {
host: apiUrl.host,
headers: Object.assign({ accept: 'application/vnd.heroku+json; version=3', 'user-agent': `heroku-cli/${self.config.version} ${self.config.platform}` }, envHeaders),
};
if (auth)
opts.headers.authorization = `Bearer ${auth}`;
this.http = class APIHTTPClient extends deps_1.default.HTTP.HTTP.create(opts) {
static async twoFactorRetry(err, url, opts = {}, retries = 3) {
const app = err.body.app ? err.body.app.name : null;
if (!app || !options.preauth) {
opts.headers = opts.headers || {};
opts.headers['Heroku-Two-Factor-Code'] = await self.twoFactorPrompt();
return this.request(url, opts, retries);
}
else {
// if multiple requests are run in parallel for the same app, we should
// only preauth for the first so save the fact we already preauthed
if (!self.preauthPromises[app]) {
self.preauthPromises[app] = self.twoFactorPrompt().then((factor) => self.preauth(app, factor));
this.http = class APIHTTPClient extends deps_1.default.HTTP.HTTP {
static get defaultOptions() {
let opts = Object.assign({}, super.defaultOptions, { host: apiUrl.host, headers: Object.assign({}, super.defaultOptions.headers, { accept: 'application/vnd.heroku+json; version=3', 'user-agent': `heroku-cli/${self.config.version} ${self.config.platform}` }, envHeaders) });
if (auth)
opts.headers.authorization = `Bearer ${auth}`;
return opts;
}
static twoFactorRetry(err, url, opts = {}, retries = 3) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const app = err.body.app ? err.body.app.name : null;
if (!app || !options.preauth) {
opts.headers = opts.headers || {};
opts.headers['Heroku-Two-Factor-Code'] = yield self.twoFactorPrompt();
return this.request(url, opts, retries);
}
await self.preauthPromises[app];
return this.request(url, opts, retries);
}
else {
// if multiple requests are run in parallel for the same app, we should
// only preauth for the first so save the fact we already preauthed
if (!self.preauthPromises[app]) {
self.preauthPromises[app] = self.twoFactorPrompt().then(factor => self.preauth(app, factor));
}
yield self.preauthPromises[app];
return this.request(url, opts, retries);
}
});
}
static async request(url, opts = {}, retries = 3) {
retries--;
try {
return await super.request(url, opts);
}
catch (err) {
if (!(err instanceof deps_1.default.HTTP.HTTPError))
throw err;
if (retries > 0) {
if (err.http.statusCode === 403 && err.body.id === 'two_factor') {
return this.twoFactorRetry(err, url, opts, retries);
static request(url, opts = {}, retries = 3) {
const _super = name => super[name];
return tslib_1.__awaiter(this, void 0, void 0, function* () {
retries--;
try {
return yield _super("request").call(this, url, opts);
}
catch (err) {
if (!(err instanceof deps_1.default.HTTP.HTTPError))
throw err;
if (retries > 0) {
if (err.http.statusCode === 403 && err.body.id === 'two_factor') {
return this.twoFactorRetry(err, url, opts, retries);
}
}
throw new HerokuAPIError(err);
}
throw new HerokuAPIError(err);
}
});
}

@@ -102,5 +108,5 @@ };

deps_1.default.yubikey.enable();
return this.twoFactorMutex.synchronize(async () => {
return this.twoFactorMutex.synchronize(() => tslib_1.__awaiter(this, void 0, void 0, function* () {
try {
let factor = await deps_1.default.cli.prompt('Two-factor code', { type: 'mask' });
let factor = yield deps_1.default.cli.prompt('Two-factor code', { type: 'mask' });
deps_1.default.yubikey.disable();

@@ -113,3 +119,3 @@ return factor;

}
});
}));
}

@@ -142,6 +148,6 @@ preauth(app, factor) {

}
get defaults() {
return this.http.defaults;
get defaultOptions() {
return this.http.defaultOptions;
}
}
exports.APIClient = APIClient;

@@ -1,2 +0,2 @@

import { Command as Base } from '@oclif/command';
import { Command as Base } from '@cli-engine/command';
import { APIClient } from './api_client';

@@ -11,2 +11,3 @@ export declare abstract class Command extends Base {

readonly out: any;
readonly app: any;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("@oclif/command");
const command_1 = require("@cli-engine/command");
const util_1 = require("util");

@@ -27,6 +27,6 @@ const deps_1 = require("./deps");

debug: this.config.debug,
host: `${this.heroku.defaults.protocol || 'https:'}//${this.heroku.defaults.host ||
host: `${this.heroku.defaultOptions.protocol || 'https:'}//${this.heroku.defaultOptions.host ||
'api.heroku.com'}`,
token: this.heroku.auth,
userAgent: this.heroku.defaults.headers['user-agent'],
userAgent: this.heroku.defaultOptions.headers['user-agent'],
};

@@ -42,3 +42,8 @@ this._legacyHerokuClient = new HerokuClient(options);

}
get app() {
return util_1.deprecate(() => {
return this.flags.app;
}, 'this.app is deprecated but you can implement it yourself as `get app() { return this.flags.app }`')();
}
}
exports.Command = Command;

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

import { flags } from '@oclif/command';
import * as Config from '@oclif/config';
import { flags } from '@cli-engine/command';
import { Config } from '@cli-engine/config';
export { AppCompletion, RemoteCompletion } from './flags/app';
export declare const oneDay: number;
export declare const herokuGet: (resource: string, ctx: {
config: Config.IConfig;
config: Config;
}) => Promise<string[]>;

@@ -8,0 +8,0 @@ export declare const BuildpackCompletion: flags.ICompletion;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const path = require("path");

@@ -9,12 +10,12 @@ const deps_1 = require("./deps");

exports.oneDay = 60 * 60 * 24;
exports.herokuGet = async (resource, ctx) => {
exports.herokuGet = (resource, ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
const heroku = new deps_1.default.APIClient(ctx.config);
let { body: resources } = await heroku.get(`/${resource}`);
let { body: resources } = yield heroku.get(`/${resource}`);
if (typeof resources === 'string')
resources = JSON.parse(resources);
return resources.map((a) => a.name).sort();
};
});
exports.BuildpackCompletion = {
skipCache: true,
options: async () => {
options: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
return [

@@ -31,52 +32,52 @@ 'heroku/ruby',

];
},
}),
};
exports.AppAddonCompletion = {
cacheDuration: exports.oneDay,
cacheKey: async (ctx) => {
cacheKey: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
return ctx.flags && ctx.flags.app ? `${ctx.flags.app}_addons` : '';
},
options: async (ctx) => {
let addons = ctx.flags && ctx.flags.app ? await exports.herokuGet(`apps/${ctx.flags.app}/addons`, ctx) : [];
}),
options: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
let addons = ctx.flags && ctx.flags.app ? yield exports.herokuGet(`apps/${ctx.flags.app}/addons`, ctx) : [];
return addons;
},
}),
};
exports.AppDynoCompletion = {
cacheDuration: exports.oneDay,
cacheKey: async (ctx) => {
cacheKey: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
return ctx.flags && ctx.flags.app ? `${ctx.flags.app}_dynos` : '';
},
options: async (ctx) => {
let dynos = ctx.flags && ctx.flags.app ? await exports.herokuGet(`apps/${ctx.flags.app}/dynos`, ctx) : [];
}),
options: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
let dynos = ctx.flags && ctx.flags.app ? yield exports.herokuGet(`apps/${ctx.flags.app}/dynos`, ctx) : [];
return dynos;
},
}),
};
exports.DynoSizeCompletion = {
cacheDuration: exports.oneDay * 90,
options: async (ctx) => {
let sizes = await exports.herokuGet('dyno-sizes', ctx);
options: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
let sizes = yield exports.herokuGet('dyno-sizes', ctx);
return sizes;
},
}),
};
exports.FileCompletion = {
skipCache: true,
options: async () => {
let files = await deps_1.default.file.readdir(process.cwd());
options: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
let files = yield deps_1.default.file.readdir(process.cwd());
return files;
},
}),
};
exports.PipelineCompletion = {
cacheDuration: exports.oneDay,
options: async (ctx) => {
let pipelines = await exports.herokuGet('pipelines', ctx);
options: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
let pipelines = yield exports.herokuGet('pipelines', ctx);
return pipelines;
},
}),
};
exports.ProcessTypeCompletion = {
skipCache: true,
options: async () => {
options: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
let types = [];
let procfile = path.join(process.cwd(), 'Procfile');
try {
let buff = await deps_1.default.file.readFile(procfile);
let buff = yield deps_1.default.file.readFile(procfile);
types = buff

@@ -98,49 +99,49 @@ .toString()

return types;
},
}),
};
exports.RegionCompletion = {
cacheDuration: exports.oneDay * 7,
options: async (ctx) => {
let regions = await exports.herokuGet('regions', ctx);
options: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
let regions = yield exports.herokuGet('regions', ctx);
return regions;
},
}),
};
exports.RoleCompletion = {
skipCache: true,
options: async () => {
options: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
return ['admin', 'collaborator', 'member', 'owner'];
},
}),
};
exports.ScopeCompletion = {
skipCache: true,
options: async () => {
options: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
return ['global', 'identity', 'read', 'write', 'read-protected', 'write-protected'];
},
}),
};
exports.SpaceCompletion = {
cacheDuration: exports.oneDay,
options: async (ctx) => {
let spaces = await exports.herokuGet('spaces', ctx);
options: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
let spaces = yield exports.herokuGet('spaces', ctx);
return spaces;
},
}),
};
exports.StackCompletion = {
cacheDuration: exports.oneDay,
options: async (ctx) => {
let stacks = await exports.herokuGet('stacks', ctx);
options: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
let stacks = yield exports.herokuGet('stacks', ctx);
return stacks;
},
}),
};
exports.StageCompletion = {
skipCache: true,
options: async () => {
options: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
return ['test', 'review', 'development', 'staging', 'production'];
},
}),
};
exports.TeamCompletion = {
cacheDuration: exports.oneDay,
options: async (ctx) => {
let teams = await exports.herokuGet('teams', ctx);
options: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
let teams = yield exports.herokuGet('teams', ctx);
return teams;
},
}),
};

@@ -11,21 +11,3 @@ /// <reference types="node" />

export declare const deps: {
readonly cli: {
config: CLI.Config;
trace: (...data: any[]) => void;
debug: (...data: any[]) => void;
info: (...data: any[]) => void;
log: (...data: any[]) => void;
warn: (input: string | Error, opts?: CLI.ErrorOptions | undefined) => void;
error: (input: string | Error, opts?: CLI.ErrorOptions | undefined) => void;
fatal: (input: string | Error, opts?: CLI.ErrorOptions | undefined) => void;
exit(code?: number | undefined, error?: Error | undefined): never;
readonly prompt: (name: string, options?: CLI.IPromptOptions | undefined) => Promise<any>;
readonly confirm: (message: string) => Promise<boolean>;
readonly action: CLI.ActionBase;
styledObject(obj: any, keys?: string[] | undefined): void;
readonly styledHeader: (header: string) => void;
readonly styledJSON: (obj: any) => void;
readonly table: typeof CLI.Table.default;
done(): Promise<void>;
};
readonly cli: CLI.CLI;
readonly HTTP: typeof HTTP;

@@ -32,0 +14,0 @@ readonly netrc: netrc.Netrc;

@@ -8,3 +8,3 @@ "use strict";

if (_debug)
_debug = require('debug')('@heroku-cli/command:file');
_debug = require('debug')('cli-engine-heroku:file');
_debug(...args);

@@ -11,0 +11,0 @@ }

@@ -1,2 +0,2 @@

import { flags } from '@oclif/command';
import { flags } from '@cli-engine/command';
export declare const AppCompletion: flags.ICompletion;

@@ -3,0 +3,0 @@ export declare const app: flags.Definition<string>;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("@oclif/command");
const errors_1 = require("@oclif/errors");
const tslib_1 = require("tslib");
const command_1 = require("@cli-engine/command");
const completions_1 = require("../completions");
const git_1 = require("../git");
const deps_1 = require("../deps");
const vars_1 = require("../vars");
class MultipleRemotesError extends errors_1.CLIError {
class MultipleRemotesError extends Error {
constructor(gitRemotes) {

@@ -24,8 +24,8 @@ super(`Multiple apps in git remotes

cacheDuration: completions_1.oneDay,
options: async (ctx) => {
let apps = await completions_1.herokuGet('apps', ctx);
options: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
let apps = yield completions_1.herokuGet('apps', ctx);
return apps;
},
}),
};
exports.app = command_1.flags.build({
exports.app = command_1.flags.option({
char: 'a',

@@ -42,3 +42,3 @@ completion: exports.AppCompletion,

if (flags.remote && gitRemotes.length === 0) {
errors_1.error(`remote ${flags.remote} not found in git remotes`);
throw new Error(`remote ${flags.remote} not found in git remotes`);
}

@@ -52,8 +52,8 @@ if (gitRemotes.length > 1 && options.required) {

skipCache: true,
options: async () => {
options: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
let remotes = getGitRemotes(configRemote());
return remotes.map(r => r.remote);
},
}),
};
exports.remote = command_1.flags.build({
exports.remote = command_1.flags.option({
char: 'r',

@@ -64,10 +64,10 @@ completion: exports.RemoteCompletion,

function configRemote() {
let git = new git_1.Git();
let git = new deps_1.default.Git();
try {
return git.exec('config heroku.remote').trim();
}
catch (_a) { }
catch (err) { }
}
function getGitRemotes(onlyRemote) {
let git = new git_1.Git();
let git = new deps_1.default.Git();
let appRemotes = [];

@@ -78,3 +78,3 @@ let remotes;

}
catch (_a) {
catch (err) {
return [];

@@ -81,0 +81,0 @@ }

@@ -1,2 +0,2 @@

export * from '@oclif/command/lib/flags';
export * from '@cli-engine/command/lib/flags';
export { app, remote } from './app';

@@ -3,0 +3,0 @@ export { org } from './org';

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("@oclif/command/lib/flags"), exports);
tslib_1.__exportStar(require("@cli-engine/command/lib/flags"), exports);
var app_1 = require("./app");

@@ -6,0 +6,0 @@ exports.app = app_1.app;

@@ -1,2 +0,2 @@

import { flags } from '@oclif/command';
import { flags } from '@cli-engine/command';
export declare const org: flags.Definition<string>;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("@oclif/command");
exports.org = command_1.flags.build({
const command_1 = require("@cli-engine/command");
exports.org = command_1.flags.option({
char: 'o',

@@ -6,0 +6,0 @@ default: () => process.env.HEROKU_ORGANIZATION,

@@ -1,2 +0,2 @@

import { flags } from '@oclif/command';
import { flags } from '@cli-engine/command';
export declare const pipeline: flags.Definition<string>;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("@oclif/command");
exports.pipeline = command_1.flags.build({
const command_1 = require("@cli-engine/command");
exports.pipeline = command_1.flags.option({
char: 'p',
description: 'name of pipeline',
});

@@ -1,2 +0,2 @@

import { flags } from '@oclif/command';
import { flags } from '@cli-engine/command';
export declare const team: flags.Definition<string>;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const command_1 = require("@oclif/command");
const command_1 = require("@cli-engine/command");
const completions_1 = require("../completions");
exports.team = command_1.flags.build({
exports.team = command_1.flags.option({
char: 't',

@@ -7,0 +7,0 @@ completion: completions_1.TeamCompletion,

@@ -7,6 +7,6 @@ export declare type PromiseResolve<T> = (value: T | PromiseLike<T> | undefined) => void;

private busy;
private readonly queue;
private queue;
synchronize(task: Task<T>): Promise<T>;
dequeue(): Promise<void> | undefined;
execute(record: Record<T>): Promise<void>;
dequeue(): void;
execute(record: Record<T>): void;
}

@@ -20,3 +20,3 @@ "use strict";

if (next) {
return this.execute(next);
this.execute(next);
}

@@ -29,3 +29,3 @@ else {

let [task, resolve, reject] = record;
return task()
task()
.then(resolve, reject)

@@ -32,0 +32,0 @@ .then(() => {

@@ -10,3 +10,3 @@ "use strict";

}
catch (_a) { }
catch (err) { }
}

@@ -13,0 +13,0 @@ exports.yubikey = {

{
"name": "@heroku-cli/command",
"description": "base class for Heroku CLI commands",
"version": "7.1.1",
"description": "heroku specific functionality for the cli-engine",
"version": "7.1.2",
"author": "Jeff Dickey @jdxcode",
"bugs": "https://github.com/heroku/heroku-cli-command/issues",
"bugs": "https://github.com/heroku/cli-engine-heroku/issues",
"dependencies": {
"cli-ux": "^3.3.26",
"debug": "^3.1.0",
"@cli-engine/command": "^12.1.1",
"@cli-engine/config": "^5.1.0",
"cli-ux": "^2.0.21",
"heroku-client": "3.0.6",
"http-call": "^5.1.0",
"netrc-parser": "^3.1.2"
"http-call": "^4.0.8",
"netrc-parser": "^3.0.3"
},
"devDependencies": {
"@cli-engine/util": "^1.2.12",
"@heroku-cli/tslint": "^1.1.4",
"@oclif/command": "^1.4.6",
"@oclif/config": "^1.3.62",
"@oclif/errors": "^1.0.3",
"@oclif/tslint": "^1.0.2",
"@types/ansi-styles": "^2.0.30",
"@types/chai": "^4.1.2",
"@types/mocha": "^5.0.0",
"@types/nock": "9.1.2",
"@types/node": "^9.6.2",
"@types/proxyquire": "^1.3.28",
"@types/sinon": "^4.3.1",
"chai": "^4.1.2",
"concurrently": "^3.5.1",
"fancy-test": "^1.0.1",
"mocha": "^5.0.5",
"nock": "9.2.3",
"prettier": "1.11.1",
"proxyquire": "^2.0.1",
"sinon": "^4.5.0",
"testdouble": "^3.7.0",
"ts-node": "^5.0.1",
"@types/chalk": "^2.2.0",
"@types/jest": "22.0.1",
"@types/nock": "9.1.1",
"@types/node": "9.3.0",
"husky": "0.14.3",
"jest": "^22.1.1",
"nock": "9.1.6",
"prettier": "1.10.2",
"ts-jest": "22.0.1",
"tslint": "^5.9.1",
"typescript": "2.8.1"
"typescript": "2.6.2"
},

@@ -46,3 +37,3 @@ "engines": {

],
"homepage": "https://github.com/heroku/heroku-cli-command",
"homepage": "https://github.com/heroku/cli-engine-heroku",
"keywords": [

@@ -53,11 +44,9 @@ "heroku"

"main": "lib/index.js",
"repository": "heroku/heroku-cli-command",
"repository": "heroku/cli-engine-heroku",
"scripts": {
"build": "rm -rf lib && tsc",
"lint": "concurrently -p command \"tsc -p test --noEmit\" \"tslint -p test -t stylish\"",
"posttest": "yarn run lint",
"prepublishOnly": "yarn run build",
"test": "mocha --forbid-only \"test/**/*.test.ts\""
"precommit": "cli-engine-util",
"prepare": "cli-engine-util",
"test": "cli-engine-util"
},
"types": "./lib/index.d.ts"
}

@@ -1,13 +0,6 @@

@heroku-cli/command
===================
# @cli-engine/heroku
Base class for Heroku CLI commands. Built off of [oclif](https://oclif.io).
[![Version](https://img.shields.io/npm/v/@heroku-cli/command.svg)](https://npmjs.org/package/@heroku-cli/command)
[![CircleCI](https://circleci.com/gh/heroku/heroku-cli-command/tree/master.svg?style=svg)](https://circleci.com/gh/heroku/heroku-cli-command/tree/master)
[![Appveyor CI](https://ci.appveyor.com/api/projects/status/github/heroku/heroku-cli-command?branch=master&svg=true)](https://ci.appveyor.com/project/heroku/heroku-cli-command/branch/master)
[![Codecov](https://codecov.io/gh/heroku/heroku-cli-command/branch/master/graph/badge.svg)](https://codecov.io/gh/heroku/heroku-cli-command)
[![Greenkeeper](https://badges.greenkeeper.io/heroku/heroku-cli-command.svg)](https://greenkeeper.io/)
[![Known Vulnerabilities](https://snyk.io/test/npm/@heroku-cli/command/badge.svg)](https://snyk.io/test/npm/@heroku-cli/command)
[![Downloads/week](https://img.shields.io/npm/dw/@heroku-cli/command.svg)](https://npmjs.org/package/@heroku-cli/command)
[![License](https://img.shields.io/npm/l/@heroku-cli/command.svg)](https://github.com/heroku/heroku-cli-command/blob/master/package.json)
[![Greenkeeper badge](https://badges.greenkeeper.io/heroku/cli-engine-heroku.svg)](https://greenkeeper.io/)
[![CircleCI](https://circleci.com/gh/heroku/cli-engine-heroku.svg?style=svg)](https://circleci.com/gh/heroku/cli-engine-heroku)
[![Build status](https://ci.appveyor.com/api/projects/status/2o5qa4nbjryx8du5/branch/master?svg=true)](https://ci.appveyor.com/project/Heroku/cli-engine-heroku/branch/master)
[![codecov](https://codecov.io/gh/heroku/cli-engine-heroku/branch/master/graph/badge.svg)](https://codecov.io/gh/heroku/cli-engine-heroku)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc