clever-tools 3.13.1 → 4.0.0

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.
Files changed (93) hide show
  1. package/README.md +130 -37
  2. package/bin/clever.js +1427 -718
  3. package/package.json +62 -77
  4. package/src/clever-client/auth-bridge.js +9 -12
  5. package/src/clever-client/ng.js +1 -1
  6. package/src/clever-client/operators.js +7 -7
  7. package/src/command-options.js +6 -6
  8. package/src/command-promise-handler.js +6 -6
  9. package/src/commands/accesslogs.js +38 -29
  10. package/src/commands/activity.js +62 -40
  11. package/src/commands/addon.js +65 -69
  12. package/src/commands/applications.js +33 -31
  13. package/src/commands/cancel-deploy.js +7 -7
  14. package/src/commands/config.js +9 -7
  15. package/src/commands/console.js +3 -3
  16. package/src/commands/create.js +33 -32
  17. package/src/commands/curl.js +14 -36
  18. package/src/commands/database.js +10 -30
  19. package/src/commands/delete.js +8 -8
  20. package/src/commands/deploy.js +60 -46
  21. package/src/commands/diag.js +27 -25
  22. package/src/commands/domain.js +89 -91
  23. package/src/commands/drain.js +26 -14
  24. package/src/commands/emails.js +24 -28
  25. package/src/commands/env.js +18 -18
  26. package/src/commands/features.js +12 -18
  27. package/src/commands/keycloak.js +11 -11
  28. package/src/commands/kv.js +13 -13
  29. package/src/commands/link.js +9 -8
  30. package/src/commands/login.js +19 -27
  31. package/src/commands/logout.js +1 -1
  32. package/src/commands/logs.js +20 -12
  33. package/src/commands/makeDefault.js +5 -5
  34. package/src/commands/matomo.js +7 -7
  35. package/src/commands/metabase.js +9 -9
  36. package/src/commands/ng.js +32 -32
  37. package/src/commands/notify-email.js +11 -15
  38. package/src/commands/open.js +1 -1
  39. package/src/commands/otoroshi.js +11 -11
  40. package/src/commands/profile.js +8 -8
  41. package/src/commands/published-config.js +11 -11
  42. package/src/commands/restart.js +15 -8
  43. package/src/commands/scale.js +25 -16
  44. package/src/commands/service.js +28 -11
  45. package/src/commands/ssh-keys.js +25 -29
  46. package/src/commands/ssh.js +1 -2
  47. package/src/commands/status.js +31 -22
  48. package/src/commands/stop.js +3 -3
  49. package/src/commands/tcp-redirs.js +27 -26
  50. package/src/commands/tokens.js +44 -43
  51. package/src/commands/unlink.js +7 -5
  52. package/src/commands/version.js +2 -4
  53. package/src/commands/webhooks.js +8 -10
  54. package/src/experimental-features.js +7 -5
  55. package/src/format-table.js +46 -15
  56. package/src/initial-setup.js +2 -32
  57. package/src/initial-update-notifier.js +18 -0
  58. package/src/lib/has-param.js +16 -0
  59. package/src/lib/ng-print.js +21 -24
  60. package/src/lib/operator-commands.js +58 -53
  61. package/src/lib/prompts.js +5 -5
  62. package/src/lib/slugify.js +1 -1
  63. package/src/logger.js +26 -21
  64. package/src/models/activity.js +3 -3
  65. package/src/models/addon.js +54 -56
  66. package/src/models/app_configuration.js +34 -32
  67. package/src/models/application.js +90 -77
  68. package/src/models/application_configuration.js +19 -27
  69. package/src/models/configuration.js +30 -32
  70. package/src/models/deployments.js +9 -19
  71. package/src/models/domain.js +10 -9
  72. package/src/models/drain.js +16 -15
  73. package/src/models/exit-strategy-option.js +15 -5
  74. package/src/models/fs-utils.js +4 -3
  75. package/src/models/git.js +26 -32
  76. package/src/models/ids-resolver.js +37 -41
  77. package/src/models/isomorphic-http-with-agent.js +3 -3
  78. package/src/models/json-array.js +5 -6
  79. package/src/models/log-v4.js +44 -52
  80. package/src/models/log.js +40 -45
  81. package/src/models/namespaces.js +4 -4
  82. package/src/models/ng-resources.js +49 -39
  83. package/src/models/ng.js +41 -40
  84. package/src/models/node-dns-resolver.js +3 -3
  85. package/src/models/notification.js +7 -10
  86. package/src/models/operator.js +8 -10
  87. package/src/models/organisation.js +5 -8
  88. package/src/models/send-to-api.js +18 -11
  89. package/src/models/user.js +6 -5
  90. package/src/models/utils.js +5 -8
  91. package/src/models/variables.js +18 -20
  92. package/src/parsers.js +34 -34
  93. package/src/load-package-json.cjs +0 -5
