@contentstack/cli-utilities 1.13.2 → 1.14.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.
- package/lib/authentication-handler.js +20 -11
- package/lib/helpers.d.ts +3 -1
- package/lib/helpers.js +47 -28
- package/lib/logger/cli-error-handler.js +3 -9
- package/lib/logger/log.js +7 -1
- package/lib/logger/logger.js +4 -0
- package/package.json +1 -1
|
@@ -26,16 +26,17 @@ class AuthenticationHandler {
|
|
|
26
26
|
index_1.cliux.print('Session timed out, please login to proceed', {
|
|
27
27
|
color: 'yellow',
|
|
28
28
|
});
|
|
29
|
-
|
|
29
|
+
throw new Error('Session timed out, please login to proceed');
|
|
30
30
|
}
|
|
31
31
|
break;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
catch (error) {
|
|
35
|
-
|
|
35
|
+
const formattedError = (0, index_1.formatError)(error);
|
|
36
|
+
index_1.cliux.print(`Error occurred while fetching auth details: ${formattedError}`, {
|
|
36
37
|
color: 'red',
|
|
37
38
|
});
|
|
38
|
-
|
|
39
|
+
throw error;
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
get isOauthEnabled() {
|
|
@@ -48,6 +49,7 @@ class AuthenticationHandler {
|
|
|
48
49
|
return this.token;
|
|
49
50
|
}
|
|
50
51
|
async refreshAccessToken(error, maxRetryCount = 1) {
|
|
52
|
+
var _a, _b;
|
|
51
53
|
// Add configurable delay only for CI/CD pipelines
|
|
52
54
|
const delayMs = process.env.DELAY_MS;
|
|
53
55
|
if (delayMs) {
|
|
@@ -71,7 +73,8 @@ class AuthenticationHandler {
|
|
|
71
73
|
if (refreshed) {
|
|
72
74
|
return this.refreshAccessToken(error, maxRetryCount); // Retry after refreshing the token
|
|
73
75
|
}
|
|
74
|
-
|
|
76
|
+
const errorDetails = (0, index_1.formatError)(error);
|
|
77
|
+
index_1.cliux.print(`Authentication failed: ${errorDetails}`, { color: 'red' });
|
|
75
78
|
// For Basic Auth, exit immediately without retrying
|
|
76
79
|
return;
|
|
77
80
|
}
|
|
@@ -79,10 +82,12 @@ class AuthenticationHandler {
|
|
|
79
82
|
case 429:
|
|
80
83
|
case 408:
|
|
81
84
|
if (maxRetryCount >= 3) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
});
|
|
85
|
-
|
|
85
|
+
const errorDetails = (0, index_1.formatError)(error);
|
|
86
|
+
const statusText = ((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) === 429 ? 'Rate Limited' : 'Request Timeout';
|
|
87
|
+
index_1.cliux.print(`Max retry attempts exceeded (${maxRetryCount}/3)`, { color: 'red' });
|
|
88
|
+
index_1.cliux.print(`Status: ${(_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.status} - ${statusText}`, { color: 'yellow' });
|
|
89
|
+
index_1.cliux.print(`Error: ${errorDetails}`, { color: 'white' });
|
|
90
|
+
return;
|
|
86
91
|
}
|
|
87
92
|
maxRetryCount++; // Increment for the next retry attempt
|
|
88
93
|
// These cases require a wait, adding a delay before retrying
|
|
@@ -99,7 +104,9 @@ class AuthenticationHandler {
|
|
|
99
104
|
index_1.cliux.print('Session timed out, please login to proceed', {
|
|
100
105
|
color: 'yellow',
|
|
101
106
|
});
|
|
102
|
-
|
|
107
|
+
index_1.cliux.print('\nTo fix this:', { color: 'cyan' });
|
|
108
|
+
index_1.cliux.print('• Run: "csdx auth:login"', { color: 'white' });
|
|
109
|
+
resolve(false);
|
|
103
110
|
}
|
|
104
111
|
else if (this.authType === 'OAUTH') {
|
|
105
112
|
index_1.authHandler.host = hostName;
|
|
@@ -111,7 +118,9 @@ class AuthenticationHandler {
|
|
|
111
118
|
resolve(true);
|
|
112
119
|
})
|
|
113
120
|
.catch((error) => {
|
|
114
|
-
|
|
121
|
+
const errorDetails = (0, index_1.formatError)(error);
|
|
122
|
+
index_1.cliux.print('OAuth Token Refresh Failed', { color: 'red' });
|
|
123
|
+
index_1.cliux.print(`Error: ${errorDetails}`, { color: 'white' });
|
|
115
124
|
resolve(false);
|
|
116
125
|
});
|
|
117
126
|
}
|
|
@@ -119,7 +128,7 @@ class AuthenticationHandler {
|
|
|
119
128
|
index_1.cliux.print('You do not have the permissions to perform this action, please login to proceed', {
|
|
120
129
|
color: 'yellow',
|
|
121
130
|
});
|
|
122
|
-
|
|
131
|
+
resolve(false);
|
|
123
132
|
}
|
|
124
133
|
});
|
|
125
134
|
}
|
package/lib/helpers.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { ContentstackClient } from '.';
|
|
1
2
|
export declare const isAuthenticated: () => boolean;
|
|
2
3
|
export declare const doesBranchExist: (stack: any, branchName: any) => Promise<any>;
|
|
4
|
+
export declare const getBranchFromAlias: (stack: ReturnType<ContentstackClient['stack']>, branchAlias: string) => Promise<string>;
|
|
3
5
|
export declare const isManagementTokenValid: (stackAPIKey: any, managementToken: any) => Promise<{
|
|
4
6
|
valid: boolean;
|
|
5
7
|
message?: undefined;
|
|
@@ -17,7 +19,7 @@ export declare const sanitizePath: (str: string) => string;
|
|
|
17
19
|
export declare const validateUids: (uid: any) => boolean;
|
|
18
20
|
export declare const validateFileName: (fileName: any) => boolean;
|
|
19
21
|
export declare const validateRegex: (str: unknown) => import("recheck").Diagnostics;
|
|
20
|
-
export declare const formatError: (error: any) =>
|
|
22
|
+
export declare const formatError: (error: any) => string;
|
|
21
23
|
/**
|
|
22
24
|
* The function redactObject takes an object as input and replaces any sensitive keys with the string
|
|
23
25
|
* '[REDACTED]'.
|
package/lib/helpers.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.redactObject = exports.formatError = exports.validateRegex = exports.validateFileName = exports.validateUids = exports.sanitizePath = exports.escapeRegExp = exports.validatePath = exports.createDeveloperHubUrl = exports.isManagementTokenValid = exports.doesBranchExist = exports.isAuthenticated = void 0;
|
|
3
|
+
exports.redactObject = exports.formatError = exports.validateRegex = exports.validateFileName = exports.validateUids = exports.sanitizePath = exports.escapeRegExp = exports.validatePath = exports.createDeveloperHubUrl = exports.isManagementTokenValid = exports.getBranchFromAlias = exports.doesBranchExist = exports.isAuthenticated = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const recheck_1 = require("recheck");
|
|
6
6
|
const traverse_1 = tslib_1.__importDefault(require("traverse"));
|
|
@@ -17,6 +17,22 @@ const doesBranchExist = async (stack, branchName) => {
|
|
|
17
17
|
});
|
|
18
18
|
};
|
|
19
19
|
exports.doesBranchExist = doesBranchExist;
|
|
20
|
+
const getBranchFromAlias = async (stack, branchAlias) => {
|
|
21
|
+
if (!stack || !branchAlias || typeof branchAlias !== 'string') {
|
|
22
|
+
throw new Error('Invalid input. Both stack and branch alias are required.');
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const response = await stack.branchAlias(branchAlias).fetch();
|
|
26
|
+
if (!(response === null || response === void 0 ? void 0 : response.uid)) {
|
|
27
|
+
throw new Error(`Invalid Branch Alias. No Branch found for the branch alias: ${branchAlias}`);
|
|
28
|
+
}
|
|
29
|
+
return response.uid;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
exports.getBranchFromAlias = getBranchFromAlias;
|
|
20
36
|
const isManagementTokenValid = async (stackAPIKey, managementToken) => {
|
|
21
37
|
var _a;
|
|
22
38
|
const httpClient = new _1.HttpClient({ headers: { api_key: stackAPIKey, authorization: managementToken } });
|
|
@@ -86,7 +102,7 @@ const validateRegex = (str) => {
|
|
|
86
102
|
};
|
|
87
103
|
exports.validateRegex = validateRegex;
|
|
88
104
|
const formatError = function (error) {
|
|
89
|
-
var _a, _b, _c, _d, _e;
|
|
105
|
+
var _a, _b, _c, _d, _e, _f;
|
|
90
106
|
let parsedError;
|
|
91
107
|
// Parse the error
|
|
92
108
|
try {
|
|
@@ -103,6 +119,26 @@ const formatError = function (error) {
|
|
|
103
119
|
catch (e) {
|
|
104
120
|
parsedError = error;
|
|
105
121
|
}
|
|
122
|
+
// Helper function to append error details
|
|
123
|
+
const appendErrorDetails = (message, errorObj) => {
|
|
124
|
+
if (errorObj.errors && typeof errorObj.errors === 'object' && Object.keys(errorObj.errors).length > 0) {
|
|
125
|
+
const entityNames = {
|
|
126
|
+
authorization: 'Authentication',
|
|
127
|
+
api_key: 'Stack API key',
|
|
128
|
+
uid: 'Content Type',
|
|
129
|
+
access_token: 'Delivery Token',
|
|
130
|
+
};
|
|
131
|
+
const errorList = Object.entries(errorObj.errors)
|
|
132
|
+
.map(([field, errors]) => {
|
|
133
|
+
const errorArray = Array.isArray(errors) ? errors : [errors];
|
|
134
|
+
const fieldName = entityNames[field] || field;
|
|
135
|
+
return ` • ${fieldName}: ${errorArray.join(', ')}`;
|
|
136
|
+
})
|
|
137
|
+
.join('\n');
|
|
138
|
+
return `${message}\n\nError Details:\n${errorList}\n`;
|
|
139
|
+
}
|
|
140
|
+
return message;
|
|
141
|
+
};
|
|
106
142
|
if (parsedError && typeof parsedError === 'object' && Object.keys(parsedError).length === 0) {
|
|
107
143
|
if (!parsedError.message &&
|
|
108
144
|
!parsedError.code &&
|
|
@@ -113,21 +149,21 @@ const formatError = function (error) {
|
|
|
113
149
|
}
|
|
114
150
|
}
|
|
115
151
|
if ((_b = (_a = parsedError === null || parsedError === void 0 ? void 0 : parsedError.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.errorMessage) {
|
|
116
|
-
return parsedError.response.data.errorMessage;
|
|
152
|
+
return appendErrorDetails(parsedError.response.data.errorMessage, ((_c = parsedError === null || parsedError === void 0 ? void 0 : parsedError.response) === null || _c === void 0 ? void 0 : _c.data) || parsedError);
|
|
117
153
|
}
|
|
118
154
|
if (parsedError === null || parsedError === void 0 ? void 0 : parsedError.errorMessage) {
|
|
119
|
-
return parsedError.errorMessage;
|
|
155
|
+
return appendErrorDetails(parsedError.errorMessage, parsedError);
|
|
120
156
|
}
|
|
121
|
-
const status = (parsedError === null || parsedError === void 0 ? void 0 : parsedError.status) || ((
|
|
122
|
-
const errorCode = (parsedError === null || parsedError === void 0 ? void 0 : parsedError.errorCode) || ((
|
|
157
|
+
const status = (parsedError === null || parsedError === void 0 ? void 0 : parsedError.status) || ((_d = parsedError === null || parsedError === void 0 ? void 0 : parsedError.response) === null || _d === void 0 ? void 0 : _d.status);
|
|
158
|
+
const errorCode = (parsedError === null || parsedError === void 0 ? void 0 : parsedError.errorCode) || ((_f = (_e = parsedError === null || parsedError === void 0 ? void 0 : parsedError.response) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.errorCode);
|
|
123
159
|
if (status === 422 && errorCode === 104) {
|
|
124
|
-
return 'Invalid email or password. Please check your credentials and try again.';
|
|
160
|
+
return appendErrorDetails('Invalid email or password. Please check your credentials and try again.', parsedError);
|
|
125
161
|
}
|
|
126
162
|
if (status === 401) {
|
|
127
|
-
return 'Authentication failed. Please check your credentials.';
|
|
163
|
+
return appendErrorDetails('Authentication failed. Please check your credentials.', parsedError);
|
|
128
164
|
}
|
|
129
165
|
if (status === 403) {
|
|
130
|
-
return 'Access denied. Please check your permissions.';
|
|
166
|
+
return appendErrorDetails('Access denied. Please check your permissions.', parsedError);
|
|
131
167
|
}
|
|
132
168
|
// Check for specific SSL error
|
|
133
169
|
if ((parsedError === null || parsedError === void 0 ? void 0 : parsedError.code) === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY') {
|
|
@@ -156,25 +192,8 @@ const formatError = function (error) {
|
|
|
156
192
|
catch (e) {
|
|
157
193
|
// message is not in JSON format, no need to parse
|
|
158
194
|
}
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
const entityNames = {
|
|
162
|
-
authorization: 'Authentication',
|
|
163
|
-
api_key: 'Stack API key',
|
|
164
|
-
uid: 'Content Type',
|
|
165
|
-
// deepcode ignore HardcodedNonCryptoSecret: The hardcoded value 'access_token' is used as a key in an error message mapping object and does not represent a sensitive secret or cryptographic key.
|
|
166
|
-
access_token: 'Delivery Token',
|
|
167
|
-
};
|
|
168
|
-
const errorList = Object.entries(parsedError.errors)
|
|
169
|
-
.map(([field, errors]) => {
|
|
170
|
-
const errorArray = Array.isArray(errors) ? errors : [errors];
|
|
171
|
-
const fieldName = entityNames[field] || field;
|
|
172
|
-
return ` • ${fieldName}: ${errorArray.join(', ')}`;
|
|
173
|
-
})
|
|
174
|
-
.join('\n');
|
|
175
|
-
message += `\n\nAPI Errors:\n${errorList}`;
|
|
176
|
-
}
|
|
177
|
-
return message;
|
|
195
|
+
// Always append error details at the end
|
|
196
|
+
return appendErrorDetails(message, parsedError);
|
|
178
197
|
};
|
|
179
198
|
exports.formatError = formatError;
|
|
180
199
|
/**
|
|
@@ -82,21 +82,15 @@ class CLIErrorHandler {
|
|
|
82
82
|
* Extracts a clear, concise error message from various error types.
|
|
83
83
|
*/
|
|
84
84
|
extractClearMessage(error) {
|
|
85
|
-
var _a, _b
|
|
86
|
-
if ((_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.errorMessage) {
|
|
87
|
-
return error.response.data.errorMessage;
|
|
88
|
-
}
|
|
89
|
-
if (error === null || error === void 0 ? void 0 : error.errorMessage) {
|
|
90
|
-
return error.errorMessage;
|
|
91
|
-
}
|
|
85
|
+
var _a, _b;
|
|
92
86
|
// Use existing formatError function for other cases
|
|
93
87
|
try {
|
|
94
88
|
const formattedMessage = (0, helpers_1.formatError)(error);
|
|
95
89
|
return formattedMessage || 'An error occurred. Please try again.';
|
|
96
90
|
}
|
|
97
|
-
catch (
|
|
91
|
+
catch (_c) {
|
|
98
92
|
// Fallback to basic error message extraction if formatError fails
|
|
99
|
-
if (typeof ((
|
|
93
|
+
if (typeof ((_b = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.errorMessage) === 'string') {
|
|
100
94
|
return error.response.data.errorMessage;
|
|
101
95
|
}
|
|
102
96
|
if (typeof (error === null || error === void 0 ? void 0 : error.errorMessage) === 'string') {
|
package/lib/logger/log.js
CHANGED
|
@@ -10,9 +10,15 @@ const cli_error_handler_1 = require("./cli-error-handler");
|
|
|
10
10
|
const __1 = require("..");
|
|
11
11
|
let loggerInstance = null;
|
|
12
12
|
function createLoggerInstance() {
|
|
13
|
+
var _a;
|
|
14
|
+
const logConfig = __1.configHandler.get('log');
|
|
15
|
+
const logLevel = (logConfig === null || logConfig === void 0 ? void 0 : logConfig.level) || 'info';
|
|
16
|
+
const showConsoleLogs = (_a = logConfig === null || logConfig === void 0 ? void 0 : logConfig['show-console-logs']) !== null && _a !== void 0 ? _a : false;
|
|
13
17
|
const config = {
|
|
14
18
|
basePath: getLogPath(),
|
|
15
|
-
logLevel:
|
|
19
|
+
logLevel: logLevel,
|
|
20
|
+
consoleLoggingEnabled: showConsoleLogs,
|
|
21
|
+
consoleLogLevel: logLevel,
|
|
16
22
|
};
|
|
17
23
|
return new logger_1.default(config);
|
|
18
24
|
}
|
package/lib/logger/logger.js
CHANGED
|
@@ -95,6 +95,10 @@ class Logger {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
shouldLog(level, target) {
|
|
98
|
+
// If console logging is disabled, don't log to console
|
|
99
|
+
if (target === 'console' && this.config.consoleLoggingEnabled === false) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
98
102
|
const configLevel = target === 'console' ? this.config.consoleLogLevel : this.config.logLevel;
|
|
99
103
|
const minLevel = configLevel ? logging_1.logLevels[configLevel] : 2;
|
|
100
104
|
return logging_1.logLevels[level] <= minLevel;
|