@contentstack/cli-utilities 1.16.0 → 1.17.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.
@@ -6,19 +6,24 @@ const management_1 = require("@contentstack/management");
6
6
  const auth_handler_1 = tslib_1.__importDefault(require("./auth-handler"));
7
7
  const node_https_1 = require("node:https");
8
8
  const config_handler_1 = tslib_1.__importDefault(require("./config-handler"));
9
+ const proxy_helper_1 = require("./proxy-helper");
10
+ const dotenv_1 = tslib_1.__importDefault(require("dotenv"));
11
+ dotenv_1.default.config();
9
12
  class ManagementSDKInitiator {
10
13
  constructor() { }
11
14
  init(context) {
12
15
  this.analyticsInfo = context === null || context === void 0 ? void 0 : context.analyticsInfo;
13
16
  }
14
17
  async createAPIClient(config) {
18
+ // Get proxy configuration with priority: Environment variables > Global config
19
+ const proxyConfig = (0, proxy_helper_1.getProxyConfig)();
15
20
  const option = {
16
21
  host: config.host,
17
22
  maxContentLength: config.maxContentLength || 100000000,
18
23
  maxBodyLength: config.maxBodyLength || 1000000000,
19
24
  maxRequests: 10,
20
25
  retryLimit: 3,
21
- timeout: 60000,
26
+ timeout: proxyConfig ? 10000 : 60000,
22
27
  delayMs: config.delayMs,
23
28
  httpsAgent: new node_https_1.Agent({
24
29
  maxSockets: 100,
@@ -31,6 +36,28 @@ class ManagementSDKInitiator {
31
36
  retryDelay: Math.floor(Math.random() * (8000 - 3000 + 1) + 3000),
32
37
  logHandler: (level, data) => { },
33
38
  retryCondition: (error) => {
39
+ // Don't retry proxy connection errors - fail fast
40
+ // Check if proxy is configured and this is a connection error
41
+ if (proxyConfig) {
42
+ const errorCode = error.code || error.errno || error.syscall;
43
+ const errorMessage = error.message || String(error);
44
+ // Detect proxy connection failures - don't retry these
45
+ if (errorCode === 'ECONNREFUSED' ||
46
+ errorCode === 'ETIMEDOUT' ||
47
+ errorCode === 'ENOTFOUND' ||
48
+ errorCode === 'ERR_BAD_RESPONSE' ||
49
+ (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes('ECONNREFUSED')) ||
50
+ (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes('connect ECONNREFUSED')) ||
51
+ (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes('ERR_BAD_RESPONSE')) ||
52
+ (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes('Bad response')) ||
53
+ (errorMessage === null || errorMessage === void 0 ? void 0 : errorMessage.includes('proxy')) ||
54
+ (errorCode === 'ETIMEDOUT' && proxyConfig) // Timeout with proxy likely means proxy issue
55
+ ) {
56
+ // Don't retry - return false to fail immediately
57
+ // The error will be thrown and handled by error formatter
58
+ return false;
59
+ }
60
+ }
34
61
  // LINK ***REMOVED***vascript/blob/72fee8ad75ba7d1d5bab8489ebbbbbbaefb1c880/src/core/stack.js#L49
35
62
  if (error.response && error.response.status) {
36
63
  switch (error.response.status) {
@@ -43,6 +70,7 @@ class ManagementSDKInitiator {
43
70
  return false;
44
71
  }
45
72
  }
73
+ return false;
46
74
  },
47
75
  retryDelayOptions: {
48
76
  base: 1000,
@@ -75,6 +103,9 @@ class ManagementSDKInitiator {
75
103
  });
76
104
  },
77
105
  };
106
+ if (proxyConfig) {
107
+ option.proxy = proxyConfig;
108
+ }
78
109
  if (config.endpoint) {
79
110
  option.endpoint = config.endpoint;
80
111
  }
package/lib/helpers.js CHANGED
@@ -6,6 +6,7 @@ const recheck_1 = require("recheck");
6
6
  const traverse_1 = tslib_1.__importDefault(require("traverse"));
7
7
  const auth_handler_1 = tslib_1.__importDefault(require("./auth-handler"));
8
8
  const _1 = require(".");
9
+ const proxy_helper_1 = require("./proxy-helper");
9
10
  const isAuthenticated = () => auth_handler_1.default.isAuthenticated();
10
11
  exports.isAuthenticated = isAuthenticated;
11
12
  const doesBranchExist = async (stack, branchName) => {
@@ -102,7 +103,7 @@ const validateRegex = (str) => {
102
103
  };
103
104
  exports.validateRegex = validateRegex;
104
105
  const formatError = function (error) {
105
- var _a, _b, _c, _d, _e, _f;
106
+ var _a, _b, _c, _d, _e, _f, _g;
106
107
  let parsedError;
107
108
  // Parse the error
108
109
  try {
@@ -174,7 +175,12 @@ const formatError = function (error) {
174
175
  return 'Self-signed certificate in the certificate chain! Please ensure your certificate configuration is correct and the necessary CA certificates are trusted.';
175
176
  }
176
177
  // ENHANCED: Handle network errors with user-friendly messages
177
- if (['ECONNREFUSED', 'ENOTFOUND', 'ETIMEDOUT', 'ENETUNREACH'].includes(parsedError === null || parsedError === void 0 ? void 0 : parsedError.code)) {
178
+ const networkErrorCodes = ['ECONNREFUSED', 'ENOTFOUND', 'ETIMEDOUT', 'ENETUNREACH', 'ERR_BAD_RESPONSE'];
179
+ if (networkErrorCodes.includes(parsedError === null || parsedError === void 0 ? void 0 : parsedError.code) || ((_g = parsedError === null || parsedError === void 0 ? void 0 : parsedError.message) === null || _g === void 0 ? void 0 : _g.includes('ERR_BAD_RESPONSE'))) {
180
+ // Check if proxy is configured and connection failed - likely proxy issue
181
+ if ((0, proxy_helper_1.hasProxy)()) {
182
+ return `Proxy error: Unable to connect to proxy server at ${(0, proxy_helper_1.getProxyUrl)()}. Please verify your proxy configuration. Error: ${(parsedError === null || parsedError === void 0 ? void 0 : parsedError.code) || 'Unknown'}`;
183
+ }
178
184
  return `Connection failed: Unable to reach the server. Please check your internet connection.`;
179
185
  }
180
186
  // Determine the error message
@@ -6,6 +6,7 @@ const axios_1 = tslib_1.__importDefault(require("axios"));
6
6
  const http_response_1 = require("./http-response");
7
7
  const config_handler_1 = tslib_1.__importDefault(require("../config-handler"));
8
8
  const auth_handler_1 = tslib_1.__importDefault(require("../auth-handler"));
9
+ const proxy_helper_1 = require("../proxy-helper");
9
10
  class HttpClient {
10
11
  /**
11
12
  * Createa new pending HTTP request instance.
@@ -306,14 +307,22 @@ class HttpClient {
306
307
  let counter = 0;
307
308
  this.axiosInstance.interceptors.response.use(null, async (error) => {
308
309
  var _a, _b;
309
- const { message, response } = error;
310
+ const { message, response, code } = error;
311
+ // Don't retry proxy connection errors - fail fast
312
+ const proxyErrorCodes = ['ECONNREFUSED', 'ETIMEDOUT', 'ENOTFOUND', 'ERR_BAD_RESPONSE'];
313
+ const isProxyConfigured = this.request.proxy || (0, proxy_helper_1.hasProxy)();
314
+ if (isProxyConfigured && (proxyErrorCodes.includes(code) || (message === null || message === void 0 ? void 0 : message.includes('ERR_BAD_RESPONSE')))) {
315
+ const proxyUrl = this.request.proxy && typeof this.request.proxy === 'object'
316
+ ? `${this.request.proxy.protocol}://${this.request.proxy.host}:${this.request.proxy.port}`
317
+ : (0, proxy_helper_1.getProxyUrl)();
318
+ return Promise.reject(new Error(`Proxy error: Unable to connect to proxy server at ${proxyUrl}. Please verify your proxy configuration.`));
319
+ }
310
320
  if ((_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.error_message) === null || _b === void 0 ? void 0 : _b.includes('access token is invalid or expired')) {
311
321
  const token = await this.refreshToken();
312
322
  this.headers(Object.assign(Object.assign({}, this.request.headers), { authorization: token.authorization }));
313
323
  return await this.axiosInstance(Object.assign(Object.assign({ url,
314
324
  method, withCredentials: true }, this.request), { data: this.prepareRequestPayload() }));
315
325
  }
316
- // Retry while Network timeout or Network Error
317
326
  if (!(message.includes('timeout') || message.includes('Network Error') || message.includes('getaddrinfo ENOTFOUND'))) {
318
327
  return Promise.reject(error);
319
328
  }
@@ -331,6 +340,13 @@ class HttpClient {
331
340
  this.headers({ 'x-header-ea': Object.values(earlyAccessHeaders).join(',') });
332
341
  }
333
342
  }
343
+ // Configure proxy if available (priority: request.proxy > getProxyConfig())
344
+ if (!this.request.proxy) {
345
+ const proxyConfig = (0, proxy_helper_1.getProxyConfig)();
346
+ if (proxyConfig) {
347
+ this.request.proxy = proxyConfig;
348
+ }
349
+ }
334
350
  return await this.axiosInstance(Object.assign(Object.assign({ url,
335
351
  method, withCredentials: true }, this.request), { data: this.prepareRequestPayload() }));
336
352
  }
@@ -0,0 +1,24 @@
1
+ export interface ProxyConfig {
2
+ protocol: 'http' | 'https';
3
+ host: string;
4
+ port: number;
5
+ auth?: {
6
+ username: string;
7
+ password: string;
8
+ };
9
+ }
10
+ /**
11
+ * Get proxy configuration with priority: Environment variables > Global config
12
+ * @returns ProxyConfig object or undefined if no proxy is configured
13
+ */
14
+ export declare function getProxyConfig(): ProxyConfig | undefined;
15
+ /**
16
+ * Check if proxy is configured (from any source)
17
+ * @returns true if proxy is configured, false otherwise
18
+ */
19
+ export declare function hasProxy(): boolean;
20
+ /**
21
+ * Get proxy URL string for display purposes
22
+ * @returns Proxy URL string or 'proxy server' if not available
23
+ */
24
+ export declare function getProxyUrl(): string;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getProxyUrl = exports.hasProxy = exports.getProxyConfig = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const config_handler_1 = tslib_1.__importDefault(require("./config-handler"));
6
+ /**
7
+ * Get proxy configuration with priority: Environment variables > Global config
8
+ * @returns ProxyConfig object or undefined if no proxy is configured
9
+ */
10
+ function getProxyConfig() {
11
+ // Priority 1: Check environment variables (HTTPS_PROXY or HTTP_PROXY)
12
+ const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
13
+ if (proxyUrl) {
14
+ try {
15
+ const url = new URL(proxyUrl);
16
+ const defaultPort = url.protocol === 'https:' ? 443 : 80;
17
+ const port = url.port ? Number.parseInt(url.port, 10) : defaultPort;
18
+ if (!Number.isNaN(port) && port >= 1 && port <= 65535) {
19
+ const protocol = url.protocol.replace(':', '');
20
+ const proxyConfig = {
21
+ protocol: protocol,
22
+ host: url.hostname,
23
+ port: port,
24
+ };
25
+ if (url.username || url.password) {
26
+ proxyConfig.auth = {
27
+ username: url.username,
28
+ password: url.password,
29
+ };
30
+ }
31
+ return proxyConfig;
32
+ }
33
+ }
34
+ catch (_a) {
35
+ // Invalid URL, continue to check global config
36
+ }
37
+ }
38
+ // Priority 2: Check global config store
39
+ const globalProxyConfig = config_handler_1.default.get('proxy');
40
+ if (globalProxyConfig) {
41
+ if (typeof globalProxyConfig === 'object') {
42
+ const port = globalProxyConfig.port;
43
+ if (port !== undefined && !Number.isNaN(port) && port >= 1 && port <= 65535) {
44
+ return globalProxyConfig;
45
+ }
46
+ }
47
+ else if (typeof globalProxyConfig === 'string') {
48
+ try {
49
+ const url = new URL(globalProxyConfig);
50
+ const defaultPort = url.protocol === 'https:' ? 443 : 80;
51
+ const port = url.port ? Number.parseInt(url.port, 10) : defaultPort;
52
+ if (!Number.isNaN(port) && port >= 1 && port <= 65535) {
53
+ const protocol = url.protocol.replace(':', '');
54
+ const proxyConfig = {
55
+ protocol: protocol,
56
+ host: url.hostname,
57
+ port: port,
58
+ };
59
+ if (url.username || url.password) {
60
+ proxyConfig.auth = {
61
+ username: url.username,
62
+ password: url.password,
63
+ };
64
+ }
65
+ return proxyConfig;
66
+ }
67
+ }
68
+ catch (_b) {
69
+ // Invalid URL, return undefined
70
+ }
71
+ }
72
+ }
73
+ return undefined;
74
+ }
75
+ exports.getProxyConfig = getProxyConfig;
76
+ /**
77
+ * Check if proxy is configured (from any source)
78
+ * @returns true if proxy is configured, false otherwise
79
+ */
80
+ function hasProxy() {
81
+ return !!getProxyConfig() || !!process.env.HTTPS_PROXY || !!process.env.HTTP_PROXY || !!config_handler_1.default.get('proxy');
82
+ }
83
+ exports.hasProxy = hasProxy;
84
+ /**
85
+ * Get proxy URL string for display purposes
86
+ * @returns Proxy URL string or 'proxy server' if not available
87
+ */
88
+ function getProxyUrl() {
89
+ const proxyConfig = getProxyConfig();
90
+ if (proxyConfig) {
91
+ return `${proxyConfig.protocol}://${proxyConfig.host}:${proxyConfig.port}`;
92
+ }
93
+ const envProxy = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
94
+ if (envProxy) {
95
+ return envProxy;
96
+ }
97
+ const globalProxy = config_handler_1.default.get('proxy');
98
+ if (globalProxy && typeof globalProxy === 'object') {
99
+ return `${globalProxy.protocol}://${globalProxy.host}:${globalProxy.port}`;
100
+ }
101
+ return 'proxy server';
102
+ }
103
+ exports.getProxyUrl = getProxyUrl;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentstack/cli-utilities",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Utilities for contentstack projects",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -32,7 +32,7 @@
32
32
  "author": "contentstack",
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
- "@contentstack/management": "~1.25.1",
35
+ "@contentstack/management": "~1.27.3",
36
36
  "@contentstack/marketplace-sdk": "^1.4.0",
37
37
  "@oclif/core": "^4.3.0",
38
38
  "axios": "^1.9.0",
@@ -68,7 +68,7 @@
68
68
  "@types/mkdirp": "^1.0.2",
69
69
  "@types/mocha": "^10.0.10",
70
70
  "@types/node": "^14.18.63",
71
- "@types/sinon": "^10.0.20",
71
+ "@types/sinon": "^21.0.0",
72
72
  "@types/traverse": "^0.6.37",
73
73
  "chai": "^4.5.0",
74
74
  "eslint": "^8.57.1",
@@ -77,7 +77,7 @@
77
77
  "fancy-test": "^2.0.42",
78
78
  "mocha": "10.8.2",
79
79
  "nyc": "^15.1.0",
80
- "sinon": "^19.0.5",
80
+ "sinon": "^21.0.1",
81
81
  "ts-node": "^10.9.2",
82
82
  "typescript": "^4.9.5"
83
83
  }