@contentstack/cli-utilities 2.0.0-beta.9 → 2.0.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.
@@ -91,7 +91,9 @@ class AuthHandler {
91
91
  async initSDK() {
92
92
  // Ensure we have a valid host for the SDK initialization
93
93
  const host = this._host || this.getCmaHost();
94
- this.managementAPIClient = await (0, contentstack_management_sdk_1.default)({ host });
94
+ // skipTokenValidity: true avoids calling compareOAuthExpiry here, which would deadlock
95
+ // when initSDK is itself called from inside a compareOAuthExpiry refresh cycle.
96
+ this.managementAPIClient = await (0, contentstack_management_sdk_1.default)({ host, skipTokenValidity: true });
95
97
  this.oauthHandler = this.managementAPIClient.oauth({
96
98
  appId: this.OAuthAppId,
97
99
  clientId: this.OAuthClientId,
@@ -347,7 +349,17 @@ class AuthHandler {
347
349
  return this.oauthRefreshInFlight;
348
350
  }
349
351
  this.isRefreshingToken = true;
350
- this.oauthRefreshInFlight = (async () => {
352
+ // Set the guard promise BEFORE starting the async work so re-entrant synchronous
353
+ // callers (e.g. compareOAuthExpiry invoked again via refreshToken → initSDK →
354
+ // managementSDKClient → createAPIClient) see a non-null oauthRefreshInFlight and
355
+ // return early instead of starting another recursive refresh cycle.
356
+ let _resolve;
357
+ let _reject;
358
+ this.oauthRefreshInFlight = new Promise((res, rej) => {
359
+ _resolve = res;
360
+ _reject = rej;
361
+ });
362
+ (async () => {
351
363
  try {
352
364
  if (force) {
353
365
  cli_ux_1.default.print('Forcing token refresh...');
@@ -356,10 +368,11 @@ class AuthHandler {
356
368
  cli_ux_1.default.print('Token expired, refreshing the token');
357
369
  }
358
370
  await this.refreshToken();
371
+ _resolve();
359
372
  }
360
373
  catch (error) {
361
374
  cli_ux_1.default.error('Error refreshing token');
362
- throw error;
375
+ _reject(error);
363
376
  }
364
377
  finally {
365
378
  this.isRefreshingToken = false;
@@ -53,6 +53,11 @@ class AuthenticationHandler {
53
53
  if (error.response && error.response.status) {
54
54
  switch (error.response.status) {
55
55
  case 401:
56
+ if (maxRetryCount >= 2) {
57
+ const errorDetails = (0, index_1.formatError)(error);
58
+ index_1.cliux.print(`Authentication failed after token refresh: ${errorDetails}`, { color: 'red' });
59
+ return;
60
+ }
56
61
  // NOTE: Refresh the token if the type is OAuth.
57
62
  const region = index_1.configHandler.get('region') || {};
58
63
  if (region === null || region === void 0 ? void 0 : region.cma) {
@@ -65,11 +70,10 @@ class AuthenticationHandler {
65
70
  hostName = hostName || region.cma;
66
71
  const refreshed = await this.refreshToken(hostName);
67
72
  if (refreshed) {
68
- return this.refreshAccessToken(error, maxRetryCount); // Retry after refreshing the token
73
+ return this.refreshAccessToken(error, maxRetryCount + 1);
69
74
  }
70
75
  const errorDetails = (0, index_1.formatError)(error);
71
76
  index_1.cliux.print(`Authentication failed: ${errorDetails}`, { color: 'red' });
72
- // For Basic Auth, exit immediately without retrying
73
77
  return;
74
78
  }
75
79
  break;
@@ -13,4 +13,4 @@ export declare const levelColors: {
13
13
  info: string;
14
14
  debug: string;
15
15
  };
16
- export declare const PROGRESS_SUPPORTED_MODULES: readonly ["export", "import", "audit", "import-setup", "clone"];
16
+ export declare const PROGRESS_SUPPORTED_MODULES: readonly ["export", "import", "audit", "import-setup", "clone", "bulk-operations"];
@@ -17,4 +17,4 @@ exports.levelColors = {
17
17
  info: 'white',
18
18
  debug: 'blue',
19
19
  };
20
- exports.PROGRESS_SUPPORTED_MODULES = ['export', 'import', 'audit', 'import-setup', 'clone'];
20
+ exports.PROGRESS_SUPPORTED_MODULES = ['export', 'import', 'audit', 'import-setup', 'clone', 'bulk-operations'];
@@ -1,7 +1,2 @@
1
- /**
2
- * Reads all content type schema files from a directory
3
- * @param dirPath - Path to content types directory
4
- * @param ignoredFiles - Files to ignore (defaults to schema.json, .DS_Store, __master.json, __priority.json)
5
- * @returns Array of content type schemas (empty if the path is missing or has no eligible files)
6
- */
1
+ export declare function readGlobalFieldSchemas(dirPath: string, ignoredFiles?: string[]): Record<string, unknown>[];
7
2
  export declare function readContentTypeSchemas(dirPath: string, ignoredFiles?: string[]): Record<string, unknown>[];
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readGlobalFieldSchemas = readGlobalFieldSchemas;
3
4
  exports.readContentTypeSchemas = readContentTypeSchemas;
4
5
  const node_fs_1 = require("node:fs");
5
6
  const node_path_1 = require("node:path");
@@ -9,6 +10,10 @@ const node_path_1 = require("node:path");
9
10
  * @param ignoredFiles - Files to ignore (defaults to schema.json, .DS_Store, __master.json, __priority.json)
10
11
  * @returns Array of content type schemas (empty if the path is missing or has no eligible files)
11
12
  */
13
+ const DEFAULT_GF_IGNORED_FILES = ['schema.json', '.DS_Store', '__master.json', '__priority.json', 'field_rules_uid.json', 'globalfields.json'];
14
+ function readGlobalFieldSchemas(dirPath, ignoredFiles = DEFAULT_GF_IGNORED_FILES) {
15
+ return readContentTypeSchemas(dirPath, ignoredFiles);
16
+ }
12
17
  function readContentTypeSchemas(dirPath, ignoredFiles = ['schema.json', '.DS_Store', '__master.json', '__priority.json', 'field_rules_uid.json']) {
13
18
  if (!(0, node_fs_1.existsSync)(dirPath)) {
14
19
  return [];
@@ -0,0 +1,9 @@
1
+ export interface AuthHeaders {
2
+ [key: string]: string;
3
+ }
4
+ export declare function buildAuthHeaders(ctx?: {
5
+ managementToken?: string;
6
+ apiKey?: string;
7
+ authToken?: string;
8
+ orgUid?: string;
9
+ }): AuthHeaders;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildAuthHeaders = buildAuthHeaders;
4
+ const tslib_1 = require("tslib");
5
+ const config_handler_1 = tslib_1.__importDefault(require("../config-handler"));
6
+ function buildAuthHeaders(ctx) {
7
+ var _a, _b;
8
+ // 1. Management token takes priority (no 'Bearer' prefix — API contract)
9
+ if ((ctx === null || ctx === void 0 ? void 0 : ctx.managementToken) && (ctx === null || ctx === void 0 ? void 0 : ctx.apiKey)) {
10
+ return {
11
+ Authorization: ctx.managementToken,
12
+ api_key: ctx.apiKey,
13
+ };
14
+ }
15
+ // 2. OAuth
16
+ const oauthToken = config_handler_1.default.get('oauthAccessToken');
17
+ if (oauthToken) {
18
+ return { Authorization: `Bearer ${oauthToken}` };
19
+ }
20
+ // 3. Authtoken + organization_uid
21
+ const authtoken = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.authToken) !== null && _a !== void 0 ? _a : config_handler_1.default.get('authtoken');
22
+ const orgUid = (_b = ctx === null || ctx === void 0 ? void 0 : ctx.orgUid) !== null && _b !== void 0 ? _b : config_handler_1.default.get('oauthOrgUid');
23
+ if (authtoken && orgUid) {
24
+ return { authtoken, organization_uid: orgUid };
25
+ }
26
+ // 4. Authtoken + api_key
27
+ if (authtoken && (ctx === null || ctx === void 0 ? void 0 : ctx.apiKey)) {
28
+ return { authtoken, api_key: ctx.apiKey };
29
+ }
30
+ throw new Error('PLAN_CHECK: Cannot build auth headers — no valid credentials available. ' +
31
+ 'Please log in or provide a management token alias.');
32
+ }
@@ -0,0 +1,3 @@
1
+ import { FeatureStatus, FeatureCtx } from './types';
2
+ export declare function isFeatureEnabled(featureUid: string, ctx?: FeatureCtx): Promise<FeatureStatus>;
3
+ export declare function assertFeatureEnabled(featureUid: string, ctx?: FeatureCtx): Promise<FeatureStatus>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isFeatureEnabled = isFeatureEnabled;
4
+ exports.assertFeatureEnabled = assertFeatureEnabled;
5
+ const http_client_1 = require("../http-client");
6
+ const resolve_auth_host_1 = require("./resolve-auth-host");
7
+ const build_auth_headers_1 = require("./build-auth-headers");
8
+ async function isFeatureEnabled(featureUid, ctx) {
9
+ const host = (0, resolve_auth_host_1.resolveAuthHost)(ctx);
10
+ const headers = (0, build_auth_headers_1.buildAuthHeaders)(ctx);
11
+ const client = new http_client_1.HttpClient();
12
+ client.baseUrl(host).headers(headers);
13
+ const res = await client.get(`/v1/feature-status?feature_uid=${encodeURIComponent(featureUid)}`);
14
+ if (res.status < 200 || res.status >= 300) {
15
+ throw new Error(`PLAN_CHECK: feature-status API returned ${res.status} for "${featureUid}".`);
16
+ }
17
+ return res.data;
18
+ }
19
+ async function assertFeatureEnabled(featureUid, ctx) {
20
+ let status;
21
+ try {
22
+ status = await isFeatureEnabled(featureUid, ctx);
23
+ }
24
+ catch (e) {
25
+ throw new Error(`Could not verify your plan for "${featureUid}". ` +
26
+ `This command requires a confirmed plan status to run. ` +
27
+ `Please retry; if the problem persists contact support. (${e.message})`);
28
+ }
29
+ if (!status.is_part_of_plan) {
30
+ throw new Error(`"${featureUid}" is not part of your current plan. Please upgrade your plan to use this feature.`);
31
+ }
32
+ if (status.status !== 'enabled') {
33
+ throw new Error(`"${featureUid}" is not enabled for your plan (status: ${status.status}).`);
34
+ }
35
+ return status;
36
+ }
@@ -0,0 +1,5 @@
1
+ export declare const FEATURE: {
2
+ readonly ASSET_MANAGEMENT: "amAssets";
3
+ readonly ASSET_SCANNING: "assetsScan";
4
+ };
5
+ export type FeatureUid = (typeof FEATURE)[keyof typeof FEATURE];
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FEATURE = void 0;
4
+ exports.FEATURE = {
5
+ ASSET_MANAGEMENT: 'amAssets',
6
+ ASSET_SCANNING: 'assetsScan',
7
+ };
@@ -0,0 +1,5 @@
1
+ export * from './types';
2
+ export * from './feature-uids';
3
+ export { resolveAuthHost } from './resolve-auth-host';
4
+ export { buildAuthHeaders } from './build-auth-headers';
5
+ export { isFeatureEnabled, assertFeatureEnabled } from './feature-status-handler';
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertFeatureEnabled = exports.isFeatureEnabled = exports.buildAuthHeaders = exports.resolveAuthHost = void 0;
4
+ const tslib_1 = require("tslib");
5
+ tslib_1.__exportStar(require("./types"), exports);
6
+ tslib_1.__exportStar(require("./feature-uids"), exports);
7
+ var resolve_auth_host_1 = require("./resolve-auth-host");
8
+ Object.defineProperty(exports, "resolveAuthHost", { enumerable: true, get: function () { return resolve_auth_host_1.resolveAuthHost; } });
9
+ var build_auth_headers_1 = require("./build-auth-headers");
10
+ Object.defineProperty(exports, "buildAuthHeaders", { enumerable: true, get: function () { return build_auth_headers_1.buildAuthHeaders; } });
11
+ var feature_status_handler_1 = require("./feature-status-handler");
12
+ Object.defineProperty(exports, "isFeatureEnabled", { enumerable: true, get: function () { return feature_status_handler_1.isFeatureEnabled; } });
13
+ Object.defineProperty(exports, "assertFeatureEnabled", { enumerable: true, get: function () { return feature_status_handler_1.assertFeatureEnabled; } });
@@ -0,0 +1,8 @@
1
+ export declare function resolveAuthHost(ctx?: {
2
+ region?: {
3
+ endpoints?: {
4
+ auth?: string;
5
+ };
6
+ cma?: string;
7
+ };
8
+ }): string;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveAuthHost = resolveAuthHost;
4
+ const tslib_1 = require("tslib");
5
+ const config_handler_1 = tslib_1.__importDefault(require("../config-handler"));
6
+ // Mirrors config:set:region's own custom-region fallback (region.ts's transformUrl) —
7
+ // same heuristic, applied retroactively for a custom region that predates auth-api.
8
+ // Computed fresh on each call, never persisted back to config.
9
+ function deriveAuthFromCma(cma) {
10
+ let transformed = cma.replace('api', 'auth-api');
11
+ if (transformed.startsWith('http')) {
12
+ transformed = transformed.split('//')[1];
13
+ }
14
+ transformed = transformed.replace(/^dev\d+/, 'dev');
15
+ transformed = transformed.endsWith('io') ? transformed.replace('io', 'com') : transformed;
16
+ return `https://${transformed}`;
17
+ }
18
+ function resolveAuthHost(ctx) {
19
+ var _a, _b;
20
+ const region = ((_a = ctx === null || ctx === void 0 ? void 0 : ctx.region) !== null && _a !== void 0 ? _a : config_handler_1.default.get('region'));
21
+ let authUrl = (_b = region === null || region === void 0 ? void 0 : region.endpoints) === null || _b === void 0 ? void 0 : _b.auth;
22
+ if (!authUrl && (region === null || region === void 0 ? void 0 : region.cma)) {
23
+ authUrl = deriveAuthFromCma(region.cma);
24
+ }
25
+ if (!authUrl) {
26
+ throw new Error('PLAN_CHECK: Auth host is not configured for the current region. ' +
27
+ "Re-run `csdx config:set:region` to refresh region endpoints.");
28
+ }
29
+ return String(authUrl).replace(/\/$/, '');
30
+ }
@@ -0,0 +1,21 @@
1
+ export interface FeatureStatus {
2
+ org_uid: string;
3
+ feature_key: string;
4
+ status: 'enabled' | 'disabled' | string;
5
+ limit: number;
6
+ max_limit: number;
7
+ is_part_of_plan: boolean;
8
+ }
9
+ export interface FeatureCtx {
10
+ apiKey?: string;
11
+ orgUid?: string;
12
+ managementToken?: string;
13
+ authToken?: string;
14
+ region?: {
15
+ name?: string;
16
+ cma?: string;
17
+ endpoints?: {
18
+ auth?: string;
19
+ };
20
+ };
21
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/lib/index.d.ts CHANGED
@@ -29,4 +29,7 @@ export type { ChalkInstance } from './chalk';
29
29
  export { Logger };
30
30
  export { default as authenticationHandler } from './authentication-handler';
31
31
  export { v2Logger as log, cliErrorHandler, handleAndLogError, getLogPath, getSessionLogPath } from './logger/log';
32
- export { CLIProgressManager, SummaryManager, PrimaryProcessStrategy, ProgressStrategyRegistry, CustomProgressStrategy, DefaultProgressStrategy } from './progress-summary';
32
+ export { CLIProgressManager, SummaryManager, PrimaryProcessStrategy, ProgressStrategyRegistry, CustomProgressStrategy, DefaultProgressStrategy, } from './progress-summary';
33
+ export * from './feature-status';
34
+ export * from './region-endpoints';
35
+ export { refreshRegionEndpoints } from './region-refresh';
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DefaultProgressStrategy = exports.CustomProgressStrategy = exports.ProgressStrategyRegistry = exports.PrimaryProcessStrategy = exports.SummaryManager = exports.CLIProgressManager = exports.getSessionLogPath = exports.getLogPath = exports.handleAndLogError = exports.cliErrorHandler = exports.log = exports.authenticationHandler = exports.Logger = exports.getChalk = exports.loadChalk = exports.TablePrompt = exports.execute = exports.ux = exports.flush = exports.settings = exports.toConfiguredId = exports.toStandardizedId = exports.Plugin = exports.Parser = exports.Interfaces = exports.HelpBase = exports.Help = exports.loadHelpClass = exports.Flags = exports.Errors = exports.Config = exports.CommandHelp = exports.Args = exports.marketplaceSDKInitiator = exports.MarketplaceSDKInitiator = exports.marketplaceSDKClient = exports.CLITable = exports.createLogContext = exports.Command = exports.flags = exports.args = exports.NodeCrypto = exports.managementSDKInitiator = exports.managementSDKClient = exports.configHandler = exports.authHandler = exports.messageHandler = exports.CLIError = exports.cliux = exports.LoggerService = void 0;
4
+ exports.refreshRegionEndpoints = void 0;
4
5
  const tslib_1 = require("tslib");
5
6
  const logger_1 = tslib_1.__importDefault(require("./logger"));
6
7
  exports.Logger = logger_1.default;
@@ -82,3 +83,7 @@ Object.defineProperty(exports, "PrimaryProcessStrategy", { enumerable: true, get
82
83
  Object.defineProperty(exports, "ProgressStrategyRegistry", { enumerable: true, get: function () { return progress_summary_1.ProgressStrategyRegistry; } });
83
84
  Object.defineProperty(exports, "CustomProgressStrategy", { enumerable: true, get: function () { return progress_summary_1.CustomProgressStrategy; } });
84
85
  Object.defineProperty(exports, "DefaultProgressStrategy", { enumerable: true, get: function () { return progress_summary_1.DefaultProgressStrategy; } });
86
+ tslib_1.__exportStar(require("./feature-status"), exports);
87
+ tslib_1.__exportStar(require("./region-endpoints"), exports);
88
+ var region_refresh_1 = require("./region-refresh");
89
+ Object.defineProperty(exports, "refreshRegionEndpoints", { enumerable: true, get: function () { return region_refresh_1.refreshRegionEndpoints; } });
@@ -12,6 +12,12 @@ export default class Logger {
12
12
  private isSensitiveKey;
13
13
  private redactObject;
14
14
  private redact;
15
+ /**
16
+ * Stringifies a log entry without throwing on circular references.
17
+ * `redact()` can fall back to the original (still-circular) object when
18
+ * cloning fails, so this must never assume the input is cycle-free.
19
+ */
20
+ private safeStringify;
15
21
  private shouldLog;
16
22
  error(message: string, meta?: any): void;
17
23
  warn(message: string, meta?: any): void;
@@ -52,7 +52,7 @@ class Logger {
52
52
  new winston.transports.File(Object.assign(Object.assign({}, this.loggerOptions), { filename: `${filePath}/${level}.log`, format: winston.format.combine(winston.format.timestamp(), winston.format.printf((info) => {
53
53
  // Apply minimal redaction for files (debugging info preserved)
54
54
  const redactedInfo = this.redact(info, false);
55
- return JSON.stringify(redactedInfo);
55
+ return this.safeStringify(redactedInfo);
56
56
  })) })),
57
57
  ];
58
58
  // Determine console logging based on configuration
@@ -70,7 +70,11 @@ class Logger {
70
70
  showConsoleLogs = true;
71
71
  }
72
72
  }
73
- if (showConsoleLogs) {
73
+ // Errors and warnings must always reach the console, even when progress bars
74
+ // suppress info/success/debug output — otherwise failures (e.g. an invalid
75
+ // stack API key or a taxonomy error) are silently swallowed in progress mode.
76
+ const isErrorOrWarn = level === 'error' || level === 'warn';
77
+ if (showConsoleLogs || isErrorOrWarn) {
74
78
  transports.push(new winston.transports.Console({
75
79
  format: winston.format.combine(winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.printf((info) => {
76
80
  // Apply full redaction for console (user-facing)
@@ -117,6 +121,28 @@ class Logger {
117
121
  return info;
118
122
  }
119
123
  }
124
+ /**
125
+ * Stringifies a log entry without throwing on circular references.
126
+ * `redact()` can fall back to the original (still-circular) object when
127
+ * cloning fails, so this must never assume the input is cycle-free.
128
+ */
129
+ safeStringify(value) {
130
+ var _a;
131
+ const seen = new WeakSet();
132
+ try {
133
+ return JSON.stringify(value, (_key, val) => {
134
+ if (typeof val === 'object' && val !== null) {
135
+ if (seen.has(val))
136
+ return '[Circular]';
137
+ seen.add(val);
138
+ }
139
+ return val;
140
+ });
141
+ }
142
+ catch (_b) {
143
+ return JSON.stringify({ level: value === null || value === void 0 ? void 0 : value.level, message: (_a = value === null || value === void 0 ? void 0 : value.message) !== null && _a !== void 0 ? _a : '[Unserializable log entry]' });
144
+ }
145
+ }
120
146
  shouldLog(level, target) {
121
147
  // If console logging is disabled, don't log to console
122
148
  if (target === 'console' && this.config.consoleLoggingEnabled === false) {
@@ -163,9 +189,12 @@ class Logger {
163
189
  if (this.shouldLog('error', 'file')) {
164
190
  this.loggers.error.error(logPayload);
165
191
  }
166
- // For console, use debug level if hidden, otherwise error level
192
+ // For console, use debug level if hidden, otherwise error level. Each level's
193
+ // winston logger instance bundles both the file and console transports, so only
194
+ // write again here when the target differs from the one already written above —
195
+ // otherwise this would duplicate both the console line and the file entry.
167
196
  const consoleLevel = params.hidden ? 'debug' : 'error';
168
- if (this.shouldLog(consoleLevel, 'console')) {
197
+ if (consoleLevel !== 'error' && this.shouldLog(consoleLevel, 'console')) {
169
198
  this.loggers[consoleLevel].error(logPayload);
170
199
  }
171
200
  }
@@ -2,9 +2,6 @@
2
2
  * Message handler
3
3
  */
4
4
  declare class Messages {
5
- private messages;
6
- messageFilePath: any;
7
- constructor();
8
5
  init(context: any): void;
9
6
  parse(messageKey: string, ...substitutions: Array<any>): string;
10
7
  }
@@ -3,32 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const fs_1 = tslib_1.__importDefault(require("fs"));
5
5
  const cli_error_1 = tslib_1.__importDefault(require("./cli-error"));
6
+ const STORE_KEY = '__cs_messages__';
6
7
  /**
7
8
  * Message handler
8
9
  */
9
10
  class Messages {
10
- constructor() {
11
- this.messages = {};
12
- }
13
11
  init(context) {
14
12
  if (!context.messageFilePath) {
15
13
  return;
16
14
  }
17
15
  try {
18
- this.messages = JSON.parse(fs_1.default.readFileSync(context.messageFilePath, 'utf-8'));
16
+ const loaded = JSON.parse(fs_1.default.readFileSync(context.messageFilePath, 'utf-8'));
17
+ globalThis[STORE_KEY] = Object.assign(Object.assign({}, globalThis[STORE_KEY]), loaded);
19
18
  }
20
19
  catch (error) {
21
- // create empty messages object if message file is not exist
22
- if (error.code === 'ENOENT') {
23
- this.messages = {};
24
- }
25
- else {
20
+ if (error.code !== 'ENOENT') {
26
21
  throw new cli_error_1.default(`Error: ${error.message}`);
27
22
  }
28
23
  }
29
24
  }
30
25
  parse(messageKey, ...substitutions) {
31
- const msg = this.messages[messageKey];
26
+ const store = globalThis[STORE_KEY] || {};
27
+ const msg = store[messageKey];
32
28
  if (!msg) {
33
29
  return messageKey;
34
30
  }
@@ -353,14 +353,19 @@ class CLIProgressManager {
353
353
  }
354
354
  if (!this.showConsoleLogs && process.progressBar) {
355
355
  const totalProcessed = process.current;
356
- const percentage = Math.round((totalProcessed / process.total) * 100);
356
+ // Reconcile the bar's total with the number of items actually processed so the
357
+ // rendered "value/total" matches the "Complete (success/processed)" summary. A
358
+ // process registered with an estimated total larger than the items actually ticked
359
+ // would otherwise render mismatched counts, e.g. "37/37 | Complete (27/27)".
360
+ process.progressBar.setTotal(totalProcessed);
361
+ const percentage = totalProcessed > 0 ? 100 : 0;
357
362
  const formattedPercentage = this.formatPercentage(percentage);
358
363
  const statusText = success
359
- ? (0, chalk_1.getChalk)().green(`✓ Complete (${process.successCount}/${process.current})`)
360
- : (0, chalk_1.getChalk)().red(`✗ Failed (${process.successCount}/${process.current})`);
364
+ ? (0, chalk_1.getChalk)().green(`✓ Complete (${process.successCount}/${totalProcessed})`)
365
+ : (0, chalk_1.getChalk)().red(`✗ Failed (${process.successCount}/${totalProcessed})`);
361
366
  const displayName = this.formatProcessName(processName);
362
367
  const indentedLabel = ` ├─ ${displayName}`.padEnd(25);
363
- process.progressBar.update(process.total, {
368
+ process.progressBar.update(totalProcessed, {
364
369
  label: success ? (0, chalk_1.getChalk)().green(indentedLabel) : (0, chalk_1.getChalk)().red(indentedLabel),
365
370
  status: statusText,
366
371
  percentage: formattedPercentage,
@@ -0,0 +1,26 @@
1
+ export interface RegionEndpoints {
2
+ [key: string]: string;
3
+ }
4
+ export interface CanonicalRegion {
5
+ name: string;
6
+ cma: string;
7
+ cda: string;
8
+ uiHost: string;
9
+ developerHubUrl: string;
10
+ launchHubUrl: string;
11
+ personalizeUrl: string;
12
+ composableStudioUrl: string;
13
+ csAssetsUrl?: string;
14
+ endpoints: RegionEndpoints;
15
+ }
16
+ /**
17
+ * Resolve the canonical endpoint set for a region name/alias via @contentstack/utils.
18
+ * Returns null when the name isn't a recognized region (e.g. a custom region name).
19
+ */
20
+ export declare function resolveCanonicalEndpoints(name: string): RegionEndpoints | null;
21
+ /**
22
+ * Build a region object from a raw endpoints map, keeping the existing named
23
+ * fields (cma/cda/uiHost/...) plus a full raw `endpoints` passthrough so any
24
+ * future endpoint Contentstack adds is available without further code changes.
25
+ */
26
+ export declare function buildRegionFromEndpoints(name: string, endpoints: RegionEndpoints): CanonicalRegion;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveCanonicalEndpoints = resolveCanonicalEndpoints;
4
+ exports.buildRegionFromEndpoints = buildRegionFromEndpoints;
5
+ const utils_1 = require("@contentstack/utils");
6
+ /**
7
+ * Resolve the canonical endpoint set for a region name/alias via @contentstack/utils.
8
+ * Returns null when the name isn't a recognized region (e.g. a custom region name).
9
+ */
10
+ function resolveCanonicalEndpoints(name) {
11
+ try {
12
+ const endpoints = (0, utils_1.getContentstackEndpoint)(name);
13
+ if (!endpoints || typeof endpoints === 'string')
14
+ return null;
15
+ return endpoints;
16
+ }
17
+ catch (_a) {
18
+ return null;
19
+ }
20
+ }
21
+ /**
22
+ * Build a region object from a raw endpoints map, keeping the existing named
23
+ * fields (cma/cda/uiHost/...) plus a full raw `endpoints` passthrough so any
24
+ * future endpoint Contentstack adds is available without further code changes.
25
+ */
26
+ function buildRegionFromEndpoints(name, endpoints) {
27
+ return {
28
+ name,
29
+ cma: endpoints.contentManagement,
30
+ cda: endpoints.contentDelivery,
31
+ uiHost: endpoints.application,
32
+ developerHubUrl: endpoints.developerHub,
33
+ launchHubUrl: endpoints.launch,
34
+ personalizeUrl: endpoints.personalizeManagement,
35
+ composableStudioUrl: endpoints.composableStudio,
36
+ csAssetsUrl: endpoints.assetManagement,
37
+ endpoints,
38
+ };
39
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Self-heals the persisted region config on every CLI invocation so that
3
+ * customers who set their region on an older CLI version (before a field
4
+ * like authUrl existed) get it backfilled without re-running
5
+ * `csdx config:set:region`. Only named/built-in regions are touched — a
6
+ * custom region's name won't resolve via resolveCanonicalEndpoints, so it's
7
+ * left untouched. Never throws.
8
+ */
9
+ export declare function refreshRegionEndpoints(): void;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.refreshRegionEndpoints = refreshRegionEndpoints;
4
+ const tslib_1 = require("tslib");
5
+ const config_handler_1 = tslib_1.__importDefault(require("./config-handler"));
6
+ const region_endpoints_1 = require("./region-endpoints");
7
+ /**
8
+ * Self-heals the persisted region config on every CLI invocation so that
9
+ * customers who set their region on an older CLI version (before a field
10
+ * like authUrl existed) get it backfilled without re-running
11
+ * `csdx config:set:region`. Only named/built-in regions are touched — a
12
+ * custom region's name won't resolve via resolveCanonicalEndpoints, so it's
13
+ * left untouched. Never throws.
14
+ */
15
+ function refreshRegionEndpoints() {
16
+ const stored = config_handler_1.default.get('region');
17
+ if (!(stored === null || stored === void 0 ? void 0 : stored.name))
18
+ return;
19
+ const endpoints = (0, region_endpoints_1.resolveCanonicalEndpoints)(stored.name);
20
+ if (!endpoints)
21
+ return;
22
+ const merged = Object.assign(Object.assign({}, stored), (0, region_endpoints_1.buildRegionFromEndpoints)(stored.name, endpoints));
23
+ if (JSON.stringify(merged) !== JSON.stringify(stored)) {
24
+ config_handler_1.default.set('region', merged);
25
+ }
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentstack/cli-utilities",
3
- "version": "2.0.0-beta.9",
3
+ "version": "2.0.1",
4
4
  "description": "Utilities for contentstack projects",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -14,7 +14,8 @@
14
14
  },
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": ""
17
+ "url": "git+https://github.com/contentstack/cli.git",
18
+ "directory": "packages/contentstack-utilities"
18
19
  },
19
20
  "keywords": [
20
21
  "contentstack",
@@ -27,10 +28,11 @@
27
28
  "author": "contentstack",
28
29
  "license": "MIT",
29
30
  "dependencies": {
30
- "@contentstack/management": "~1.30.3",
31
- "@contentstack/marketplace-sdk": "^1.5.2",
32
- "@oclif/core": "^4.11.4",
33
- "axios": "^1.16.1",
31
+ "@contentstack/management": "~1.31.1",
32
+ "@contentstack/marketplace-sdk": "^1.5.4",
33
+ "@contentstack/utils": "~1.9.1",
34
+ "@oclif/core": "^4.11.14",
35
+ "axios": "^1.20.0",
34
36
  "chalk": "^5.6.2",
35
37
  "cli-cursor": "^3.1.0",
36
38
  "cli-progress": "^3.12.0",
@@ -41,39 +43,39 @@
41
43
  "inquirer": "12.11.1",
42
44
  "inquirer-search-checkbox": "^1.0.0",
43
45
  "inquirer-search-list": "^1.2.6",
44
- "js-yaml": "^4.1.1",
46
+ "js-yaml": "^4.3.2",
45
47
  "klona": "^2.0.6",
46
48
  "lodash": "^4.18.1",
47
49
  "mkdirp": "^1.0.4",
48
50
  "open": "^8.4.2",
49
51
  "ora": "^5.4.1",
50
- "papaparse": "^5.5.3",
52
+ "papaparse": "^5.5.4",
51
53
  "recheck": "~4.5.0",
52
54
  "rxjs": "^6.6.7",
53
55
  "traverse": "^0.6.11",
54
56
  "tty-table": "^4.2.3",
55
57
  "unique-string": "^2.0.0",
56
58
  "short-uuid": "^6.0.3",
57
- "uuid": "^14.0.0",
59
+ "uuid": "^14.0.1",
58
60
  "winston": "^3.19.0",
59
61
  "xdg-basedir": "^4.0.0"
60
62
  },
61
63
  "overrides": {
62
64
  "@oclif/core": {
63
- "picomatch": "^4.0.4"
65
+ "picomatch": "^4.0.7"
64
66
  }
65
67
  },
66
68
  "devDependencies": {
67
69
  "@types/chai": "^4.3.20",
68
- "@types/inquirer": "^9.0.9",
70
+ "@types/inquirer": "^9.0.10",
69
71
  "@types/mkdirp": "^1.0.2",
70
72
  "@types/mocha": "^10.0.10",
71
73
  "@types/node": "^18.19.130",
72
- "@types/sinon": "^21.0.0",
74
+ "@types/sinon": "^21.0.1",
73
75
  "@types/traverse": "^0.6.37",
74
76
  "chai": "^4.5.0",
75
- "eslint": "^9.26.0",
76
- "eslint-config-oclif": "^6.0.62",
77
+ "eslint": "^10.6.0",
78
+ "eslint-config-oclif": "^6.0.175",
77
79
  "eslint-config-oclif-typescript": "^3.1.14",
78
80
  "mocha": "10.8.2",
79
81
  "nyc": "^15.1.0",
@@ -82,4 +84,4 @@
82
84
  "ts-node": "^10.9.2",
83
85
  "typescript": "^5.9.3"
84
86
  }
85
- }
87
+ }