@contentstack/cli-utilities 1.16.1 → 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.
- package/lib/contentstack-management-sdk.js +32 -1
- package/lib/helpers.js +8 -2
- package/lib/http-client/client.js +18 -2
- package/lib/proxy-helper.d.ts +24 -0
- package/lib/proxy-helper.js +103 -0
- package/package.json +3 -3
|
@@ -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
|
-
|
|
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.
|
|
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",
|
|
@@ -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": "^
|
|
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": "^
|
|
80
|
+
"sinon": "^21.0.1",
|
|
81
81
|
"ts-node": "^10.9.2",
|
|
82
82
|
"typescript": "^4.9.5"
|
|
83
83
|
}
|