package/src/models/ng.js CHANGED
@@ -1,13 +1,18 @@
1
- import colors from 'colors/safe.js';
2
- import * as User from '../models/user.js';
3
- import * as Organisation from '../models/organisation.js';
4
-
1
+ import {
2
+ createNetworkGroup,
3
+ deleteNetworkGroup,
4
+ getNetworkGroup,
5
+ getNetworkGroupWireGuardConfiguration,
6
+ listNetworkGroups,
7
+ } from '@clevercloud/client/esm/api/v4/network-group.js';
5
8
  import crypto from 'node:crypto';
9
+ import { styleText } from 'node:util';
10
+ import { searchNetworkGroupOrResource } from '../clever-client/ng.js';
6
11
  import { Logger } from '../logger.js';
7
- import { sendToApi } from './send-to-api.js';
8
12
  import { checkMembersToLink } from './ng-resources.js';
9
- import { searchNetworkGroupOrResource } from '../clever-client/ng.js';
10
- import { createNetworkGroup, deleteNetworkGroup, getNetworkGroup, getNetworkGroupWireGuardConfiguration, listNetworkGroups } from '@clevercloud/client/esm/api/v4/network-group.js';
13
+ import * as Organisation from './organisation.js';
14
+ import { sendToApi } from './send-to-api.js';
15
+ import * as User from './user.js';
11
16
 
12
17
  export const POLLING_TIMEOUT_MS = 30_000;
13
18
  export const POLLING_INTERVAL_MS = 1000;
