diff --git a/.talismanrc b/.talismanrc index d7cb80977f..7603e78eb9 100644 --- a/.talismanrc +++ b/.talismanrc @@ -117,7 +117,7 @@ fileignoreconfig: - filename: packages/contentstack/package.json checksum: 9b0fdd100effcdbb5ee3809f7f102bfd11c88dd76e49db5103434f3aa29473dd - filename: pnpm-lock.yaml - checksum: d1ac3746440f92fdf23f07fcbe3266ee7ac5ad5ce1b7d16108b593b352e5e719 + checksum: 5f34e6fc746bf8832dd7767bab9d87f9940ff94f380fb702ebf45dcf7558f19c - filename: packages/contentstack-audit/src/audit-base-command.ts checksum: bd99d269c0b6694577f4751fa96b3d85856e41bbef624b4ec1196630d6c1d168 - filename: packages/contentstack-migrate-rte/test/commands/json-migration.test.js @@ -127,5 +127,11 @@ fileignoreconfig: - filename: packages/contentstack-bootstrap/test/bootstrap.test.js checksum: 5f0355a5048183d61b605cbc160e6727a9de32832d9159e903fee49f9ab751d5 - filename: package-lock.json - checksum: a42087c2e6c587487f58fd300f6899e70c5484fa1c0707e29cedfc674129c27b + checksum: 1aaeb0bd3d7e20e1932b2acd9577ffad15a5163da0797d14d289389c49bd6c0e + - filename: packages/contentstack-utilities/test/unit/logger.test.ts + checksum: c773181ea55c49d91363adacf0424b84c927e3cffd6bef1444ec2559ddf1b3b0 + - filename: packages/contentstack-utilities/src/logger/cliErrorHandler.ts + checksum: 023cf08f215cd0778599fb8478c94419373d4687f04421c4eb99d87de86a4a3e + - filename: packages/contentstack-utilities/src/logger/logger.ts + checksum: 09f3b73dd995bafc253265c676f06308513e6b1842d9bc01d39e6b6945a54c7d version: "1.0" diff --git a/package-lock.json b/package-lock.json index 9ff720afa1..77775f809c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27889,7 +27889,7 @@ "@contentstack/cli-config": "~1.12.1", "@contentstack/cli-launch": "^1.9.1", "@contentstack/cli-migration": "~1.7.2", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@contentstack/cli-variants": "~1.2.1", "@contentstack/management": "~1.21.3", "@oclif/core": "^4.3.0", @@ -27946,7 +27946,7 @@ "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@oclif/plugin-help": "^6.2.28", "@oclif/plugin-plugins": "^5.4.36", "chalk": "^4.1.2", @@ -28046,6 +28046,61 @@ "node": ">=14.0.0" } }, + "packages/contentstack-auth/node_modules/@contentstack/cli-utilities": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@contentstack/cli-utilities/-/cli-utilities-1.11.1.tgz", + "integrity": "sha512-d2i17OeHMc2WOkkwuzVNmplD0y/d1GV4v21PZre/A66xcB/COaBn8rvvsKjo18RIUl6vY72PsrzNq6xRc7AOfg==", + "license": "MIT", + "dependencies": { + "@contentstack/management": "~1.20.2", + "@contentstack/marketplace-sdk": "^1.2.5", + "@oclif/core": "^4.2.7", + "axios": "^1.8.2", + "chalk": "^4.1.2", + "cli-cursor": "^3.1.0", + "cli-progress": "^3.12.0", + "cli-table": "^0.3.11", + "conf": "^10.2.0", + "dotenv": "^16.4.7", + "figures": "^3.2.0", + "inquirer": "8.2.6", + "inquirer-search-checkbox": "^1.0.0", + "inquirer-search-list": "^1.2.6", + "js-yaml": "^4.1.0", + "klona": "^2.0.6", + "lodash": "^4.17.21", + "mkdirp": "^1.0.4", + "open": "^8.4.2", + "ora": "^5.4.1", + "papaparse": "^5.5.2", + "recheck": "~4.4.5", + "rxjs": "^6.6.7", + "traverse": "^0.6.11", + "tty-table": "^4.2.3", + "unique-string": "^2.0.0", + "uuid": "^9.0.1", + "winston": "^3.17.0", + "xdg-basedir": "^4.0.0" + } + }, + "packages/contentstack-auth/node_modules/@contentstack/management": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/@contentstack/management/-/management-1.20.3.tgz", + "integrity": "sha512-ddPLEYRCBfcezTx7X85oO63aTLn7ZglBXDhp7ofJdFefwF52LTWKni71Scxn5NX5CrniSNqNAquPKZBnfOVl0Q==", + "license": "MIT", + "dependencies": { + "assert": "^2.1.0", + "axios": "^1.8.4", + "buffer": "^6.0.3", + "form-data": "^4.0.2", + "lodash": "^4.17.21", + "qs": "^6.14.0", + "stream-browserify": "^3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "packages/contentstack-auth/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -28168,7 +28223,7 @@ "dependencies": { "@contentstack/cli-cm-seed": "~1.11.1", "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "inquirer": "8.2.6", "mkdirp": "^1.0.4", "tar": "^6.2.1 " @@ -28247,7 +28302,7 @@ "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@oclif/core": "^4.3.0", "chalk": "^4.1.2", "just-diff": "^6.0.2", @@ -28279,7 +28334,7 @@ "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "chalk": "^4.1.2", "dotenv": "^16.5.0", "inquirer": "8.2.6", @@ -28308,7 +28363,7 @@ "@contentstack/cli-cm-export": "~1.16.2", "@contentstack/cli-cm-import": "~1.22.1", "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "chalk": "^4.1.2", "inquirer": "8.2.6", "lodash": "^4.17.21", @@ -28337,7 +28392,7 @@ "version": "1.5.0", "license": "MIT", "dependencies": { - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "contentstack": "^3.25.3" }, "devDependencies": { @@ -28410,7 +28465,7 @@ "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "lodash": "^4.17.21" }, "devDependencies": { @@ -28742,7 +28797,7 @@ "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@contentstack/cli-variants": "~1.2.1", "@oclif/core": "^4.3.0", "async": "^3.2.6", @@ -28785,7 +28840,7 @@ "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "fast-csv": "^4.3.6", "inquirer": "8.2.6", "inquirer-checkbox-plus-prompt": "1.4.2", @@ -29119,7 +29174,7 @@ "dependencies": { "@contentstack/cli-audit": "~1.12.2", "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@contentstack/cli-variants": "~1.2.1", "@contentstack/management": "~1.21.3", "@oclif/core": "^4.3.0", @@ -29165,7 +29220,7 @@ "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@oclif/core": "^4.3.0", "big-json": "^3.2.0", "chalk": "^4.1.2", @@ -29205,7 +29260,7 @@ "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@contentstack/json-rte-serializer": "~2.1.0", "chalk": "^4.1.2", "collapse-whitespace": "^1.1.7", @@ -29235,7 +29290,7 @@ "license": "MIT", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "async": "^3.2.6", "callsites": "^3.1.0", "cardinal": "^2.1.1", @@ -29265,7 +29320,7 @@ "dependencies": { "@contentstack/cli-cm-import": "~1.22.1", "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@contentstack/management": "~1.21.3", "inquirer": "8.2.6", "mkdirp": "^1.0.4", @@ -29342,7 +29397,7 @@ }, "packages/contentstack-utilities": { "name": "@contentstack/cli-utilities", - "version": "1.11.2", + "version": "1.12.0", "license": "MIT", "dependencies": { "@contentstack/management": "~1.21.3", @@ -29422,6 +29477,61 @@ "typescript": "^5.8.3" } }, + "packages/contentstack-variants/node_modules/@contentstack/cli-utilities": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@contentstack/cli-utilities/-/cli-utilities-1.11.1.tgz", + "integrity": "sha512-d2i17OeHMc2WOkkwuzVNmplD0y/d1GV4v21PZre/A66xcB/COaBn8rvvsKjo18RIUl6vY72PsrzNq6xRc7AOfg==", + "license": "MIT", + "dependencies": { + "@contentstack/management": "~1.20.2", + "@contentstack/marketplace-sdk": "^1.2.5", + "@oclif/core": "^4.2.7", + "axios": "^1.8.2", + "chalk": "^4.1.2", + "cli-cursor": "^3.1.0", + "cli-progress": "^3.12.0", + "cli-table": "^0.3.11", + "conf": "^10.2.0", + "dotenv": "^16.4.7", + "figures": "^3.2.0", + "inquirer": "8.2.6", + "inquirer-search-checkbox": "^1.0.0", + "inquirer-search-list": "^1.2.6", + "js-yaml": "^4.1.0", + "klona": "^2.0.6", + "lodash": "^4.17.21", + "mkdirp": "^1.0.4", + "open": "^8.4.2", + "ora": "^5.4.1", + "papaparse": "^5.5.2", + "recheck": "~4.4.5", + "rxjs": "^6.6.7", + "traverse": "^0.6.11", + "tty-table": "^4.2.3", + "unique-string": "^2.0.0", + "uuid": "^9.0.1", + "winston": "^3.17.0", + "xdg-basedir": "^4.0.0" + } + }, + "packages/contentstack-variants/node_modules/@contentstack/management": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/@contentstack/management/-/management-1.20.3.tgz", + "integrity": "sha512-ddPLEYRCBfcezTx7X85oO63aTLn7ZglBXDhp7ofJdFefwF52LTWKni71Scxn5NX5CrniSNqNAquPKZBnfOVl0Q==", + "license": "MIT", + "dependencies": { + "assert": "^2.1.0", + "axios": "^1.8.4", + "buffer": "^6.0.3", + "form-data": "^4.0.2", + "lodash": "^4.17.21", + "qs": "^6.14.0", + "stream-browserify": "^3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "packages/contentstack-variants/node_modules/@types/node": { "version": "20.17.55", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.55.tgz", diff --git a/packages/contentstack-audit/package.json b/packages/contentstack-audit/package.json index 07d138566e..21c7ddc03c 100644 --- a/packages/contentstack-audit/package.json +++ b/packages/contentstack-audit/package.json @@ -20,7 +20,7 @@ "dependencies": { "@contentstack/cli-command": "~1.5.0", "@oclif/plugin-help": "^6.2.28", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@oclif/plugin-plugins": "^5.4.36", "chalk": "^4.1.2", "fast-csv": "^4.3.6", diff --git a/packages/contentstack-bootstrap/package.json b/packages/contentstack-bootstrap/package.json index df284be7ad..bcb019b9b3 100644 --- a/packages/contentstack-bootstrap/package.json +++ b/packages/contentstack-bootstrap/package.json @@ -19,7 +19,7 @@ "dependencies": { "@contentstack/cli-cm-seed": "~1.11.1", "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "inquirer": "8.2.6", "mkdirp": "^1.0.4", "tar": "^6.2.1 " diff --git a/packages/contentstack-branches/package.json b/packages/contentstack-branches/package.json index 705fe093d7..dae02d9189 100644 --- a/packages/contentstack-branches/package.json +++ b/packages/contentstack-branches/package.json @@ -7,7 +7,7 @@ "dependencies": { "@contentstack/cli-command": "~1.5.0", "@oclif/core": "^4.3.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "chalk": "^4.1.2", "just-diff": "^6.0.2", "lodash": "^4.17.21" diff --git a/packages/contentstack-bulk-publish/package.json b/packages/contentstack-bulk-publish/package.json index 27f17299a5..363346025d 100644 --- a/packages/contentstack-bulk-publish/package.json +++ b/packages/contentstack-bulk-publish/package.json @@ -6,7 +6,7 @@ "bugs": "https://github.com/contentstack/cli/issues", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "chalk": "^4.1.2", "dotenv": "^16.5.0", "inquirer": "8.2.6", diff --git a/packages/contentstack-clone/package.json b/packages/contentstack-clone/package.json index 792e25c26d..59615b0dbf 100644 --- a/packages/contentstack-clone/package.json +++ b/packages/contentstack-clone/package.json @@ -9,7 +9,7 @@ "@contentstack/cli-cm-export": "~1.16.2", "@contentstack/cli-cm-import": "~1.22.1", "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "chalk": "^4.1.2", "inquirer": "8.2.6", "lodash": "^4.17.21", diff --git a/packages/contentstack-command/package.json b/packages/contentstack-command/package.json index ac8e596c4e..e6f7cc1f96 100644 --- a/packages/contentstack-command/package.json +++ b/packages/contentstack-command/package.json @@ -17,7 +17,7 @@ "format": "eslint src/**/*.ts --fix" }, "dependencies": { - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "contentstack": "^3.25.3" }, "devDependencies": { diff --git a/packages/contentstack-config/package.json b/packages/contentstack-config/package.json index 75a5da1709..10253c327f 100644 --- a/packages/contentstack-config/package.json +++ b/packages/contentstack-config/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/packages/contentstack-export-to-csv/package.json b/packages/contentstack-export-to-csv/package.json index bd71212164..bb848c137d 100644 --- a/packages/contentstack-export-to-csv/package.json +++ b/packages/contentstack-export-to-csv/package.json @@ -6,7 +6,7 @@ "bugs": "https://github.com/contentstack/cli/issues", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "fast-csv": "^4.3.6", "inquirer": "8.2.6", "inquirer-checkbox-plus-prompt": "1.4.2", diff --git a/packages/contentstack-export/package.json b/packages/contentstack-export/package.json index c80d13efcd..e08c93ebc4 100644 --- a/packages/contentstack-export/package.json +++ b/packages/contentstack-export/package.json @@ -8,7 +8,7 @@ "@contentstack/cli-command": "~1.5.0", "@contentstack/cli-variants": "~1.2.1", "@oclif/core": "^4.3.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "async": "^3.2.6", "big-json": "^3.2.0", "bluebird": "^3.7.2", diff --git a/packages/contentstack-import-setup/package.json b/packages/contentstack-import-setup/package.json index ced9390f03..43b13d35ba 100644 --- a/packages/contentstack-import-setup/package.json +++ b/packages/contentstack-import-setup/package.json @@ -7,7 +7,7 @@ "dependencies": { "@contentstack/cli-command": "~1.5.0", "@oclif/core": "^4.3.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "big-json": "^3.2.0", "chalk": "^4.1.2", "fs-extra": "^11.3.0", diff --git a/packages/contentstack-import/package.json b/packages/contentstack-import/package.json index 66e46ccf17..415b045bc9 100644 --- a/packages/contentstack-import/package.json +++ b/packages/contentstack-import/package.json @@ -7,7 +7,7 @@ "dependencies": { "@contentstack/cli-audit": "~1.12.2", "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@contentstack/management": "~1.21.3", "@contentstack/cli-variants": "~1.2.1", "@oclif/core": "^4.3.0", diff --git a/packages/contentstack-migrate-rte/package.json b/packages/contentstack-migrate-rte/package.json index 70e59399ec..29c259dd7c 100644 --- a/packages/contentstack-migrate-rte/package.json +++ b/packages/contentstack-migrate-rte/package.json @@ -6,7 +6,7 @@ "bugs": "https://github.com/contentstack/cli/issues", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@contentstack/json-rte-serializer": "~2.1.0", "collapse-whitespace": "^1.1.7", "chalk": "^4.1.2", diff --git a/packages/contentstack-migration/package.json b/packages/contentstack-migration/package.json index 23f97351f9..fc5084a81d 100644 --- a/packages/contentstack-migration/package.json +++ b/packages/contentstack-migration/package.json @@ -5,7 +5,7 @@ "bugs": "https://github.com/contentstack/cli/issues", "dependencies": { "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "async": "^3.2.6", "callsites": "^3.1.0", "cardinal": "^2.1.1", diff --git a/packages/contentstack-seed/package.json b/packages/contentstack-seed/package.json index 0c5517e413..b60befdfc2 100644 --- a/packages/contentstack-seed/package.json +++ b/packages/contentstack-seed/package.json @@ -7,7 +7,7 @@ "dependencies": { "@contentstack/cli-cm-import": "~1.22.1", "@contentstack/cli-command": "~1.5.0", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@contentstack/management": "~1.21.3", "inquirer": "8.2.6", "mkdirp": "^1.0.4", diff --git a/packages/contentstack-utilities/package.json b/packages/contentstack-utilities/package.json index 1935c31c4d..041074112d 100644 --- a/packages/contentstack-utilities/package.json +++ b/packages/contentstack-utilities/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/cli-utilities", - "version": "1.11.2", + "version": "1.12.0", "description": "Utilities for contentstack projects", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/contentstack-utilities/src/constants/errorTypes.ts b/packages/contentstack-utilities/src/constants/errorTypes.ts new file mode 100644 index 0000000000..cbecaf9437 --- /dev/null +++ b/packages/contentstack-utilities/src/constants/errorTypes.ts @@ -0,0 +1,10 @@ +export const ERROR_TYPES = { + NETWORK: 'NETWORK_ERROR', + DATABASE: 'DATABASE_ERROR', + APPLICATION: 'APPLICATION_ERROR', + UNKNOWN: 'UNKNOWN_ERROR', + NORMALIZATION: 'NORMALIZATION_ERROR', + API_RESPONSE: 'API_RESPONSE_DATA', + API_ERROR: 'API_ERROR', + SERVER_ERROR: 'SERVER_ERROR', +} as const; \ No newline at end of file diff --git a/packages/contentstack-utilities/src/constants/logging.ts b/packages/contentstack-utilities/src/constants/logging.ts new file mode 100644 index 0000000000..e32d6a0699 --- /dev/null +++ b/packages/contentstack-utilities/src/constants/logging.ts @@ -0,0 +1,17 @@ +export const logLevels = { + error: 0, + warn: 1, + info: 2, + success: 2, // Maps to info level but with different type + debug: 3, + verbose: 4 +} as const; + +// 2. Create color mappings (for console only) +export const levelColors = { + error: 'red', + warn: 'yellow', + success: 'green', // Custom color for success + info: 'blue', + debug: 'white' +}; \ No newline at end of file diff --git a/packages/contentstack-utilities/src/interfaces/index.ts b/packages/contentstack-utilities/src/interfaces/index.ts index 02b95976bd..6fa5dfcf25 100644 --- a/packages/contentstack-utilities/src/interfaces/index.ts +++ b/packages/contentstack-utilities/src/interfaces/index.ts @@ -1,3 +1,5 @@ +import { logLevels } from "../constants/logging"; + export interface IPromptOptions { prompt?: string; type?: 'normal' | 'mask' | 'hide' | 'single'; @@ -72,3 +74,44 @@ export interface Locale { } export interface CliUXPromptOptions extends IPromptOptions {} + +export interface LoggerConfig { + basePath: string; // Base path for log storage + processName?: string; // Optional name of the plugin/process + consoleLoggingEnabled?: boolean; // Should logs be printed to console + consoleLogLevel?: LogType; // Console log level (info, debug, etc.) + logLevel?: LogType; // File log level +} + +export interface PrintOptions { + bold?: boolean; + color?: string; +} + +export type LogType = 'info' | 'warn' | 'error' | 'debug'; +export type LogsType = LogType | PrintOptions | undefined; +export type MessageType = string | Error | Record | Record[]; + +export type LogLevel = keyof typeof logLevels; + +export type ClassifiedError = { + type: string; + message: string; + error: Record; + debug?: Record; + meta?: Record; + context?: string; + hidden?: boolean; +}; + +export type ErrorContext = { + operation?: string; + component?: string; + userId?: string; + requestId?: string; + email?: string; + sessionId?: string; + orgId?: string; + apiKey?: string; +}; + diff --git a/packages/contentstack-utilities/src/logger/cliErrorHandler.ts b/packages/contentstack-utilities/src/logger/cliErrorHandler.ts new file mode 100644 index 0000000000..6efd5134fa --- /dev/null +++ b/packages/contentstack-utilities/src/logger/cliErrorHandler.ts @@ -0,0 +1,235 @@ +import { AxiosError } from 'axios'; +import { ClassifiedError, ErrorContext } from '../interfaces'; +import { formatError } from '../helpers'; +import { ERROR_TYPES } from '../constants/errorTypes'; + +/** + * Handles errors in a CLI application by classifying, normalizing, and extracting + * relevant information for debugging and logging purposes. + * + * This class provides methods to: + * - Normalize unknown error types into standard `Error` objects. + * - Classify errors into predefined categories such as API errors, network errors, + * server errors, and more. + * - Extract detailed error payloads for logging, including HTTP request and response + * details when applicable. + * - Identify sensitive information in error messages to prevent accidental exposure. + * - Generate debug payloads for enhanced troubleshooting when debugging is enabled. + * + * @remarks + * This class is designed to handle a wide range of error types, including generic + * JavaScript errors, API errors, and custom error objects. It also supports + * optional debugging and context metadata for enhanced error reporting. + * + * @example + * ```typescript + * const errorHandler = new CLIErrorHandler(true); + * + * try { + * // Some operation that may throw an error + * } catch (error) { + * const classifiedError = errorHandler.classifyError(error, { + * operation: 'fetchData', + * component: 'DataService', + * }); + * console.error(classifiedError); + * } + * ``` + * + * @public + */ +export default class CLIErrorHandler { + private isDebug: boolean; + + constructor(isDebug = false) { + this.isDebug = isDebug; + } + + /** + * Classifies an error into a structured format for better handling and debugging. + * + * @param error - The error object to classify. Can be of any type. + * @param context - Optional additional context about the error, typically used to provide + * more information about where or why the error occurred. + * + * @returns A `ClassifiedError` object containing details about the error, including its type, + * message, payload, context, metadata, and whether it contains sensitive information. + * If the error is an API error or debugging is enabled, additional debug information + * is included. + * + * @throws This method handles its own errors and will return a `ClassifiedError` with type + * `ERROR_TYPES.NORMALIZATION` if it fails to normalize or classify the input error. + */ + classifyError(error: unknown, context?: ErrorContext): ClassifiedError { + try { + const normalized = this.normalizeToError(error); + const isApi = this.isApiError(normalized); + const type = this.determineErrorType(normalized); + const hidden = this.containsSensitiveInfo(normalized); + + const result: ClassifiedError = { + type, + message: normalized.message || 'Unhandled error', + error: this.extractErrorPayload(normalized), + context: context ? JSON.stringify(context) : undefined, + meta: this.extractMeta(context), + hidden, + }; + + if (isApi || this.isDebug) { + result.debug = this.extractDebugPayload(normalized, context); + } + + return result; + } catch (e) { + return { + type: ERROR_TYPES.NORMALIZATION, + message: 'Failed to normalize or classify error', + error: { message: String(e) }, + context: context ? JSON.stringify(context) : undefined, + meta: this.extractMeta(context), + hidden: false, + }; + } + } + + private normalizeToError(error: unknown): Error { + if (!error) return new Error('Unknown error occurred'); + if (error instanceof Error) return error; + if (typeof error === 'string') return new Error(error); + + if (typeof error === 'object') { + try { + const msg = (error as any).message; + const err = new Error(msg || 'Unknown error'); + Object.assign(err, error); + return err; + } catch { + return new Error(JSON.stringify(error)); + } + } + + return new Error(String(error)); + } + + private isApiError(error: Error): boolean { + return ( + (error as AxiosError).isAxiosError || + typeof (error as any).status === 'number' || + typeof (error as any).statusText === 'string' || + (error as any).request !== undefined + ); + } + + private determineErrorType(error: Error & Record): string { + const status = error.status || error.response?.status; + + //Ignore 4XX errors + if (status >= 400 && status < 500) { + return ERROR_TYPES.API_ERROR; + } + + //Server-side HTTP errors + if (status >= 500) { + return ERROR_TYPES.SERVER_ERROR; + } + + //Network-related error + if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND' || error.code === 'ETIMEDOUT') { + return ERROR_TYPES.NETWORK; + } + + //Database error + if (error.name === 'DatabaseError') { + return ERROR_TYPES.DATABASE; + } + + //Axios errors without 4XX + if ((error as AxiosError).isAxiosError) { + return ERROR_TYPES.NETWORK; + } + + //Default + return ERROR_TYPES.APPLICATION; + } + + private extractErrorPayload(error: Error & Record): Record { + const code = error.code || error.errorCode; + const status = error.status || error.response?.status; + const statusText = error.statusText || error.response?.statusText; + const method = error.request?.method || error.config?.method || 'UNKNOWN'; + const url = error.request?.url || error.config?.url; + const endpoint = url ? new URL(url, 'http://dummy').pathname : 'UNKNOWN'; + + const payload: Record = { + name: error.name, + message: formatError(error), + code, + status, + statusText, + method, + endpoint, + }; + + if (this.isDebug) { + payload.stack = error.stack; + } + + return payload; + } + + private extractDebugPayload(error: Error & Record, context?: ErrorContext): Record { + const method = error.request?.method || error.config?.method; + const url = error.request?.url || error.config?.url; + const status = error.status || error.response?.status; + const statusText = error.statusText || error.response?.statusText; + const data = error.data || error.response?.data || error.errors || error.error; + + return { + command: context?.operation, + module: context?.component, + request: { + method, + url, + headers: error.request?.headers, + data: error.request?.data, + }, + response: { + status, + statusText, + data, + }, + }; + } + + private extractMeta(context?: ErrorContext): Record { + return { + email: context?.email, + sessionId: context?.sessionId, + userId: context?.userId, + apiKey: context?.apiKey, + orgId: context?.orgId, + }; + } + + private containsSensitiveInfo(error: Error): boolean { + try { + const content = `${error.message} ${error.stack || ''}`.toLowerCase(); + return [ + 'password', + 'token', + 'secret', + 'credentials', + 'api_key', + 'api-key', + 'authorization', + 'sessionid', + 'email', + ].some((term) => content.includes(term)); + } catch { + return false; + } + } +} + +export { CLIErrorHandler }; diff --git a/packages/contentstack-utilities/src/logger/log.ts b/packages/contentstack-utilities/src/logger/log.ts new file mode 100644 index 0000000000..878629ce34 --- /dev/null +++ b/packages/contentstack-utilities/src/logger/log.ts @@ -0,0 +1,54 @@ +import * as path from 'path'; +import { default as Logger } from './logger'; +import { CLIErrorHandler } from './cliErrorHandler'; +import { ErrorContext } from '../interfaces'; + +const v2Logger = new Logger({ basePath: process.env.CS_CLI_LOG_PATH || path.join(process.cwd(), 'logs') }); +const cliErrorHandler = new CLIErrorHandler(true); // Enable debug mode for error classification + +/** + * Handles and logs an error by classifying it and logging the relevant details. + * + * This function uses the `cliErrorHandler` to classify the provided error and logs + * the error details using `v2Logger`. If debug information is available, it logs + * additional debug details, including a stack trace if not already present. + * + * @param error - The error to be handled and logged. Can be of any type. + * @param context - Optional context information to assist in error classification + * and logging. + * + * @remarks + * - The error is always logged with its type, message, and other metadata. + * - If debug information is available, it is logged separately with a more specific + * debug type and additional details. + */ +function handleAndLogError(error: unknown, context?: ErrorContext): void { + const classified = cliErrorHandler.classifyError(error, context); + + // Always log the error + v2Logger.logError({ + type: classified.type, + message: classified.message, + error: classified.error, + context: classified.context, + hidden: classified.hidden, + meta: classified.meta, + }); + + // Log debug information if available + if (classified.debug) { + v2Logger.logDebug({ + type: `${classified.type}_DEBUG`, // More specific debug type + message: `${classified.message} [DEBUG]`, + debug: { + ...classified.debug, + // Ensure stack trace is included if not already there + stackTrace: classified.debug.stackTrace || classified.error.stack, + }, + context: classified.context, + meta: classified.meta, + }); + } +} + +export { v2Logger, cliErrorHandler, handleAndLogError }; diff --git a/packages/contentstack-utilities/src/logger/logger.ts b/packages/contentstack-utilities/src/logger/logger.ts new file mode 100644 index 0000000000..c63c16352b --- /dev/null +++ b/packages/contentstack-utilities/src/logger/logger.ts @@ -0,0 +1,280 @@ +import traverse from 'traverse'; +import { klona } from 'klona/full'; +import { normalize } from 'path'; +import * as winston from 'winston'; +import { LogEntry } from 'winston'; +import { logLevels } from '../constants/logging'; +import { LoggerConfig, LogLevel, LogType } from '../interfaces/index'; + +export default class Logger { + private loggers: Record; + private config: LoggerConfig; + + private sensitiveKeys = [ + /authtoken/i, + /^email$/i, + /^password$/i, + /secret/i, + /token/i, + /api[-._]?key/i, + /management[-._]?token/i, + /sessionid/i, + /orgid/i, + ]; + + constructor(config: LoggerConfig) { + this.config = config; + this.loggers = { + error: this.getLoggerInstance('error'), + warn: this.getLoggerInstance('warn'), + info: this.getLoggerInstance('info'), + debug: this.getLoggerInstance('debug'), + success: this.getLoggerInstance('info'), // Map success to info + }; + } + + getLoggerInstance(level: 'error' | 'info' | 'warn' | 'debug' | 'hidden' = 'info'): winston.Logger { + const filePath = normalize(process.env.CS_CLI_LOG_PATH || this.config.basePath).replace(/^(\.\.(\/|\\|$))+/, ''); + if (level === 'hidden') { + return this.createLogger('error', filePath); + } + return this.createLogger(level, filePath); + } + + private get loggerOptions(): winston.transports.FileTransportOptions { + return { + filename: '', + maxFiles: 20, + tailable: true, + maxsize: 1000000, + }; + } + + private createLogger(level: LogLevel, filePath: string): winston.Logger { + return winston.createLogger({ + levels: logLevels, + level: level, + transports: [ + new winston.transports.File({ + ...this.loggerOptions, + filename: `${filePath}/${level}.log`, + format: winston.format.combine( + winston.format.timestamp(), + winston.format.json() + ), + }), + new winston.transports.Console({ + format: winston.format.combine( + winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + winston.format.printf((info) => { + const colorizer = winston.format.colorize(); + const levelText = info.level.toUpperCase(); + const timestamp = info.timestamp; + const message = info.message; + const meta = info.meta; + + let fullLine = `[${timestamp}] ${levelText}: ${message}`; + if (meta && (info.level !== 'info' && info.level !== 'success')) { + const redactedMeta = this.isLogEntry(meta) ? JSON.stringify(this.redact(meta)) : JSON.stringify(this.redact(meta)); + fullLine += ` - ${redactedMeta}`; + } + + return colorizer.colorize(info.level, fullLine); + }), + ), + }), + ], + }); + } + + private isSensitiveKey(keyStr: string): boolean { + return keyStr && typeof keyStr === 'string' + ? this.sensitiveKeys.some((regex) => regex.test(keyStr)) + : false; + } + + private redactObject(obj: any): void { + const self = this; + traverse(obj).forEach(function redactor() { + if (this.key && self.isSensitiveKey(this.key)) { + this.update('[REDACTED]'); + } + }); + } + + private redact(info: any): any { + try { + const copy = klona(info); + this.redactObject(copy); + + const splat = copy[Symbol.for('splat')]; + if (splat) this.redactObject(splat); + + return copy; + } catch (error) { + return info; + } + } + + private isLogEntry(obj: any): obj is LogEntry { + return typeof obj === 'object' && 'level' in obj && 'message' in obj; + } + + private shouldLog(level: LogType, target: 'console' | 'file'): boolean { + const configLevel = target === 'console' ? this.config.consoleLogLevel : this.config.logLevel; + const minLevel = configLevel ? logLevels[configLevel] : 2; // default: info + const entryLevel = logLevels[level]; + return entryLevel <= minLevel; + } + + /* === Public Log Methods === */ + + public error(message: string, meta?: any): void { + if (this.shouldLog('error', 'console') || this.shouldLog('error', 'file')) { + this.loggers.error.error(message, meta); + } + } + + public warn(message: string, meta?: any): void { + if (this.shouldLog('warn', 'console') || this.shouldLog('warn', 'file')) { + this.loggers.warn.warn(message, meta); + } + } + + public info(message: string, meta?: any): void { + if (this.shouldLog('info', 'console') || this.shouldLog('info', 'file')) { + this.loggers.info.info(message, meta); + } + } + + public success(message: string, meta?: any): void { + if (this.shouldLog('info', 'console') || this.shouldLog('info', 'file')) { + this.loggers.success.info(message, { ...meta, type: 'success' }); + } + } + + public debug(message: string, meta?: any): void { + if (this.shouldLog('debug', 'console') || this.shouldLog('debug', 'file')) { + this.loggers.debug.debug(message, meta); + } + } + + /* === Structured Logging === */ + + public logError(params: { + type: string; + message: string; + error: any; + context?: string; + hidden?: boolean; + meta?: Record; + }): void { + const logPayload = { + level: logLevels.error, + message: params.message, + timestamp: new Date(), + meta: { + type: params.type, + error: params.error, + context: params.context, + ...params.meta, + }, + }; + const targetLevel: LogType = params.hidden ? 'debug' : 'error'; + + if (this.shouldLog(targetLevel, 'console') || this.shouldLog(targetLevel, 'file')) { + this.loggers[targetLevel].error(logPayload); + } + } + + public logWarn(params: { + type: string; + message: string; + warn?: any; + context?: string; + meta?: Record; + }): void { + const logPayload = { + level: logLevels.warn, + message: params.message, + timestamp: new Date(), + meta: { + type: params.type, + context: params.context, + ...params.meta, + }, + }; + if (this.shouldLog('warn', 'console') || this.shouldLog('warn', 'file')) { + this.loggers.warn.warn(logPayload); + } + } + + public logInfo(params: { + type: string; + message: string; + info?: any; + context?: string; + meta?: Record; + }): void { + const logPayload = { + level: logLevels.info, + message: params.message, + timestamp: new Date(), + meta: { + type: params.type, + info: params.info, + context: params.context, + ...params.meta, + }, + }; + if (this.shouldLog('info', 'console') || this.shouldLog('info', 'file')) { + this.loggers.info.info(logPayload); + } + } + + public logSuccess(params: { + type: string; + message: string; + data?: any; + context?: string; + meta?: Record; + }): void { + const logPayload = { + level: logLevels.success, + message: params.message, + timestamp: new Date(), + meta: { + type: params.type, + data: params.data, + context: params.context, + ...params.meta, + }, + }; + if (this.shouldLog('info', 'console') || this.shouldLog('info', 'file')) { + this.loggers.success.info(logPayload); + } + } + + public logDebug(params: { + type: string; + message: string; + debug?: any; + context?: string; + meta?: Record; + }): void { + const logPayload = { + level: logLevels.debug, + message: params.message, + timestamp: new Date(), + meta: { + type: params.type, + debug: params.debug, + context: params.context, + ...params.meta, + }, + }; + if (this.shouldLog('debug', 'console') || this.shouldLog('debug', 'file')) { + this.loggers.debug.debug(logPayload); + } + } +} diff --git a/packages/contentstack-utilities/test/unit/auth-handler.test.ts b/packages/contentstack-utilities/test/unit/auth-handler.test.ts index 849fb2b42e..de7e37279c 100644 --- a/packages/contentstack-utilities/test/unit/auth-handler.test.ts +++ b/packages/contentstack-utilities/test/unit/auth-handler.test.ts @@ -79,7 +79,7 @@ describe('Auth Handler', () => { expect(createHTTPServerStub.calledOnce).to.be.true; expect(openOAuthURLStub.calledOnce).to.be.true; - expect(result).to.deep.equal({}); + expect(result).to.be.undefined; }); }); @@ -127,7 +127,6 @@ describe('Auth Handler', () => { sandbox.stub(authHandler, 'setOAuthBaseURL').resolves(); sandbox.stub(crypto, 'createHash').returns({ update: () => {}, digest: () => {} }); - sandbox.stub(authHandler, 'codeVerifier').value('CODE_VERIFIER'); sandbox.stub(authHandler, 'OAuthBaseURL').value('https://example.com'); sandbox.stub(authHandler, 'OAuthAppId').value('APP_ID'); sandbox.stub(authHandler, 'OAuthResponseType').value('response_type'); @@ -355,13 +354,15 @@ describe('Auth Handler', () => { const data = { access_token: '', }; - - const userDetailsPromise = authHandler.getUserDetails(data); - - return userDetailsPromise.catch((error) => { - expect(error).to.equal('Invalid/Empty access token'); - }); - }); + + try { + await authHandler.getUserDetails(data); + throw new Error('Expected getUserDetails to throw'); // ensure failure if no error is thrown + } catch (error) { + expect(error).to.be.instanceOf(Error); + expect(error.message).to.equal('Invalid/Empty access token'); + } + }); }); describe('isAuthenticated', () => { diff --git a/packages/contentstack-utilities/test/unit/cliErrorHandler.test.ts b/packages/contentstack-utilities/test/unit/cliErrorHandler.test.ts new file mode 100644 index 0000000000..eef513d60b --- /dev/null +++ b/packages/contentstack-utilities/test/unit/cliErrorHandler.test.ts @@ -0,0 +1,108 @@ +import { expect } from 'chai'; +import { fancy } from 'fancy-test'; +import CLIErrorHandler from '../../src/logger/cliErrorHandler'; +import { ERROR_TYPES } from '../../src/constants/errorTypes'; + +describe('CLIErrorHandler', () => { + let errorHandler: CLIErrorHandler; + + beforeEach(() => { + errorHandler = new CLIErrorHandler(true); // debug mode enabled + }); + + fancy.it('should normalize string error to Error object', () => { + const error = errorHandler['normalizeToError']('simple error'); + expect(error).to.be.instanceOf(Error); + expect(error.message).to.equal('simple error'); + }); + + fancy.it('should classify a 500 error as SERVER_ERROR', () => { + const error = new Error('Server failure'); + (error as any).status = 500; + const type = errorHandler['determineErrorType'](error); + expect(type).to.equal(ERROR_TYPES.SERVER_ERROR); + }); + + fancy.it('should classify 400 error as API Error (ignores 4XX)', () => { + const error = new Error('Client error'); + (error as any).status = 404; + const type = errorHandler['determineErrorType'](error); + expect(type).to.equal(ERROR_TYPES.API_ERROR); + }); + + fancy.it('should flag error containing sensitive info as hidden', () => { + const error = new Error('My password is secret'); + const hidden = errorHandler['containsSensitiveInfo'](error); + expect(hidden).to.equal(true); + }); + + fancy.it('should extract debug payload correctly', () => { + const error = new Error('API error'); + (error as any).request = { + method: 'GET', + url: 'http://api.test/resource', + headers: { authorization: 'token' }, + data: { q: 'test' }, + }; + (error as any).response = { + status: 500, + statusText: 'Internal Server Error', + data: { error: 'fail' }, + }; + + const debugPayload = errorHandler['extractDebugPayload'](error); + expect(debugPayload.request.method).to.equal('GET'); + expect(debugPayload.response.status).to.equal(500); + expect(debugPayload.command).to.be.undefined; + }); + + fancy.it('should return full classified error with context', () => { + const err = new Error('Test error'); + (err as any).status = 502; + const classified = errorHandler.classifyError(err, { + operation: 'testOp', + component: 'testComponent', + }); + + expect(classified.type).to.equal(ERROR_TYPES.SERVER_ERROR); + expect(classified.message).to.equal('Test error'); + expect(classified.context).to.contain('testOp'); + expect(classified.meta?.email).to.be.undefined; + expect(classified.hidden).to.be.false; + }); + + fancy.it('containsSensitiveInfo should return false for clean error', () => { + const error = new Error('All good'); + const result = errorHandler['containsSensitiveInfo'](error); + expect(result).to.be.false; + }); + + fancy.it('extractMeta should return full meta', () => { + const meta = errorHandler['extractMeta']({ + email: 'a@b.com', + apiKey: '123', + sessionId: 's1', + userId: 'u1', + orgId: 'o1', + }); + expect(meta).to.deep.equal({ + email: 'a@b.com', + apiKey: '123', + sessionId: 's1', + userId: 'u1', + orgId: 'o1', + }); + }); + + fancy.it('classifyError handles internal error gracefully', () => { + const invalidError = { + get message() { + throw new Error('trigger normalize fail'); + }, + }; + + const result = errorHandler.classifyError(invalidError); + expect(result.type).to.equal(ERROR_TYPES.NORMALIZATION); + expect(result.message).to.include('Failed to normalize'); + }); +}); diff --git a/packages/contentstack-utilities/test/unit/logger.test.ts b/packages/contentstack-utilities/test/unit/logger.test.ts new file mode 100644 index 0000000000..5eaef8d350 --- /dev/null +++ b/packages/contentstack-utilities/test/unit/logger.test.ts @@ -0,0 +1,201 @@ +import { expect } from 'chai'; +import { fancy } from 'fancy-test'; +import sinon from 'sinon'; +import Logger from '../../src/logger/logger'; + +describe('Logger', () => { + let logger: Logger; + + beforeEach(() => { + logger = new Logger({ + basePath: './logs', + consoleLogLevel: 'info', + logLevel: 'info', + }); + }); + + fancy.it('should create logger instance with correct level', () => { + const winLogger = logger.getLoggerInstance('error'); + expect(winLogger.level).to.equal('error'); + }); + + fancy.it('should create hidden logger as error level', () => { + const hiddenLogger = logger.getLoggerInstance('hidden'); + expect(hiddenLogger.level).to.equal('error'); + }); + + fancy.it('should redact sensitive keys', () => { + const testMeta = { + password: 'secret123', + token: 'tokenvalue', + email: 'user@example.com', + other: 'safe', + }; + + const redacted = logger['redact'](testMeta); + expect(redacted.password).to.equal('[REDACTED]'); + expect(redacted.token).to.equal('[REDACTED]'); + expect(redacted.email).to.equal('[REDACTED]'); + expect(redacted.other).to.equal('safe'); + }); + + fancy.it('should detect valid LogEntry object', () => { + const validEntry = { level: 'info', message: 'Test message' }; + expect(logger['isLogEntry'](validEntry)).to.equal(true); + + const invalidEntry = { msg: 'nope' }; + expect(logger['isLogEntry'](invalidEntry)).to.equal(false); + }); + + fancy.it('should log error messages using error method', () => { + const errorLogger = logger['loggers'].error; + const spy = sinon.spy(); + errorLogger.error = spy; + + logger.error('error message', { some: 'meta' }); + expect(spy.calledOnce).to.be.true; + expect(spy.calledWith('error message', { some: 'meta' })).to.be.true; + }); + + fancy.it('should return correct result from shouldLog()', () => { + const shouldLogInfoConsole = logger['shouldLog']('info', 'console'); + const shouldLogDebugConsole = logger['shouldLog']('debug', 'console'); + + expect(shouldLogInfoConsole).to.equal(true); + expect(shouldLogDebugConsole).to.equal(false); + }); + + fancy.it('logSuccess should call success info logger', () => { + const successLogger = logger['loggers'].success; + const spy = sinon.spy(); + successLogger.info = spy; + + logger.logSuccess({ type: 'test', message: 'Success message' }); + expect(spy.calledOnce).to.be.true; + expect(spy.args[0][0].message).to.equal('Success message'); + }); + + fancy.it('shouldLog should handle file target level filtering', () => { + const result = logger['shouldLog']('debug', 'file'); // logLevel = info + expect(result).to.equal(false); + }); + + fancy.it('success logger should include success type in meta', () => { + const spy = sinon.spy(); + logger['loggers'].success.info = spy; + + logger.success('It worked!', { extra: 'meta' }); + expect(spy.calledOnce).to.be.true; + expect(spy.args[0][1].type).to.equal('success'); + }); + + fancy.it('logError with hidden true logs to debug logger', () => { + const debugLogger = new Logger({ + basePath: './logs', + consoleLogLevel: 'debug', + logLevel: 'debug', + }); + + const spy = sinon.spy(); + debugLogger['loggers'].debug.error = spy; + + debugLogger.logError({ + type: 'hiddenType', + message: 'Something secret', + error: new Error('oops'), + hidden: true, + }); + + expect(spy.calledOnce).to.be.true; + const [logPayload] = spy.args[0]; + expect(logPayload.meta.type).to.equal('hiddenType'); + }); + + fancy.it('redact handles splat symbol', () => { + const obj = { + token: 'abc', + [Symbol.for('splat')]: [{ password: '1234' }], + }; + const result = logger['redact'](obj); + expect(result.token).to.equal('[REDACTED]'); + expect(result[Symbol.for('splat')][0].password).to.equal('[REDACTED]'); + }); + + fancy.it('redact should return original if klona fails', () => { + const faultyLogger = new Logger({ + basePath: './logs', + consoleLogLevel: 'info', + logLevel: 'info', + }); + + const obj = { + toJSON: () => { + throw new Error('klona fails'); + } + }; + + const result = faultyLogger['redact'](obj); + expect(result).to.deep.equal(obj); + }); + + fancy.it('should call logWarn with correct meta', () => { + const warnSpy = sinon.spy(); + logger['loggers'].warn.warn = warnSpy; + + logger.logWarn({ + type: 'testType', + message: 'Warn occurred', + context: 'warnContext', + meta: { custom: 'value' } + }); + + expect(warnSpy.calledOnce).to.be.true; + expect(warnSpy.args[0][0].meta.type).to.equal('testType'); + expect(warnSpy.args[0][0].meta.custom).to.equal('value'); + }); + + fancy.it('should call logInfo with correct meta', () => { + const infoSpy = sinon.spy(); + logger['loggers'].info.info = infoSpy; + + logger.logInfo({ + type: 'infoType', + message: 'This is info', + meta: { foo: 'bar' }, + }); + + expect(infoSpy.calledOnce).to.be.true; + expect(infoSpy.args[0][0].meta.foo).to.equal('bar'); + }); + + fancy.it('should call logDebug with correct meta', () => { + const debugLogger = new Logger({ + basePath: './logs', + consoleLogLevel: 'debug', + logLevel: 'debug', + }); + + const debugSpy = sinon.spy(); + debugLogger['loggers'].debug.debug = debugSpy; + + debugLogger.logDebug({ + type: 'debugType', + message: 'Debug data', + meta: { extra: 'info' }, + }); + + expect(debugSpy.calledOnce).to.be.true; + expect(debugSpy.args[0][0].meta.extra).to.equal('info'); + }); + + fancy.it('shouldLog should default to info when config not defined', () => { + const defaultLogger = new Logger({ + basePath: './logs', + consoleLogLevel: undefined as any, + logLevel: undefined as any, + }); + + expect(defaultLogger['shouldLog']('info', 'console')).to.equal(true); + expect(defaultLogger['shouldLog']('debug', 'console')).to.equal(false); + }); +}); diff --git a/packages/contentstack/README.md b/packages/contentstack/README.md index 79ddce42ea..a660ab1932 100644 --- a/packages/contentstack/README.md +++ b/packages/contentstack/README.md @@ -3776,7 +3776,8 @@ USAGE $ csdx launch:functions [-p ] [-d ] FLAGS - -d, --data-dir= [default: /Users/sunil.lakshman/Documents/cli/packages/contentstack] Current working directory + -d, --data-dir= [default: /Users/aman.kumar/Documents/cli-repos/cli/packages/contentstack] Current working + directory -p, --port= [default: 3000] Port number DESCRIPTION diff --git a/packages/contentstack/package.json b/packages/contentstack/package.json index 4d89751cbc..2178dc1daf 100755 --- a/packages/contentstack/package.json +++ b/packages/contentstack/package.json @@ -38,7 +38,7 @@ "@contentstack/cli-config": "~1.12.1", "@contentstack/cli-launch": "^1.9.1", "@contentstack/cli-migration": "~1.7.2", - "@contentstack/cli-utilities": "~1.11.2", + "@contentstack/cli-utilities": "~1.12.0", "@contentstack/cli-variants": "~1.2.1", "@contentstack/management": "~1.21.3", "@oclif/core": "^4.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f2c704a712..4c2d2644de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,7 +28,7 @@ importers: '@contentstack/cli-config': ~1.12.1 '@contentstack/cli-launch': ^1.9.1 '@contentstack/cli-migration': ~1.7.2 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@contentstack/cli-variants': ~1.2.1 '@contentstack/management': ~1.21.3 '@oclif/core': ^4.3.0 @@ -132,7 +132,7 @@ importers: packages/contentstack-audit: specifiers: '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@oclif/plugin-help': ^6.2.28 '@oclif/plugin-plugins': ^5.4.36 '@oclif/test': ^4.1.13 @@ -213,7 +213,7 @@ importers: typescript: ^4.9.5 dependencies: '@contentstack/cli-command': link:../contentstack-command - '@contentstack/cli-utilities': link:../contentstack-utilities + '@contentstack/cli-utilities': 1.11.1 devDependencies: '@fancy-test/nock': 0.1.1 '@oclif/plugin-help': 6.2.28 @@ -239,7 +239,7 @@ importers: specifiers: '@contentstack/cli-cm-seed': ~1.11.1 '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@oclif/test': ^4.1.13 '@types/inquirer': ^9.0.8 '@types/mkdirp': ^1.0.2 @@ -286,7 +286,7 @@ importers: specifiers: '@contentstack/cli-command': ~1.5.0 '@contentstack/cli-dev-dependencies': ~1.3.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@oclif/core': ^4.3.0 '@oclif/plugin-help': ^6.2.28 '@types/flat': ^5.0.5 @@ -330,7 +330,7 @@ importers: packages/contentstack-bulk-publish: specifiers: '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@oclif/test': ^4.1.13 chai: ^4.5.0 chalk: ^4.1.2 @@ -366,7 +366,7 @@ importers: '@contentstack/cli-cm-export': ~1.16.2 '@contentstack/cli-cm-import': ~1.22.1 '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@oclif/test': ^4.1.13 chai: ^4.5.0 chalk: ^4.1.2 @@ -409,7 +409,7 @@ importers: packages/contentstack-command: specifiers: - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@oclif/test': ^4.1.13 '@types/mkdirp': ^1.0.2 '@types/mocha': ^8.2.3 @@ -441,7 +441,7 @@ importers: packages/contentstack-config: specifiers: '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@oclif/test': ^4.1.13 '@types/chai': ^4.3.20 '@types/mocha': ^8.2.3 @@ -510,7 +510,7 @@ importers: '@contentstack/cli-command': ~1.5.0 '@contentstack/cli-config': ~1.12.0 '@contentstack/cli-dev-dependencies': ~1.3.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@contentstack/cli-variants': ~1.2.1 '@oclif/core': ^4.3.0 '@oclif/plugin-help': ^6.2.28 @@ -574,7 +574,7 @@ importers: packages/contentstack-export-to-csv: specifiers: '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@oclif/test': ^4.1.13 '@types/chai': ^4.3.20 '@types/mocha': ^10.0.10 @@ -612,7 +612,7 @@ importers: specifiers: '@contentstack/cli-audit': ~1.12.2 '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@contentstack/cli-variants': ~1.2.1 '@contentstack/management': ~1.21.3 '@oclif/core': ^4.3.0 @@ -686,7 +686,7 @@ importers: packages/contentstack-import-setup: specifiers: '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@oclif/core': ^4.3.0 '@types/big-json': ^3.2.5 '@types/bluebird': ^3.5.42 @@ -747,7 +747,7 @@ importers: packages/contentstack-migrate-rte: specifiers: '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@contentstack/json-rte-serializer': ~2.1.0 '@oclif/test': ^4.1.13 chai: ^4.5.0 @@ -788,7 +788,7 @@ importers: packages/contentstack-migration: specifiers: '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@oclif/test': ^4.1.13 async: ^3.2.6 callsites: ^3.1.0 @@ -828,7 +828,7 @@ importers: specifiers: '@contentstack/cli-cm-import': ~1.22.1 '@contentstack/cli-command': ~1.5.0 - '@contentstack/cli-utilities': ~1.11.2 + '@contentstack/cli-utilities': ~1.12.0 '@contentstack/management': ~1.21.3 '@types/inquirer': ^9.0.8 '@types/jest': ^26.0.24 @@ -986,7 +986,7 @@ importers: typescript: ^5.8.3 winston: ^3.17.0 dependencies: - '@contentstack/cli-utilities': link:../contentstack-utilities + '@contentstack/cli-utilities': 1.11.1 lodash: 4.17.21 mkdirp: 1.0.4 winston: 3.17.0 @@ -2063,6 +2063,42 @@ packages: - typescript dev: false + /@contentstack/cli-utilities/1.11.1: + resolution: {integrity: sha512-d2i17OeHMc2WOkkwuzVNmplD0y/d1GV4v21PZre/A66xcB/COaBn8rvvsKjo18RIUl6vY72PsrzNq6xRc7AOfg==} + dependencies: + '@contentstack/management': 1.20.3 + '@contentstack/marketplace-sdk': 1.2.8 + '@oclif/core': 4.3.0 + axios: 1.9.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-progress: 3.12.0 + cli-table: 0.3.11 + conf: 10.2.0 + dotenv: 16.5.0 + figures: 3.2.0 + inquirer: 8.2.6 + inquirer-search-checkbox: 1.0.0 + inquirer-search-list: 1.2.6 + js-yaml: 4.1.0 + klona: 2.0.6 + lodash: 4.17.21 + mkdirp: 1.0.4 + open: 8.4.2 + ora: 5.4.1 + papaparse: 5.5.3 + recheck: 4.4.5 + rxjs: 6.6.7 + traverse: 0.6.11 + tty-table: 4.2.3 + unique-string: 2.0.0 + uuid: 9.0.1 + winston: 3.17.0 + xdg-basedir: 4.0.0 + transitivePeerDependencies: + - debug + dev: false + /@contentstack/cli-utilities/1.11.1_debug@4.4.1: resolution: {integrity: sha512-d2i17OeHMc2WOkkwuzVNmplD0y/d1GV4v21PZre/A66xcB/COaBn8rvvsKjo18RIUl6vY72PsrzNq6xRc7AOfg==} dependencies: @@ -2116,6 +2152,21 @@ packages: uuid: 8.3.2 dev: false + /@contentstack/management/1.20.3: + resolution: {integrity: sha512-ddPLEYRCBfcezTx7X85oO63aTLn7ZglBXDhp7ofJdFefwF52LTWKni71Scxn5NX5CrniSNqNAquPKZBnfOVl0Q==} + engines: {node: '>=8.0.0'} + dependencies: + assert: 2.1.0 + axios: 1.9.0 + buffer: 6.0.3 + form-data: 4.0.2 + lodash: 4.17.21 + qs: 6.14.0 + stream-browserify: 3.0.0 + transitivePeerDependencies: + - debug + dev: false + /@contentstack/management/1.20.3_debug@4.4.1: resolution: {integrity: sha512-ddPLEYRCBfcezTx7X85oO63aTLn7ZglBXDhp7ofJdFefwF52LTWKni71Scxn5NX5CrniSNqNAquPKZBnfOVl0Q==} engines: {node: '>=8.0.0'} @@ -8213,7 +8264,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 8.33.0_avq3eyf5kaj6ssrwo7fvkrwnji + '@typescript-eslint/parser': 8.33.0_hzt6xcfnpp4qecssyxfdrtmoeu debug: 3.2.7 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 @@ -8243,7 +8294,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.21.0_avq3eyf5kaj6ssrwo7fvkrwnji + '@typescript-eslint/parser': 6.21.0_hzt6xcfnpp4qecssyxfdrtmoeu debug: 3.2.7 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 @@ -8309,7 +8360,7 @@ packages: optional: true dependencies: '@rtsao/scc': 1.1.0 - '@typescript-eslint/parser': 8.33.0_avq3eyf5kaj6ssrwo7fvkrwnji + '@typescript-eslint/parser': 8.33.0_hzt6xcfnpp4qecssyxfdrtmoeu array-includes: 3.1.8 array.prototype.findlastindex: 1.2.6 array.prototype.flat: 1.3.3 @@ -8346,7 +8397,7 @@ packages: optional: true dependencies: '@rtsao/scc': 1.1.0 - '@typescript-eslint/parser': 6.21.0_avq3eyf5kaj6ssrwo7fvkrwnji + '@typescript-eslint/parser': 6.21.0_hzt6xcfnpp4qecssyxfdrtmoeu array-includes: 3.1.8 array.prototype.findlastindex: 1.2.6 array.prototype.flat: 1.3.3