@heroku/heroku-cli-util 10.5.0 → 10.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,10 +11,21 @@
11
11
  * ([see here](https://github.com/heroku/cli/blob/b79f2c93d6f21eafd9d93983bcd377e4bc7f8438/packages/cli/src/commands/pg/psql.ts#L32)).
12
12
  */
13
13
  export declare class NotFound extends Error {
14
- readonly body: {
14
+ readonly resource: string;
15
+ readonly statusCode = 404;
16
+ constructor(resource?: string);
17
+ get body(): {
15
18
  id: string;
16
19
  message: string;
20
+ resource: string;
17
21
  };
18
- readonly message = "Couldn't find that addon.";
19
- readonly statusCode = 404;
22
+ get http(): {
23
+ body: {
24
+ id: string;
25
+ message: string;
26
+ resource: string;
27
+ };
28
+ statusCode: number;
29
+ };
30
+ get message(): string;
20
31
  }
@@ -11,7 +11,19 @@
11
11
  * ([see here](https://github.com/heroku/cli/blob/b79f2c93d6f21eafd9d93983bcd377e4bc7f8438/packages/cli/src/commands/pg/psql.ts#L32)).
12
12
  */
13
13
  export class NotFound extends Error {
14
- body = { id: 'not_found', message: 'Couldn\'t find that addon.' };
15
- message = 'Couldn\'t find that addon.';
14
+ resource;
16
15
  statusCode = 404;
16
+ constructor(resource = 'addon') {
17
+ super();
18
+ this.resource = resource;
19
+ }
20
+ get body() {
21
+ return { id: 'not_found', message: `Couldn't find that ${this.resource}.`, resource: this.resource };
22
+ }
23
+ get http() {
24
+ return { body: this.body, statusCode: this.statusCode };
25
+ }
26
+ get message() {
27
+ return this.body.message;
28
+ }
17
29
  }
@@ -1,4 +1,5 @@
1
1
  import { AmbiguousError } from '../../errors/ambiguous.js';
2
+ import { NotFound } from '../../errors/not-found.js';
2
3
  export default class AddonResolver {
3
4
  heroku;
4
5
  addonHeaders = {
@@ -9,15 +10,25 @@ export default class AddonResolver {
9
10
  this.heroku = heroku;
10
11
  }
11
12
  async resolve(addon, app, addonService) {
13
+ let addons = [];
12
14
  const [appPart, addonPart] = addon.match(/^(.+)::(.+)$/)?.slice(1) ?? [app, addon];
13
- const { body: addons } = await this.heroku.post('/actions/addons/resolve', {
15
+ ({ body: addons } = await this.heroku.post('/actions/addons/resolve', {
14
16
  body: {
15
17
  addon: addonPart,
16
- addon_service: addonService,
18
+ // We would pass the add-on service slug here for Platform API to filter the add-ons by
19
+ // add-on service, but the resolvers won't allow alpha add-ons to be found.
20
+ // addon_service: addonService,
17
21
  app: appPart,
18
22
  },
19
23
  headers: this.addonHeaders,
20
- });
24
+ }));
25
+ // We implement the add-on service filtering logic here if the parameter is provided.
26
+ if (addonService) {
27
+ addons = addons.filter(addon => addon.addon_service.name === addonService);
28
+ }
29
+ if (addons.length === 0) {
30
+ throw new NotFound();
31
+ }
21
32
  if (addons.length === 1) {
22
33
  return addons[0];
23
34
  }
@@ -10,12 +10,25 @@ export default class AddonAttachmentResolver {
10
10
  this.heroku = heroku;
11
11
  }
12
12
  async resolve(appId, attachmentId, options = {}) {
13
- const { body: attachments } = await this.heroku.post('/actions/addon-attachments/resolve', {
14
- // eslint-disable-next-line camelcase
15
- body: { addon_attachment: attachmentId, addon_service: options.addonService, app: appId },
13
+ const { addonService, namespace } = options;
14
+ let attachments = [];
15
+ /* eslint-disable camelcase */
16
+ ({ body: attachments } = await this.heroku.post('/actions/addon-attachments/resolve', {
17
+ body: {
18
+ addon_attachment: attachmentId,
19
+ // We would pass the add-on service slug here for Platform API to filter the attachments by
20
+ // add-on service, but the resolvers won't allow alpha add-ons to be found.
21
+ // addon_service: addonService,
22
+ app: appId,
23
+ },
16
24
  headers: this.attachmentHeaders,
17
- });
18
- return this.singularize(attachments, options.namespace);
25
+ }));
26
+ /* eslint-enable camelcase */
27
+ // We implement the add-on service filtering logic here if the parameter is provided.
28
+ if (addonService) {
29
+ attachments = attachments.filter(attachment => attachment.addon.plan.name.split(':', 2)[0] === addonService);
30
+ }
31
+ return this.singularize(attachments, namespace);
19
32
  }
20
33
  singularize(attachments, namespace) {
21
34
  let matches;
@@ -12,7 +12,7 @@ const pgDebug = debug('pg');
12
12
  * @returns True if the attachment belongs to a non-shield Private Space, false otherwise
13
13
  */
14
14
  export function bastionKeyPlan(attachment) {
15
- return Boolean(/private/.test(attachment.addon.plan.name.split(':', 2)[1]));
15
+ return Boolean(attachment.addon.plan.name.split(':', 2)[1].startsWith('private'));
16
16
  }
17
17
  /**
18
18
  * Fetches the bastion configuration from the Data API (only relevant for add-ons installed onto a
@@ -98,7 +98,9 @@ function baseEnv(connectionDetails) {
98
98
  };
99
99
  const baseEnv = {
100
100
  PGAPPNAME: 'psql non-interactive',
101
- PGSSLMODE: (!connectionDetails.host || connectionDetails.host === 'localhost') ? 'prefer' : 'require',
101
+ PGSSLMODE: (!connectionDetails.host || connectionDetails.host === 'localhost')
102
+ ? 'prefer'
103
+ : 'require',
102
104
  ...process.env,
103
105
  };
104
106
  for (const envVar of Object.keys(mapping)) {
@@ -178,8 +180,6 @@ class Timeout {
178
180
  }
179
181
  /**
180
182
  * Cancels the timeout.
181
- *
182
- * @returns void
183
183
  */
184
184
  cancel() {
185
185
  this.events.emit('cancelled');
package/dist/ux/colors.js CHANGED
@@ -1,4 +1,3 @@
1
- /* eslint-disable import/no-named-as-default-member */
2
1
  import ansis from 'ansis';
3
2
  /**
4
3
  * Color constants for Heroku CLI output
@@ -27,11 +27,18 @@ export function styledObject(obj, keys) {
27
27
  if (typeof obj === 'boolean')
28
28
  return obj.toString();
29
29
  const output = [];
30
- const keyLengths = Object.keys(obj).map(key => key.toString().length);
31
- const maxKeyLength = Math.max(...keyLengths) + 2;
30
+ const keysToIterate = keys || Object.keys(obj);
31
+ // Only calculate max key length for keys that will actually be displayed
32
+ const displayableKeys = keysToIterate.filter(key => {
33
+ const value = obj[key];
34
+ return value !== null && value !== undefined && (!Array.isArray(value) || value.length > 0);
35
+ });
36
+ const keyLengths = displayableKeys.map(key => key.toString().length);
37
+ const maxKeyLength = keyLengths.length > 0 ? Math.max(...keyLengths) + 2 : 0;
32
38
  const logKeyValue = (key, value) => `${color.label(key)}:` + ' '.repeat(maxKeyLength - key.length - 1) + prettyPrint(value);
33
- for (const [key, value] of Object.entries(obj)) {
34
- if (keys && !keys.includes(key))
39
+ for (const key of keysToIterate) {
40
+ const value = obj[key];
41
+ if (value === null || value === undefined)
35
42
  continue;
36
43
  if (Array.isArray(value)) {
37
44
  if (value.length > 0) {
@@ -41,7 +48,7 @@ export function styledObject(obj, keys) {
41
48
  }
42
49
  }
43
50
  }
44
- else if (value !== null && value !== undefined) {
51
+ else {
45
52
  output.push(logKeyValue(key, value));
46
53
  }
47
54
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@heroku/heroku-cli-util",
4
- "version": "10.5.0",
4
+ "version": "10.6.1",
5
5
  "description": "Set of helpful CLI utilities",
6
6
  "author": "Heroku",
7
7
  "license": "ISC",
@@ -16,7 +16,7 @@
16
16
  ],
17
17
  "devDependencies": {
18
18
  "@heroku-cli/schema": "^2.0.0",
19
- "@heroku-cli/test-utils": "0.1.1",
19
+ "@heroku-cli/test-utils": "^0.1.2",
20
20
  "@types/chai": "^4.3.13",
21
21
  "@types/chai-as-promised": "^8.0.2",
22
22
  "@types/debug": "^4.1.12",
@@ -29,12 +29,10 @@
29
29
  "c8": "^7.7.0",
30
30
  "chai": "^4.4.1",
31
31
  "chai-as-promised": "^8.0.1",
32
- "eslint": "^8.57.0",
33
- "eslint-config-oclif": "^5.0.0",
34
- "eslint-config-oclif-typescript": "^3.1.14",
35
- "eslint-plugin-import": "^2.31.0",
36
- "eslint-plugin-mocha": "^10.4.3",
37
- "mocha": "^10.8.2",
32
+ "eslint": "^9",
33
+ "eslint-config-oclif": "^6.0.144",
34
+ "eslint-plugin-mocha": "^11.2.0",
35
+ "mocha": "^11",
38
36
  "mock-stdin": "^1.0.0",
39
37
  "nock": "^13.2.9",
40
38
  "open": "^11",
@@ -46,7 +44,7 @@
46
44
  "typescript": "^5.4.0"
47
45
  },
48
46
  "dependencies": {
49
- "@heroku-cli/command": "^12.0.0",
47
+ "@heroku-cli/command": "^12.2.0",
50
48
  "@heroku/http-call": "^5.5.0",
51
49
  "@oclif/core": "^4.3.0",
52
50
  "@oclif/table": "0.5.2",
@@ -64,8 +62,13 @@
64
62
  "build": "npm run clean && tsc",
65
63
  "clean": "rm -rf dist",
66
64
  "example": "sh examples/run.sh",
67
- "lint": "eslint . --ext .ts --config .eslintrc.cjs",
65
+ "lint": "eslint .",
66
+ "posttest": "npm run lint",
67
+ "prepare": "npm run build",
68
68
  "test": "c8 mocha --forbid-only \"test/**/*.test.ts\"",
69
69
  "test:local": "c8 mocha \"${npm_config_file:-test/**/*.test.+(ts|tsx)}\""
70
+ },
71
+ "overrides": {
72
+ "serialize-javascript": "^7.0.3"
70
73
  }
71
- }
74
+ }