@@ -27,7 +32,7 @@ const TYPE_PREFIXES = {
27
32
  * @param {string} orgaIdOrName The owner ID or name
28
33
  * @throws {Error} If the Network Group label is missing
29
34
  */
30
- export async function create (label, description, tags, membersIds, orgaIdOrName) {
35
+ export async function create(label, description, tags, membersIds, orgaIdOrName) {
31
36
  const id = `ng_${crypto.randomUUID()}`;
32
37
  const ownerId = await getOwnerIdFromOrgaIdOrName(orgaIdOrName);
33
38
 
@@ -53,11 +58,11 @@ export async function create (label, description, tags, membersIds, orgaIdOrName
53
58
  * @param {object} orgaIdOrName The owner ID or name
54
59
  * @throws {Error} If the Network Group is not found
55
60
  */
56
- export async function destroy (ngIdOrLabel, orgaIdOrName) {
61
+ export async function destroy(ngIdOrLabel, orgaIdOrName) {
57
62
  const [found] = await searchNgOrResource(ngIdOrLabel, orgaIdOrName, 'NetworkGroup');
58
63
 
59
64
  if (!found) {
60
- throw new Error(`Network Group ${colors.red(ngIdOrLabel.ngId ?? ngIdOrLabel.ngResourceLabel)} not found`);
65
+ throw new Error(`Network Group ${styleText('red', ngIdOrLabel.ngId ?? ngIdOrLabel.ngResourceLabel)} not found`);
61
66
  }
62
67
 
63
68
  await deleteNetworkGroup({ ownerId: found.ownerId, networkGroupId: found.id }).then(sendToApi);
@@ -76,22 +81,27 @@ export async function destroy (ngIdOrLabel, orgaIdOrName) {
76
81
  * @throws {Error} If the Network Group is not found
77
82
  * @throws {Error} If the Peer is not in the Network Group
78
83
  */
79
- export async function getPeerConfig (peerIdOrLabel, ngIdOrLabel, orgaIdOrName) {
84
+ export async function getPeerConfig(peerIdOrLabel, ngIdOrLabel, orgaIdOrName) {
80
85
  const [parentNg] = await searchNgOrResource(ngIdOrLabel, orgaIdOrName, 'NetworkGroup');
81
86
 
82
87
  if (!parentNg) {
83
- throw new Error(`Network Group ${colors.red(ngIdOrLabel.ngId ?? ngIdOrLabel.ngResourceLabel)} not found`);
88
+ throw new Error(`Network Group ${styleText('red', ngIdOrLabel.ngId ?? ngIdOrLabel.ngResourceLabel)} not found`);
84
89
  }
85
90
 
86
91
  const [peer] = await searchNgOrResource(peerIdOrLabel, orgaIdOrName, 'Peer');
87
92
 
88
93
  // peer.id is catched as a ngResourceLabel as it's a string with no distinctive prefix for now, it will change from API
89
- if (!peer || (peerIdOrLabel.ngResourceLabel && (peerIdOrLabel.ngResourceLabel !== peer.label && peerIdOrLabel.ngResourceLabel !== peer.id))) {
90
- throw new Error(`Peer ${colors.red(peerIdOrLabel.ngResourceLabel ?? peerIdOrLabel.member)} not found`);
94
+ if (
95
+ !peer ||
96
+ (peerIdOrLabel.ngResourceLabel &&
97
+ peerIdOrLabel.ngResourceLabel !== peer.label &&
98
+ peerIdOrLabel.ngResourceLabel !== peer.id)
99
+ ) {
100
+ throw new Error(`Peer ${styleText('red', peerIdOrLabel.ngResourceLabel ?? peerIdOrLabel.member)} not found`);
91
101
  }
92
102
 
93
103
  if (!parentNg.peers.find((p) => p.id === peer.id)) {
94
- throw new Error(`Peer ${colors.red(peer.id)} is not in Network Group ${colors.red(parentNg.id)}`);
104
+ throw new Error(`Peer ${styleText('red', peer.id)} is not in Network Group ${styleText('red', parentNg.id)}`);
95
105
  }
96
106
 
97
107
  Logger.debug(`Getting configuration for Peer ${peer.id}`);
@@ -111,7 +121,7 @@ export async function getPeerConfig (peerIdOrLabel, ngIdOrLabel, orgaIdOrName) {
111
121
  * @param {string} orgaIdOrName The owner ID or name
112
122
  * @returns {Promise<Array<Object>>} The Network Groups
113
123
  */
114
- export async function getNG (networkGroupId, orgaIdOrName) {
124
+ export async function getNG(networkGroupId, orgaIdOrName) {
115
125
  const ownerId = await getOwnerIdFromOrgaIdOrName(orgaIdOrName);
116
126
 
117
127
  Logger.info(`Get Network Group ${networkGroupId} for owner ${ownerId}`);
@@ -126,7 +136,7 @@ export async function getNG (networkGroupId, orgaIdOrName) {
126
136
  * @param {string} orgaIdOrName The owner ID or name
127
137
  * @returns {Promise<Array<Object>>} The Network Groups
128
138
  */
129
- export async function getAllNGs (orgaIdOrName) {
139
+ export async function getAllNGs(orgaIdOrName) {
130
140
  const ownerId = await getOwnerIdFromOrgaIdOrName(orgaIdOrName);
131
141
 
132
142
  Logger.info(`Listing Network Groups from owner ${ownerId}`);
@@ -144,17 +154,12 @@ export async function getAllNGs (orgaIdOrName) {
144
154
  * @throws {Error} If multiple Network Groups or member/peer are found in single_result mode
145
155
  * @returns {Promise<Object>} Found results
146
156
  */
147
- export async function searchNgOrResource (idOrLabel, orgaIdOrName, type = 'all', exactMatch = true) {
157
+ export async function searchNgOrResource(idOrLabel, orgaIdOrName, type = 'all', exactMatch = true) {
148
158
  const ownerId = await getOwnerIdFromOrgaIdOrName(orgaIdOrName);
149
159
 
150
160
  // If idOrLabel is a string we use it, or we look through multiple keys
151
- const query = typeof idOrLabel === 'string'
152
- ? idOrLabel
153
- : (
154
- idOrLabel.ngId
155
- ?? idOrLabel.memberId
156
- ?? idOrLabel.ngResourceLabel
157
- );
161
+ const query =
162
+ typeof idOrLabel === 'string' ? idOrLabel : (idOrLabel.ngId ?? idOrLabel.memberId ?? idOrLabel.ngResourceLabel);
158
163
 
159
164
  const found = await searchNetworkGroupOrResource({ ownerId, query }).then(sendToApi);
160
165
 
@@ -181,12 +186,12 @@ export async function searchNgOrResource (idOrLabel, orgaIdOrName, type = 'all',
181
186
  }
182
187
 
183
188
  if (filtered.length > 1 && type !== 'all') {
184
- throw new Error(`Multiple resources found for ${colors.red(query)}, use ID instead:
185
- ${filtered.map((f) => ` • ${f.id} ${colors.grey(`(${f.label} - ${f.type})`)}`).join('\n')}`);
189
+ throw new Error(`Multiple resources found for ${styleText('red', query)}, use ID instead:
190
+ ${filtered.map((f) => ` • ${f.id} ${styleText('grey', `(${f.label} - ${f.type})`)}`).join('\n')}`);
186
191
  }
187
192
 
188
193
  // Deduplicate results
189
- return filtered.filter((item, index, array) => array.findIndex((element) => (element.id === item.id)) === index);
194
+ return filtered.filter((item, index, array) => array.findIndex((element) => element.id === item.id) === index);
190
195
  }
191
196
 
192
197
  /**
@@ -195,7 +200,7 @@ ${filtered.map((f) => ` • ${f.id} ${colors.grey(`(${f.label} - ${f.type})`)}`)
195
200
  * @param {Array<string>} membersIds The members IDs
196
201
  * @returns {Array<Object>} Array of members with id, domainName and kind
197
202
  */
198
- export function constructMembers (ngId, membersIds) {
203
+ export function constructMembers(ngId, membersIds) {
199
204
  return membersIds.map((id) => {
200
205
  const domainName = `${id}.m.${ngId}.${DOMAIN}`;
201
206
  const prefixToType = TYPE_PREFIXES;
@@ -204,8 +209,7 @@ export function constructMembers (ngId, membersIds) {
204
209
  id,
205
210
  domainName,
206
211
  // Get kind from prefix match in id (app_*, addon_*, external_*) or default to 'APPLICATION'
207
- kind: prefixToType[Object.keys(prefixToType).find((p) => id.startsWith(p))]
208
- ?? TYPE_PREFIXES.app_,
212
+ kind: prefixToType[Object.keys(prefixToType).find((p) => id.startsWith(p))] ?? TYPE_PREFIXES.app_,
209
213
  };
210
214
  });
211
215
  }
@@ -219,12 +223,12 @@ export function constructMembers (ngId, membersIds) {
219
223
  * @throws {Error} When timeout is reached
220
224
  * @returns {Promise<void>}
221
225
  */
222
- async function pollNetworkGroup (ownerId, ngId, { waitForMembers = null, waitForDeletion = false } = {}) {
226
+ async function pollNetworkGroup(ownerId, ngId, { waitForMembers = null, waitForDeletion = false } = {}) {
223
227
  return new Promise((resolve, reject) => {
224
228
  Logger.info(`Polling Network Groups from owner ${ownerId}`);
225
- const timeoutTime = Date.now() + (POLLING_TIMEOUT_MS);
229
+ const timeoutTime = Date.now() + POLLING_TIMEOUT_MS;
226
230
 
227
- async function pollOnce () {
231
+ async function pollOnce() {
228
232
  if (Date.now() > timeoutTime) {
229
233
  const action = waitForDeletion ? 'deletion of' : 'creation of';
230
234
  reject(new Error(`Timeout while checking ${action} Network Group ${ngId}`));
@@ -254,8 +258,7 @@ async function pollNetworkGroup (ownerId, ngId, { waitForMembers = null, waitFor
254
258
  }
255
259
 
256
260
  setTimeout(pollOnce, POLLING_INTERVAL_MS);
257
- }
258
- catch (error) {
261
+ } catch (error) {
259
262
  reject(error);
260
263
  }
261
264
  }
@@ -269,8 +272,6 @@ async function pollNetworkGroup (ownerId, ngId, { waitForMembers = null, waitFor
269
272
  * @param {object} orgaIdOrName The Organisation ID or name
270
273
  * @returns {Promise<string>} The owner ID
271
274
  */
272
- async function getOwnerIdFromOrgaIdOrName (orgaIdOrName) {
273
- return orgaIdOrName != null
274
- ? Organisation.getId(orgaIdOrName)
275
- : User.getCurrentId();
275
+ async function getOwnerIdFromOrgaIdOrName(orgaIdOrName) {
276
+ return orgaIdOrName != null ? Organisation.getId(orgaIdOrName) : User.getCurrentId();
276
277
  }
@@ -1,7 +1,7 @@
1
1
  import { Resolver } from 'node:dns/promises';
2
2
 
3
3
  export class DnsResolver {
4
- constructor () {
4
+ constructor() {
5
5
  this._resolver = new Resolver();
6
6
  }
7
7
 
@@ -12,7 +12,7 @@ export class DnsResolver {
12
12
  * @param {string} hostname - The hostname to resolve A records for.
13
13
  * @returns {Promise<string[]>} A promise that resolves to an array of IP addresses, or an empty array if not found or an error occurs.
14
14
  */
15
- resolveA (hostname) {
15
+ resolveA(hostname) {
16
16
  return this._resolver.resolve4(hostname).catch((error) => {
17
17
  switch (error.code) {
18
18
  case 'ENOTFOUND':
@@ -30,7 +30,7 @@ export class DnsResolver {
30
30
  * @param {string} hostname - The hostname to resolve CNAME records for.
31
31
  * @returns {Promise<string|null>} A promise that resolves to the CNAME record if found, or null if not found or an error occurs.
32
32
  */
33
- resolveCname (hostname) {
33
+ resolveCname(hostname) {
34
34
  return this._resolver.resolveCname(hostname).catch((error) => {
35
35
  switch (error.code) {
36
36
  case 'ENOTFOUND':
@@ -1,9 +1,9 @@
1
1
  import cliparse from 'cliparse';
2
- import * as AppConfig from '../models/app_configuration.js';
3
- import * as Organisation from '../models/organisation.js';
4
- import * as User from '../models/user.js';
2
+ import * as AppConfig from './app_configuration.js';
3
+ import * as Organisation from './organisation.js';
4
+ import * as User from './user.js';
5
5
 
6
- export function listMetaEvents () {
6
+ export function listMetaEvents() {
7
7
  return cliparse.autocomplete.words([
8
8
  'META_SERVICE_LIFECYCLE',
9
9
  'META_DEPLOYMENT_RESULT',
@@ -12,14 +12,11 @@ export function listMetaEvents () {
12
12
  ]);
13
13
  }
14
14
 
15
- export function getOrgaIdOrUserId (orgIdOrName) {
16
- return (orgIdOrName == null)
17
- ? User.getCurrentId()
18
- : Organisation.getId(orgIdOrName);
15
+ export function getOrgaIdOrUserId(orgIdOrName) {
16
+ return orgIdOrName == null ? User.getCurrentId() : Organisation.getId(orgIdOrName);
19
17
  }
20
18
 
21
- export async function getOwnerAndApp (alias, org, useLinkedApp) {
22
-
19
+ export async function getOwnerAndApp(alias, org, useLinkedApp) {
23
20
  if (!useLinkedApp) {
24
21
  const ownerId = await getOrgaIdOrUserId(org);
25
22
  return { ownerId };
@@ -1,9 +1,8 @@
1
- import colors from 'colors/safe.js';
2
1
  import dedent from 'dedent';
3
-
4
- import { sendToApi } from './send-to-api.js';
5
- import { findAddonsByNameOrId } from './ids-resolver.js';
2
+ import { styleText } from 'node:util';
6
3
  import { getOperator } from '../clever-client/operators.js';
4
+ import { findAddonsByNameOrId } from './ids-resolver.js';
5
+ import { sendToApi } from './send-to-api.js';
7
6
 
8
7
  /**
9
8
  * Get the details of an operator from its name or ID
@@ -12,7 +11,7 @@ import { getOperator } from '../clever-client/operators.js';
12
11
  * @returns {Promise<object>} The operator's details
13
12
  * @throws {Error} If the operator provider is unknown
14
13
  */
15
- export async function getDetails (provider, operatorIdOrName) {
14
+ export async function getDetails(provider, operatorIdOrName) {
16
15
  const realId = await getSingleRealId(operatorIdOrName);
17
16
  return getOperator({ provider, realId }).then(sendToApi);
18
17
  }
@@ -24,8 +23,7 @@ export async function getDetails (provider, operatorIdOrName) {
24
23
  * @throws {Error} If the operator is not found
25
24
  * @throws {Error} If the operator name is ambiguous
26
25
  */
27
- export async function getSingleRealId (operatorIdOrName) {
28
-
26
+ export async function getSingleRealId(operatorIdOrName) {
29
27
  if (operatorIdOrName.operator_id != null) {
30
28
  return operatorIdOrName.operator_id;
31
29
  }
@@ -34,13 +32,13 @@ export async function getSingleRealId (operatorIdOrName) {
34
32
  const operators = await findAddonsByNameOrId(name);
35
33
 
36
34
  if (operators.length === 0) {
37
- throw new Error(`Could not find ${colors.red(name)}`);
35
+ throw new Error(`Could not find ${styleText('red', name)}`);
38
36
  }
39
37
 
40
38
  if (operators.length > 1) {
41
39
  throw new Error(dedent`
42
- Ambiguous name ${colors.red(name)}, use the real ID instead:
43
- ${colors.grey(operators.map((otoroshi) => `- ${otoroshi.name} (${otoroshi.realId})`).join('\n'))}
40
+ Ambiguous name ${styleText('red', name)}, use the real ID instead:
41
+ ${styleText('grey', operators.map((otoroshi) => `- ${otoroshi.name} (${otoroshi.realId})`).join('\n'))}
44
42
  `);
45
43
  }
46
44
 
@@ -1,9 +1,8 @@
1
- import _ from 'lodash';
2
-
3
1
  import { getSummary } from '@clevercloud/client/esm/api/v2/user.js';
4
- import { sendToApi } from '../models/send-to-api.js';
2
+ import _ from 'lodash';
3
+ import { sendToApi } from './send-to-api.js';
5
4
 
6
- export async function getId (orgaIdOrName) {
5
+ export async function getId(orgaIdOrName) {
7
6
  if (orgaIdOrName == null) {
8
7
  return null;
9
8
  }
@@ -12,12 +11,10 @@ export async function getId (orgaIdOrName) {
12
11
  return orgaIdOrName.orga_id;
13
12
  }
14
13
 
15
- return getByName(orgaIdOrName.orga_name)
16
- .then((orga) => orga.id);
14
+ return getByName(orgaIdOrName.orga_name).then((orga) => orga.id);
17
15
  }
18
16
 
19
- async function getByName (name) {
20
-
17
+ async function getByName(name) {
21
18
  const fullSummary = await getSummary({}).then(sendToApi);
22
19
  const filteredOrgs = _.filter(fullSummary.organisations, { name });
23
20
 
@@ -1,11 +1,11 @@
1
- import { Logger } from '../logger.js';
2
1
  import { addOauthHeader } from '@clevercloud/client/esm/oauth.js';
3
- import { conf, loadOAuthConf } from './configuration.js';
4
2
  import { prefixUrl } from '@clevercloud/client/esm/prefix-url.js';
5
3
  import { request } from '@clevercloud/client/esm/request.fetch.js';
6
4
  import { subtle as cryptoSuble } from 'node:crypto';
5
+ import { styleText } from 'node:util';
7
6
  import { addOauthHeaderPlaintext } from '../clever-client/auth-bridge.js';
8
- import colors from 'colors/safe.js';
7
+ import { Logger } from '../logger.js';
8
+ import { conf, loadOAuthConf } from './configuration.js';
9
9
 
10
10
  // Required for @clevercloud/client with "old" Node.js
11
11
  if (globalThis.crypto == null) {
@@ -14,7 +14,7 @@ if (globalThis.crypto == null) {
14
14
  };
15
15
  }
16
16
 
17
- async function loadTokens () {
17
+ async function loadTokens() {
18
18
  const tokens = await loadOAuthConf();
19
19
  return {
20
20
  OAUTH_CONSUMER_KEY: conf.OAUTH_CONSUMER_KEY,
@@ -24,33 +24,37 @@ async function loadTokens () {
24
24
  };
25
25
  }
26
26
 
27
- export async function sendToApi (requestParams) {
27
+ export async function sendToApi(requestParams) {
28
28
  const tokens = await loadTokens();
29
29
  return Promise.resolve(requestParams)
30
30
  .then(prefixUrl(conf.API_HOST))
31
31
  .then(addOauthHeader(tokens))
32
32
  .then((requestParams) => {
33
- Logger.debug(`${requestParams.method.toUpperCase()} ${requestParams.url} ? ${JSON.stringify(requestParams.queryParams)}`);
33
+ Logger.debug(
34
+ `${requestParams.method.toUpperCase()} ${requestParams.url} ? ${JSON.stringify(requestParams.queryParams)}`,
35
+ );
34
36
  return requestParams;
35
37
  })
36
38
  .then(request)
37
39
  .catch(processError);
38
40
  }
39
41
 
40
- export async function sendToAuthBridge (requestParams) {
42
+ export async function sendToAuthBridge(requestParams) {
41
43
  const tokens = await loadTokens();
42
44
  return Promise.resolve(requestParams)
43
45
  .then(prefixUrl(conf.AUTH_BRIDGE_HOST))
44
46
  .then(addOauthHeaderPlaintext(tokens))
45
47
  .then((requestParams) => {
46
- Logger.debug(`${requestParams.method.toUpperCase()} ${requestParams.url} ? ${JSON.stringify(requestParams.queryParams)}`);
48
+ Logger.debug(
49
+ `${requestParams.method.toUpperCase()} ${requestParams.url} ? ${JSON.stringify(requestParams.queryParams)}`,
50
+ );
47
51
  return requestParams;
48
52
  })
49
53
  .then(request)
50
54
  .catch(processError);
51
55
  }
52
56
 
53
- export function processError (error) {
57
+ export function processError(error) {
54
58
  const code = error.code ?? error?.cause?.code;
55
59
  if (code === 'EAI_AGAIN') {
56
60
  throw new Error('Cannot reach the Clever Cloud API, please check your internet connection.', { cause: error });
@@ -59,12 +63,15 @@ export function processError (error) {
59
63
  throw new Error('The connection to the Clever Cloud API was closed abruptly, please try again.', { cause: error });
60
64
  }
61
65
  if (error?.response?.status === 401) {
62
- throw new Error(`You're not logged in, use ${colors.red('clever login')} command to connect to your Clever Cloud account`, { cause: error });
66
+ throw new Error(
67
+ `You're not logged in, use ${styleText('red', 'clever login')} command to connect to your Clever Cloud account`,
68
+ { cause: error },
69
+ );
63
70
  }
64
71
  throw error;
65
72
  }
66
73
 
67
- export async function getHostAndTokens () {
74
+ export async function getHostAndTokens() {
68
75
  const tokens = await loadTokens();
69
76
  return {
70
77
  apiHost: conf.API_HOST,
@@ -1,17 +1,18 @@
1
1
  import { get } from '@clevercloud/client/esm/api/v2/organisation.js';
2
- import { sendToApi } from '../models/send-to-api.js';
2
+ import { sendToApi } from './send-to-api.js';
3
3
 
4
- export function getCurrent () {
4
+ export function getCurrent() {
5
5
  return get({}).then(sendToApi);
6
6
  }
7
7
 
8
- export function getCurrentId () {
9
- return get({}).then(sendToApi)
8
+ export function getCurrentId() {
9
+ return get({})
10
+ .then(sendToApi)
10
11
  .then(({ id }) => id);
11
12
  }
12
13
 
13
14
  // TODO move to clever client
14
- export function getCurrentToken () {
15
+ export function getCurrentToken() {
15
16
  return Promise.resolve({
16
17
  method: 'get',
17
18
  url: '/v2/self/tokens/current',
@@ -1,6 +1,6 @@
1
+ import openPage from 'open';
1
2
  import { Logger } from '../logger.js';
2
3
  import { conf } from './configuration.js';
3
- import openPage from 'open';
4
4
 
5
5
  // Inspirations:
6
6
  // https://github.com/sindresorhus/p-defer/blob/master/index.js
@@ -8,8 +8,7 @@ import openPage from 'open';
8
8
 
9
9
  // When you mix async/await APIs with event emitters callbacks, it's hard to keep a proper error flow without a good old deferred.
10
10
  export class Deferred {
11
-
12
- constructor () {
11
+ constructor() {
13
12
  this.promise = new Promise((resolve, reject) => {
14
13
  this.resolve = resolve;
15
14
  this.reject = reject;
@@ -23,10 +22,8 @@ export class Deferred {
23
22
  * @param {string} message The message to display before opening the URL
24
23
  * @returns {Promise<void>} A promise that resolves when the URL is opened
25
24
  */
26
- export function openBrowser (urlOrPath, message) {
27
- const url = urlOrPath.startsWith('/')
28
- ? `${conf.CONSOLE_URL}${urlOrPath}`
29
- : urlOrPath;
25
+ export function openBrowser(urlOrPath, message) {
26
+ const url = urlOrPath.startsWith('/') ? `${conf.CONSOLE_URL}${urlOrPath}` : urlOrPath;
30
27
 
31
28
  Logger.debug(`Opening URL "${url}" in browser`);
32
29
  Logger.println(message);
@@ -34,7 +31,7 @@ export function openBrowser (urlOrPath, message) {
34
31
  return openPage(url, { wait: false });
35
32
  }
36
33
 
37
- export function truncateWithEllipsis (length, string) {
34
+ export function truncateWithEllipsis(length, string) {
38
35
  if (string.length > length - 1) {
39
36
  return string.substring(0, length - 1) + '…';
40
37
  }
@@ -1,11 +1,9 @@
1
+ import { ERROR_TYPES, parseRaw, toNameValueObject, validateName } from '@clevercloud/client/esm/utils/env-vars.js';
1
2
  import _countBy from 'lodash/countBy.js';
2
3
  import readline from 'node:readline';
3
- import { ERROR_TYPES, parseRaw, toNameValueObject, validateName } from '@clevercloud/client/esm/utils/env-vars.js';
4
-
5
- function readStdin () {
6
4
 
5
+ function readStdin() {
7
6
  return new Promise((resolve, reject) => {
8
-
9
7
  const rl = readline.createInterface({
10
8
  input: process.stdin,
11
9
  output: process.stdout,
@@ -31,31 +29,32 @@ function readStdin () {
31
29
  // a "name" property with a string and value
32
30
  // a "value" property with a string and value
33
31
  // TODO: This should be moved and unit tested in the clever-client repo
34
- function parseFromJson (rawStdin) {
35
-
32
+ function parseFromJson(rawStdin) {
36
33
  let variables;
37
34
  try {
38
35
  variables = JSON.parse(rawStdin);
39
- }
40
- catch (e) {
36
+ } catch (e) {
41
37
  throw new Error(`Error when parsing JSON input: ${e.message}`);
42
38
  }
43
39
 
44
40
  if (!Array.isArray(variables) || variables.some((entry) => typeof entry !== 'object')) {
45
- throw new Error('The input was valid JSON but it does not follow the correct format. It must be an array of objects.');
41
+ throw new Error(
42
+ 'The input was valid JSON but it does not follow the correct format. It must be an array of objects.',
43
+ );
46
44
  }
47
45
 
48
46
  const someEntriesDontHaveNameAndValueAsString = variables.some(({ name, value }) => {
49
- return (typeof name !== 'string') || (typeof value !== 'string');
47
+ return typeof name !== 'string' || typeof value !== 'string';
50
48
  });
51
49
  if (someEntriesDontHaveNameAndValueAsString) {
52
- throw new Error('The input was a valid JSON array of objects but all entries must have properties "name" and "value" of type string. Ex: { "name": "THE_NAME", "value": "the value" }');
50
+ throw new Error(
51
+ 'The input was a valid JSON array of objects but all entries must have properties "name" and "value" of type string. Ex: { "name": "THE_NAME", "value": "the value" }',
52
+ );
53
53
  }
54
54
 
55
55
  const namesOccurences = _countBy(variables, 'name');
56
- const duplicatedNames = Object
57
- .entries(namesOccurences)
58
- .filter(([name, count]) => count > 1)
56
+ const duplicatedNames = Object.entries(namesOccurences)
57
+ .filter(([_name, count]) => count > 1)
59
58
  .map(([name]) => `"${name}"`)
60
59
  .join(', ');
61
60
 
@@ -75,11 +74,10 @@ function parseFromJson (rawStdin) {
75
74
  return toNameValueObject(variables);
76
75
  }
77
76
 
78
- function parseFromNameEqualsValue (rawStdin) {
77
+ function parseFromNameEqualsValue(rawStdin) {
79
78
  const { variables, errors } = parseRaw(rawStdin);
80
79
 
81
80
  if (errors.length !== 0) {
82
-
83
81
  const formattedErrors = errors
84
82
  .map(({ type, name, pos }) => {
85
83
  if (type === ERROR_TYPES.INVALID_NAME) {
@@ -95,7 +93,8 @@ function parseFromNameEqualsValue (rawStdin) {
95
93
  return `line ${pos.line}: the value is not valid, if you use quotes, you need to escape them like this: \\" or quote the whole value.`;
96
94
  }
97
95
  return 'Unknown error in your input';
98
- }).join('\n');
96
+ })
97
+ .join('\n');
99
98
 
100
99
  throw new Error(formattedErrors);
101
100
  }
@@ -103,8 +102,7 @@ function parseFromNameEqualsValue (rawStdin) {
103
102
  return toNameValueObject(variables);
104
103
  }
105
104
 
106
- export async function readVariablesFromStdin (format) {
107
-
105
+ export async function readVariablesFromStdin(format) {
108
106
  const rawStdin = await readStdin();
109
107
 
110
108
  switch (format) {
@@ -113,6 +111,6 @@ export async function readVariablesFromStdin (format) {
113
111
  case 'json':
114
112
  return parseFromJson(rawStdin);
115
113
  default:
116
- throw new Error('Unrecognized environment input format. Available formats are \'name-equals-value\' and \'json\'');
114
+ throw new Error("Unrecognized environment input format. Available formats are 'name-equals-value' and 'json'");
117
115
  }
118
116
  }