diff --git a/.talismanrc b/.talismanrc index 940f0be3a1..630ad8dfb0 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,8 +1,8 @@ fileignoreconfig: - filename: package-lock.json - checksum: c6ec152494aee731a4dc34a22af9125e0ed89a7b5e5fe09ba5133d5bb1a00eae + checksum: fa02e2883b15c973134368ce26c78187e247d3761ef14f603afc0d5ea83102a6 - filename: pnpm-lock.yaml - checksum: d66d3fc47581c5d93534b83ed384be92026fa1f2215ed25a67d81e21f3d86604 + checksum: d4174feb2ba68c22cc45987afdaf4d89997df084e7e7eb9edc7e309b5a24aca5 - filename: packages/contentstack-import-setup/test/unit/backup-handler.test.ts checksum: 0582d62b88834554cf12951c8690a73ef3ddbb78b82d2804d994cf4148e1ef93 - filename: packages/contentstack-import-setup/test/config.json @@ -78,7 +78,7 @@ fileignoreconfig: - filename: packages/contentstack-clone/src/commands/cm/stacks/clone.js checksum: 433a84a882ea3f12b27127d47d289dfc64dda6b6fc956369f5851daaa57ae493 - filename: packages/contentstack-clone/src/lib/util/clone-handler.js - checksum: 7024f22a6ed3908d7cf074bbd8e7107e2d9f43bbcc42939b28d360c89d44cc29 + checksum: f901c84eac8545b328952332216de516697da2de098298496ba6ff1e75a0a659 - filename: packages/contentstack-bulk-publish/src/util/generate-bulk-publish-url.js checksum: 5f7c1e2fac3e7fab21e861d609c54ca7191ee09fd076dd0adc66604043bf7a43 - filename: packages/contentstack-import/src/utils/interactive.ts diff --git a/package-lock.json b/package-lock.json index 36b4d7a944..aac4854055 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3058,6 +3058,27 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -4438,6 +4459,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -15452,18 +15474,18 @@ } }, "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/jake": { @@ -21827,26 +21849,29 @@ "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } }, "node_modules/path-scurry/node_modules/minipass": { "version": "7.1.2", @@ -23409,6 +23434,7 @@ "version": "5.0.10", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, "license": "ISC", "dependencies": { "glob": "^10.3.7" @@ -23424,6 +23450,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -23440,6 +23467,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -23456,19 +23484,61 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/rimraf/node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, + "node_modules/rimraf/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/rimraf/node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -27641,8 +27711,7 @@ "merge": "^2.1.1", "ora": "^5.4.1", "prompt": "^1.3.0", - "rimraf": "^5.0.10", - "winston": "^3.17.0" + "rimraf": "^6.1.0" }, "devDependencies": { "@oclif/test": "^4.1.13", @@ -27658,6 +27727,100 @@ "node": ">=14.0.0" } }, + "packages/contentstack-clone/node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/contentstack-clone/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/contentstack-clone/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/contentstack-clone/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "packages/contentstack-clone/node_modules/rimraf": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.0.tgz", + "integrity": "sha512-DxdlA1bdNzkZK7JiNWH+BAx1x4tEJWoTofIopFo6qWUU94jYrFZ0ubY05TqH3nWPJ1nKa1JWVFDINZ3fnrle/A==", + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^11.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/contentstack-clone/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "packages/contentstack-command": { "name": "@contentstack/cli-command", "version": "1.6.2", diff --git a/packages/contentstack-clone/package.json b/packages/contentstack-clone/package.json index 0ae9722fcc..e444077b1b 100644 --- a/packages/contentstack-clone/package.json +++ b/packages/contentstack-clone/package.json @@ -18,8 +18,7 @@ "merge": "^2.1.1", "ora": "^5.4.1", "prompt": "^1.3.0", - "rimraf": "^5.0.10", - "winston": "^3.17.0" + "rimraf": "^6.1.0" }, "devDependencies": { "@oclif/test": "^4.1.13", diff --git a/packages/contentstack-clone/src/commands/cm/stacks/clone.js b/packages/contentstack-clone/src/commands/cm/stacks/clone.js index a1b5a44bcc..7f210fc791 100644 --- a/packages/contentstack-clone/src/commands/cm/stacks/clone.js +++ b/packages/contentstack-clone/src/commands/cm/stacks/clone.js @@ -1,5 +1,5 @@ const { Command } = require('@contentstack/cli-command'); -const { configHandler, flags, isAuthenticated, managementSDKClient } = require('@contentstack/cli-utilities'); +const { configHandler, flags, isAuthenticated, managementSDKClient, log, handleAndLogError } = require('@contentstack/cli-utilities'); const { CloneHandler } = require('../../../lib/util/clone-handler'); const path = require('path'); const { rimraf } = require('rimraf'); @@ -9,6 +9,44 @@ const { readdirSync, readFileSync } = require('fs'); let config = {}; class StackCloneCommand extends Command { + /** + * Determine authentication method based on user preference + */ + determineAuthenticationMethod(sourceManagementTokenAlias, destinationManagementTokenAlias) { + // Track authentication method + let authenticationMethod = 'unknown'; + + // Determine authentication method based on user preference + if (sourceManagementTokenAlias || destinationManagementTokenAlias) { + authenticationMethod = 'Management Token'; + } else if (isAuthenticated()) { + // Check if user is authenticated via OAuth + const isOAuthUser = configHandler.get('authorisationType') === 'OAUTH' || false; + if (isOAuthUser) { + authenticationMethod = 'OAuth'; + } else { + authenticationMethod = 'Basic Auth'; + } + } else { + authenticationMethod = 'Basic Auth'; + } + + return authenticationMethod; + } + + /** + * Create clone context object for logging + */ + createCloneContext(authenticationMethod) { + return { + command: this.context?.info?.command || 'cm:stacks:clone', + module: 'clone', + email: configHandler.get('email') || '', + sessionId: this.context?.sessionId || '', + authenticationMethod: authenticationMethod || 'Basic Auth', + }; + } + async run() { try { let self = this; @@ -31,14 +69,27 @@ class StackCloneCommand extends Command { const handleClone = async () => { const listOfTokens = configHandler.get('tokens'); + const authenticationMethod = this.determineAuthenticationMethod( + sourceManagementTokenAlias, + destinationManagementTokenAlias, + ); + const cloneContext = this.createCloneContext(authenticationMethod); + log.debug('Starting clone operation setup', cloneContext); if (externalConfigPath) { + log.debug(`Loading external configuration from: ${externalConfigPath}`, cloneContext); let externalConfig = readFileSync(externalConfigPath, 'utf-8'); externalConfig = JSON.parse(externalConfig); config = merge.recursive(config, externalConfig); } config.forceStopMarketplaceAppsPrompt = yes; config.skipAudit = cloneCommandFlags['skip-audit']; + log.debug('Clone configuration prepared', { + ...cloneContext, + cloneType: config.cloneType, + skipAudit: config.skipAudit, + forceStopMarketplaceAppsPrompt: config.forceStopMarketplaceAppsPrompt + }); if (cloneType) { config.cloneType = cloneType; @@ -67,15 +118,18 @@ class StackCloneCommand extends Command { if (sourceManagementTokenAlias && listOfTokens[sourceManagementTokenAlias]) { config.source_alias = sourceManagementTokenAlias; config.source_stack = listOfTokens[sourceManagementTokenAlias].apiKey; + log.debug(`Using source token alias: ${sourceManagementTokenAlias}`, cloneContext); } else if (sourceManagementTokenAlias) { - console.log(`Provided source token alias (${sourceManagementTokenAlias}) not found in your config.!`); + log.warn(`Provided source token alias (${sourceManagementTokenAlias}) not found in your config.!`, cloneContext); } if (destinationManagementTokenAlias && listOfTokens[destinationManagementTokenAlias]) { config.destination_alias = destinationManagementTokenAlias; config.target_stack = listOfTokens[destinationManagementTokenAlias].apiKey; + log.debug(`Using destination token alias: ${destinationManagementTokenAlias}`, cloneContext); } else if (destinationManagementTokenAlias) { - console.log( + log.warn( `Provided destination token alias (${destinationManagementTokenAlias}) not found in your config.!`, + cloneContext, ); } if (importWebhookStatus) { @@ -83,18 +137,23 @@ class StackCloneCommand extends Command { } const managementAPIClient = await managementSDKClient(config); + log.debug('Management API client initialized successfully', cloneContext); - await this.removeContentDirIfNotEmptyBeforeClone(pathdir); // NOTE remove if folder not empty before clone - this.registerCleanupOnInterrupt(pathdir); + log.debug(`Content directory path: ${pathdir}`, cloneContext); + await this.removeContentDirIfNotEmptyBeforeClone(pathdir, cloneContext); // NOTE remove if folder not empty before clone + this.registerCleanupOnInterrupt(pathdir, cloneContext); config.auth_token = configHandler.get('authtoken'); config.host = this.cmaHost; config.cdn = this.cdaHost; config.pathDir = pathdir; + config.cloneContext = cloneContext; + log.debug('Clone configuration finalized', cloneContext); const cloneHandler = new CloneHandler(config); cloneHandler.setClient(managementAPIClient); + log.debug('Starting clone operation', cloneContext); cloneHandler.execute().catch((error) => { - console.log(error); + handleAndLogError(error, cloneContext); }); }; @@ -103,7 +162,7 @@ class StackCloneCommand extends Command { if (isAuthenticated()) { handleClone(); } else { - console.log('Please login to execute this command, csdx auth:login'); + log.error('Please login to execute this command, csdx auth:login', cloneContext); this.exit(1); } } else { @@ -112,76 +171,76 @@ class StackCloneCommand extends Command { } else if (isAuthenticated()) { handleClone(); } else { - console.log('Please login to execute this command, csdx auth:login'); + log.error('Please login to execute this command, csdx auth:login', cloneContext); this.exit(1); } } catch (error) { if (error) { - await this.cleanUp(pathdir); - // eslint-disable-next-line no-console - console.log(error.message || error); + await this.cleanUp(pathdir, null, cloneContext); + log.error('Stack clone command failed', { ...cloneContext, error: error?.message || error }); } } } - async removeContentDirIfNotEmptyBeforeClone(dir) { + async removeContentDirIfNotEmptyBeforeClone(dir, cloneContext) { try { + log.debug('Checking if content directory is empty', { ...cloneContext, dir }); const dirNotEmpty = readdirSync(dir).length; if (dirNotEmpty) { - await this.cleanUp(dir); + log.debug('Content directory is not empty, cleaning up', { ...cloneContext, dir }); + await this.cleanUp(dir, null, cloneContext); } } catch (error) { const omit = ['ENOENT']; // NOTE add emittable error codes in the array if (!omit.includes(error.code)) { - console.log(error.message); + log.error('Error checking content directory', { ...cloneContext, error: error?.message, code: error.code }); } } } - async cleanUp(pathDir, message) { + async cleanUp(pathDir, message, cloneContext) { try { + log.debug('Starting cleanup', { ...cloneContext, pathDir }); await rimraf(pathDir); if (message) { - // eslint-disable-next-line no-console - console.log(message); + log.info(message, cloneContext); } + log.debug('Cleanup completed', { ...cloneContext, pathDir }); } catch (err) { if (err) { - console.log('\nCleaning up'); + log.debug('Cleaning up', cloneContext); const skipCodeArr = ['ENOENT', 'EBUSY', 'EPERM', 'EMFILE', 'ENOTEMPTY']; if (skipCodeArr.includes(err.code)) { + log.debug('Cleanup error code is in skip list, exiting', { ...cloneContext, code: err?.code }); process.exit(); } } } } - registerCleanupOnInterrupt(pathDir) { + registerCleanupOnInterrupt(pathDir, cloneContext) { const interrupt = ['SIGINT', 'SIGQUIT', 'SIGTERM']; const exceptions = ['unhandledRejection', 'uncaughtException']; const cleanUp = async (exitOrError) => { if (exitOrError) { - // eslint-disable-next-line no-console - console.log('\nCleaning up'); - await this.cleanUp(pathDir); - // eslint-disable-next-line no-console - console.log('done'); - // eslint-disable-next-line no-process-exit + log.debug('Cleaning up on interrupt', cloneContext); + await this.cleanUp(pathDir, null, cloneContext); + log.info('Cleanup done', cloneContext); if (exitOrError instanceof Promise) { exitOrError.catch((error) => { - console.log((error && error.message) || ''); + log.error('Error during cleanup', { ...cloneContext, error: (error && error?.message) || '' }); }); } else if (exitOrError.message) { - console.log(exitOrError.message); + log.error('Cleanup error', { ...cloneContext, error: exitOrError?.message }); } else if (exitOrError.errorMessage) { - console.log(exitOrError.message); + log.error('Cleanup error', { ...cloneContext, error: exitOrError?.errorMessage }); } if (exitOrError === true) process.exit(); diff --git a/packages/contentstack-clone/src/lib/util/clone-handler.js b/packages/contentstack-clone/src/lib/util/clone-handler.js index 1449bd0cbf..0bd4aab725 100644 --- a/packages/contentstack-clone/src/lib/util/clone-handler.js +++ b/packages/contentstack-clone/src/lib/util/clone-handler.js @@ -21,7 +21,7 @@ const { Clone, HandleBranchCommand, } = require('../helpers/command-helpers'); -const { configHandler, getBranchFromAlias } = require('@contentstack/cli-utilities'); +const { configHandler, getBranchFromAlias, log } = require('@contentstack/cli-utilities'); let client = {}; let config; @@ -76,6 +76,7 @@ class CloneHandler { cloneCommand = new Clone(); this.pathDir = opt.pathDir; process.stdin.setMaxListeners(50); + log.debug('Initializing CloneHandler', config.cloneContext, { pathDir: opt.pathDir, cloneType: opt.cloneType }); } setClient(managementSDKClient) { client = managementSDKClient; @@ -84,19 +85,24 @@ class CloneHandler { handleOrgSelection(options = {}) { return new Promise(async (resolve, reject) => { const { msg = '', isSource = true } = options || {}; + log.debug('Handling organization selection', config.cloneContext); const orgList = await this.getOrganizationChoices(msg).catch(reject); - if (orgList) { - const orgSelected = await inquirer.prompt(orgList); + if (orgList) { + log.debug(`Found ${orgList.choices?.length || 0} organization(s) to choose from`, config.cloneContext); + const orgSelected = await inquirer.prompt(orgList); + log.debug(`Organization selected: ${orgSelected.Organization}`, config.cloneContext); - if (isSource) { - config.sourceOrg = orgUidList[orgSelected.Organization]; - } else { - config.targetOrg = orgUidList[orgSelected.Organization]; - } + if (isSource) { + config.sourceOrg = orgUidList[orgSelected.Organization]; + log.debug(`Source organization UID: ${config.sourceOrg}`, config.cloneContext); + } else { + config.targetOrg = orgUidList[orgSelected.Organization]; + log.debug(`Target organization UID: ${config.targetOrg}`, config.cloneContext); + } - resolve(orgSelected); - } + resolve(orgSelected); + } }); } @@ -104,13 +110,16 @@ class CloneHandler { return new Promise(async (resolve, reject) => { try { const { org = {}, msg = '', isSource = true } = options || {}; + log.debug('Handling stack selection', config.cloneContext, { isSource, orgName: org.Organization, msg }); const stackList = await this.getStack(org, msg, isSource).catch(reject); if (stackList) { this.displayBackOptionMessage(); + log.debug(`Found ${stackList.choices?.length || 0} stack(s) to choose from`, config.cloneContext); const selectedStack = await inquirer.prompt(stackList); + log.debug(`Stack selected: ${selectedStack.stack}`, config.cloneContext); if (this.executingCommand != 1) { return reject(); } @@ -118,9 +127,11 @@ class CloneHandler { config.sourceStackName = selectedStack.stack; master_locale = masterLocaleList[selectedStack.stack]; config.source_stack = stackUidList[selectedStack.stack]; + log.debug(`Source stack configured`, config.cloneContext); } else { config.target_stack = stackUidList[selectedStack.stack]; config.destinationStackName = selectedStack.stack; + log.debug(`Target stack configured`, config.cloneContext); } resolve(selectedStack); @@ -136,6 +147,7 @@ class CloneHandler { return new Promise(async (resolve, reject) => { let spinner; try { + log.debug('Handling branch selection', config.cloneContext, { isSource, returnBranch, stackApiKey: isSource ? config.source_stack : config.target_stack }); const stackAPIClient = client.stack({ api_key: isSource ? config.source_stack : config.target_stack, management_token: config.management_token, @@ -143,22 +155,27 @@ class CloneHandler { // NOTE validate if source branch is exist if (isSource && config.sourceStackBranch) { + log.debug('Validating source branch exists', { ...config.cloneContext, branch: config.sourceStackBranch }); await this.validateIfBranchExist(stackAPIClient, true); return resolve(); } else if(isSource && config.sourceStackBranchAlias) { + log.debug('Resolving source branch alias', { ...config.cloneContext, alias: config.sourceStackBranchAlias }); await this.resolveBranchAliases(true); return resolve(); } // NOTE Validate target branch is exist if (!isSource && config.targetStackBranch) { + log.debug('Validating target branch exists', { ...config.cloneContext, branch: config.targetStackBranch }); await this.validateIfBranchExist(stackAPIClient, false); return resolve(); } else if (!isSource && config.targetStackBranchAlias) { + log.debug('Resolving target branch alias', { ...config.cloneContext, alias: config.targetStackBranchAlias }); await this.resolveBranchAliases(); return resolve(); } spinner = ora('Fetching Branches').start(); + log.debug(`Querying branches for stack: ${isSource ? config.source_stack : config.target_stack}`, config.cloneContext); const result = await stackAPIClient .branch() .query() @@ -167,6 +184,7 @@ class CloneHandler { .catch((_err) => {}); const condition = result && Array.isArray(result) && result.length > 0; + log.debug(`Found ${result?.length || 0} branch(es)`, config.cloneContext); // NOTE if want to get only list of branches (Pass param -> returnBranch = true ) if (returnBranch) { @@ -185,8 +203,10 @@ class CloneHandler { } if (isSource) { config.sourceStackBranch = branch; + log.debug(`Source branch selected: ${branch}`, config.cloneContext); } else { config.targetStackBranch = branch; + log.debug(`Target branch selected: ${branch}`, config.cloneContext); } } else { spinner.succeed('No branches found.!'); @@ -196,7 +216,6 @@ class CloneHandler { } } catch (e) { if (spinner) spinner.fail(); - console.error(e && e.message); return reject(e); } }); @@ -210,6 +229,7 @@ class CloneHandler { }; try { const branch = isSource ? config.sourceStackBranch : config.targetStackBranch; + log.debug('Validating branch existence', config.cloneContext); spinner = ora(`Validation if ${isSource ? 'source' : 'target'} branch exist.!`).start(); const isBranchExist = await stackAPIClient .branch(branch) @@ -217,8 +237,10 @@ class CloneHandler { .then((data) => data); if (isBranchExist && typeof isBranchExist === 'object') { + log.debug('Branch validation successful', config.cloneContext); completeSpinner(`${isSource ? 'Source' : 'Target'} branch verified.!`); } else { + log.error('Branch not found', config.cloneContext); completeSpinner(`${isSource ? 'Source' : 'Target'} branch not found.!`, 'fail'); process.exit(); } @@ -247,8 +269,10 @@ class CloneHandler { return new Promise(async (resolve, reject) => { let keyPressHandler; try { + log.debug('Starting clone execution', { ...config.cloneContext, sourceStack: config.source_stack, targetStack: config.target_stack }); if (!config.source_stack) { const orgMsg = 'Choose an organization where your source stack exists:'; + log.debug('Source stack not provided, prompting for organization', config.cloneContext); this.setExectingCommand(0); this.removeBackKeyPressHandler(); const org = await cloneCommand.execute(new HandleOrgCommand({ msg: orgMsg, isSource: true }, this)); @@ -278,17 +302,21 @@ class CloneHandler { return reject('Org not found.'); } } else { + log.debug('Source stack provided, proceeding with branch selection and export', config.cloneContext); this.setExectingCommand(2); await this.handleBranchSelection({ api_key: config.sourceStack }); + log.debug('Starting export operation', config.cloneContext); const exportRes = await cloneCommand.execute(new HandleExportCommand(null, this)); await cloneCommand.execute(new SetBranchCommand(null, this)); if (exportRes) { + log.debug('Export completed, proceeding with destination setup', config.cloneContext); this.executeDestination().catch((error) => { return reject(error); }); } } + log.debug('Clone execution completed successfully', config.cloneContext); return resolve(); } catch (error) { return reject(error); @@ -327,10 +355,12 @@ class CloneHandler { async executeExport() { try { + log.debug('Executing export operation', config.cloneContext); const exportRes = await cloneCommand.execute(new HandleExportCommand(null, this)); await cloneCommand.execute(new SetBranchCommand(null, this)); if (exportRes) { + log.debug('Export operation completed, proceeding with destination', config.cloneContext); this.executeDestination().catch(() => { throw ''; }); @@ -346,8 +376,10 @@ class CloneHandler { return new Promise(async (resolve, reject) => { let keyPressHandler; try { + log.debug('Executing destination setup', config.cloneContext); let canCreateStack = false; if (!config.target_stack) { + log.debug('Target stack not provided, prompting for stack creation', config.cloneContext); canCreateStack = await inquirer.prompt(stackCreationConfirmation); } @@ -397,6 +429,7 @@ class CloneHandler { await this.executeBranchDestinationPrompt(params); } + log.debug('Destination setup completed successfully', config.cloneContext); return resolve(); } catch (error) { reject(error); @@ -469,10 +502,12 @@ class CloneHandler { choices: [], }; return new Promise(async (resolve, reject) => { + log.debug('Fetching organization choices', config.cloneContext); const spinner = ora('Fetching Organization').start(); try { let organizations; const configOrgUid = configHandler.get('oauthOrgUid'); + log.debug('Getting organizations', config.cloneContext, { hasConfigOrgUid: !!configOrgUid }); if (configOrgUid) { organizations = await client.organization(configOrgUid).fetch(); @@ -481,6 +516,7 @@ class CloneHandler { } spinner.succeed('Fetched Organization'); + log.debug('Fetched organizations', config.cloneContext); for (const element of organizations.items || [organizations]) { orgUidList[element.name] = element.uid; orgChoice.choices.push(element.name); @@ -501,12 +537,15 @@ class CloneHandler { message: stkMessage !== undefined ? stkMessage : 'Select the stack', choices: [], }; + log.debug('Fetching stacks', config.cloneContext); const spinner = ora('Fetching stacks').start(); try { const organization_uid = orgUidList[answer.Organization]; + log.debug('Querying stacks for organization', config.cloneContext, { organizationUid: organization_uid }); const stackList = client.stack().query({ organization_uid }).find(); stackList .then((stacklist) => { + log.debug('Fetched stacks', config.cloneContext, { count: stacklist.items ? stacklist.items.length : 0 }); for (const element of stacklist.items) { stackUidList[element.name] = element.api_key; masterLocaleList[element.name] = element.master_locale; @@ -530,9 +569,11 @@ class CloneHandler { return new Promise(async (resolve, reject) => { try { const { orgUid } = options; + log.debug('Creating new stack', config.cloneContext, { orgUid, masterLocale: master_locale, stackName: config.stackName }); this.displayBackOptionMessage(); let inputvalue; if (!config.stackName) { + log.debug('Stack name not provided, prompting user', config.cloneContext); prompt.start(); prompt.message = ''; this.setCreateNewStackPrompt(prompt); @@ -542,17 +583,24 @@ class CloneHandler { inputvalue = { stack: config.stackName }; } if (this.executingCommand === 0 || !inputvalue) { + log.debug('Stack creation cancelled or invalid input', config.cloneContext); return reject(); } let stack = { name: inputvalue.stack, master_locale: master_locale }; + log.debug('Creating stack with configuration', config.cloneContext); const spinner = ora('Creating New stack').start(); + log.debug('Sending stack creation API request', config.cloneContext); let newStack = client.stack().create({ stack }, { organization_uid: orgUid }); newStack .then((result) => { + log.debug('Stack created successfully', config.cloneContext, { + stackName: result.name, + }); spinner.succeed('New Stack created Successfully name as ' + result.name); config.target_stack = result.api_key; config.destinationStackName = result.name; + log.debug('Target stack configuration updated', config.cloneContext); return resolve(result); }) .catch((error) => { @@ -589,12 +637,15 @@ class CloneHandler { async resolveBranchAliases(isSource = false) { try { + log.debug('Resolving branch aliases', { ...config.cloneContext, isSource, alias: isSource ? config.sourceStackBranchAlias : config.targetStackBranchAlias }); if (isSource) { const sourceStack = client.stack({ api_key: config.source_stack }); config.sourceStackBranch = await getBranchFromAlias(sourceStack, config.sourceStackBranchAlias); + log.debug('Source branch alias resolved', { ...config.cloneContext, alias: config.sourceStackBranchAlias, branch: config.sourceStackBranch }); } else { const targetStack = client.stack({ api_key: config.target_stack }); config.targetStackBranch = await getBranchFromAlias(targetStack, config.targetStackBranchAlias); + log.debug('Target branch alias resolved', { ...config.cloneContext, alias: config.targetStackBranchAlias, branch: config.targetStackBranch }); } } catch (error) { throw error; @@ -604,6 +655,7 @@ class CloneHandler { async cloneTypeSelection() { console.clear(); return new Promise(async (resolve, reject) => { + log.debug('Starting clone type selection', config.cloneContext); const choices = [ 'Structure (all modules except entries & assets)', 'Structure with content (all modules including entries & assets)', @@ -619,83 +671,139 @@ class CloneHandler { let successMsg; let selectedValue = {}; config['data'] = path.join(__dirname.split('src')[0], 'contents', config.sourceStackBranch || ''); + log.debug(`Clone data directory: ${config['data']}`, config.cloneContext); if (!config.cloneType) { + log.debug('Clone type not specified, prompting user for selection', config.cloneContext); selectedValue = await inquirer.prompt(cloneTypeSelection); + } else { + log.debug(`Using pre-configured clone type: ${config.cloneType}`, config.cloneContext); } if (config.cloneType === 'a' || selectedValue.type === 'Structure (all modules except entries & assets)') { config['modules'] = structureList; successMsg = 'Stack clone Structure completed'; + log.debug(`Clone type: Structure only. Modules to clone: ${structureList.join(', ')}`, config.cloneContext); } else { successMsg = 'Stack clone completed with structure and content'; + log.debug('Clone type: Structure with content (all modules)', config.cloneContext); } this.cmdImport() - .then(() => resolve(successMsg)) + .then(() => { + log.debug('Clone type selection and import completed successfully', config.cloneContext); + resolve(successMsg); + }) .catch(reject); }); } async cmdExport() { return new Promise((resolve, reject) => { + log.debug('Preparing export command', { ...config.cloneContext, sourceStack: config.source_stack, cloneType: config.cloneType }); // Creating export specific config by merging external configurations let exportConfig = Object.assign({}, cloneDeep(config), { ...config?.export }); delete exportConfig.import; delete exportConfig.export; - const cmd = ['-k', exportConfig.source_stack, '-d', __dirname.split('src')[0] + 'contents']; + const exportDir = __dirname.split('src')[0] + 'contents'; + log.debug(`Export directory: ${exportDir}`, config.cloneContext); + const cmd = ['-k', exportConfig.source_stack, '-d', exportDir]; + if (exportConfig.cloneType === 'a') { exportConfig.filteredModules = ['stack'].concat(structureList); + log.debug(`Filtered modules for structure-only export: ${exportConfig.filteredModules.join(', ')}`, config.cloneContext); } if (exportConfig.source_alias) { cmd.push('-a', exportConfig.source_alias); + log.debug(`Using source alias: ${exportConfig.source_alias}`, config.cloneContext); } if (exportConfig.sourceStackBranch) { cmd.push('--branch', exportConfig.sourceStackBranch); + log.debug(`Using source branch: ${exportConfig.sourceStackBranch}`, config.cloneContext); } - if (exportConfig.forceStopMarketplaceAppsPrompt) cmd.push('-y'); + if (exportConfig.forceStopMarketplaceAppsPrompt) { + cmd.push('-y'); + log.debug('Force stop marketplace apps prompt enabled', config.cloneContext); + } + const configFilePath = path.join(__dirname, 'dummyConfig.json'); cmd.push('-c'); - cmd.push(path.join(__dirname, 'dummyConfig.json')); - - fs.writeFileSync(path.join(__dirname, 'dummyConfig.json'), JSON.stringify(exportConfig)); + cmd.push(configFilePath); + log.debug(`Writing export config to: ${configFilePath}`, config.cloneContext); + + fs.writeFileSync(configFilePath, JSON.stringify(exportConfig)); + log.debug('Export command prepared', config.cloneContext, { + cmd: cmd.join(' '), + exportDir, + sourceStack: exportConfig.source_stack, + branch: exportConfig.sourceStackBranch + }); + log.debug('Running export command', config.cloneContext, { cmd }); let exportData = exportCmd.run(cmd); - exportData.then(() => resolve(true)).catch(reject); + exportData.then(() => { + log.debug('Export command completed successfully', config.cloneContext); + resolve(true); + }).catch((error) => { + reject(error); + }); }); } async cmdImport() { return new Promise(async (resolve, _reject) => { + log.debug('Preparing import command', { ...config.cloneContext, targetStack: config.target_stack, targetBranch: config.targetStackBranch }); // Creating export specific config by merging external configurations let importConfig = Object.assign({}, cloneDeep(config), { ...config?.import }); delete importConfig.import; delete importConfig.export; - const cmd = ['-c', path.join(__dirname, 'dummyConfig.json')]; + const configFilePath = path.join(__dirname, 'dummyConfig.json'); + const cmd = ['-c', configFilePath]; if (importConfig.destination_alias) { cmd.push('-a', importConfig.destination_alias); + log.debug(`Using destination alias: ${importConfig.destination_alias}`, config.cloneContext); } if (!importConfig.data && importConfig.sourceStackBranch) { - cmd.push('-d', path.join(importConfig.pathDir, importConfig.sourceStackBranch)); + const dataPath = path.join(importConfig.pathDir, importConfig.sourceStackBranch); + cmd.push('-d', dataPath); + log.debug(`Import data path: ${dataPath}`, config.cloneContext); } if (importConfig.targetStackBranch) { cmd.push('--branch', importConfig.targetStackBranch); + log.debug(`Using target branch: ${importConfig.targetStackBranch}`, config.cloneContext); } if (importConfig.importWebhookStatus) { cmd.push('--import-webhook-status', importConfig.importWebhookStatus); + log.debug(`Import webhook status: ${importConfig.importWebhookStatus}`, config.cloneContext); } - if (importConfig.skipAudit) cmd.push('--skip-audit'); + if (importConfig.skipAudit) { + cmd.push('--skip-audit'); + log.debug('Skip audit flag enabled', config.cloneContext); + } - if (importConfig.forceStopMarketplaceAppsPrompt) cmd.push('-y'); + if (importConfig.forceStopMarketplaceAppsPrompt) { + cmd.push('-y'); + log.debug('Force stop marketplace apps prompt enabled', config.cloneContext); + } - fs.writeFileSync(path.join(__dirname, 'dummyConfig.json'), JSON.stringify(importConfig)); + log.debug(`Writing import config to: ${configFilePath}`, config.cloneContext); + fs.writeFileSync(configFilePath, JSON.stringify(importConfig)); + log.debug('Import command prepared', config.cloneContext, { + cmd: cmd.join(' '), + targetStack: importConfig.target_stack, + targetBranch: importConfig.targetStackBranch, + dataPath: importConfig.data || path.join(importConfig.pathDir, importConfig.sourceStackBranch) + }); + log.debug('Running import command', config.cloneContext, { cmd }); await importCmd.run(cmd); - fs.writeFileSync(path.join(__dirname, 'dummyConfig.json'), JSON.stringify({})); + log.debug('Import command completed successfully', config.cloneContext); + log.debug('Clearing import config file', config.cloneContext); + fs.writeFileSync(configFilePath, JSON.stringify({})); return resolve(); }); } diff --git a/packages/contentstack-clone/src/lib/util/log.js b/packages/contentstack-clone/src/lib/util/log.js deleted file mode 100644 index 7d806cd9a8..0000000000 --- a/packages/contentstack-clone/src/lib/util/log.js +++ /dev/null @@ -1,105 +0,0 @@ -/*! - * Contentstack Import - * Copyright (c) 2024 Contentstack LLC - * MIT Licensed - */ - -var winston = require('winston'); -var path = require('path'); -var mkdirp = require('mkdirp'); -const { pathValidator, sanitizePath } = require('@contentstack/cli-utilities'); -var slice = Array.prototype.slice; - -function returnString(args) { - var returnStr = ''; - if (args && args.length) { - returnStr = args - .map(function (item) { - if (item && typeof item === 'object') { - return JSON.stringify(item); - } - return item; - }) - .join(' ') - .trim(); - } - return returnStr; -} - -var myCustomLevels = { - levels: { - error: 0, - warn: 1, - info: 2, - debug: 3, - }, - colors: { - info: 'blue', - debug: 'green', - warn: 'yellow', - error: 'red', - }, -}; - -function init(_logPath, logfileName) { - var logsDir = pathValidator(path.resolve(sanitizePath(_logPath), 'logs', 'import')); - // Create dir if doesn't already exist - mkdirp.sync(logsDir); - var logPath = path.join(sanitizePath(logsDir), pathValidator(sanitizePath(logfileName)) + '.log'); - - var transports = [ - new winston.transports.File({ - filename: logPath, - maxFiles: 20, - maxsize: 1000000, - tailable: true, - json: true, - }), - ]; - - transports.push(new winston.transports.Console()); - - var logger = winston.createLogger({ - transports: transports, - levels: myCustomLevels.levels, - }); - - return { - log: function () { - var args = slice.call(arguments); - var logString = returnString(args); - if (logString) { - logger.log('info', logString); - } - }, - warn: function () { - var args = slice.call(arguments); - var logString = returnString(args); - if (logString) { - logger.log('warn', logString); - } - }, - error: function () { - var args = slice.call(arguments); - var logString = returnString(args); - if (logString) { - logger.log('error', logString); - } - }, - debug: function () { - var args = slice.call(arguments); - var logString = returnString(args); - if (logString) { - logger.log('debug', logString); - } - }, - }; -} - -exports.addlogs = async (config, message, type) => { - if (type !== 'error') { - init(config.oldPath, type).log(message); - } else { - init(config.oldPath, type).error(message); - } -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00cc53717e..47df0370af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -398,9 +398,8 @@ importers: oclif: ^4.17.46 ora: ^5.4.1 prompt: ^1.3.0 - rimraf: ^5.0.10 + rimraf: ^6.1.0 sinon: ^19.0.5 - winston: ^3.17.0 dependencies: '@colors/colors': 1.6.0 '@contentstack/cli-cm-export': link:../contentstack-export @@ -415,8 +414,7 @@ importers: merge: 2.1.1 ora: 5.4.1 prompt: 1.3.0 - rimraf: 5.0.10 - winston: 3.18.3 + rimraf: 6.1.0 devDependencies: '@oclif/test': 4.1.15_@oclif+core@4.8.0 chai: 4.5.0 @@ -3677,14 +3675,12 @@ packages: /@isaacs/balanced-match/4.0.1: resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} - dev: true /@isaacs/brace-expansion/5.0.0: resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} engines: {node: 20 || >=22} dependencies: '@isaacs/balanced-match': 4.0.1 - dev: true /@isaacs/cliui/8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} @@ -4163,6 +4159,7 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} requiresBuild: true + dev: true optional: true /@pnpm/config.env-replace/1.1.0: @@ -10315,6 +10312,20 @@ packages: minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + dev: true + + /glob/11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + hasBin: true + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.1.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.1 + dev: false /glob/7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -11301,6 +11312,14 @@ packages: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 + dev: true + + /jackspeak/4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} + dependencies: + '@isaacs/cliui': 8.0.2 + dev: false /jake/10.9.4: resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} @@ -12279,6 +12298,11 @@ packages: /lru-cache/10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + /lru-cache/11.2.2: + resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + engines: {node: 20 || >=22} + dev: false + /lru-cache/5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: @@ -12439,7 +12463,6 @@ packages: engines: {node: 20 || >=22} dependencies: '@isaacs/brace-expansion': 5.0.0 - dev: true /minimatch/3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -13320,6 +13343,15 @@ packages: dependencies: lru-cache: 10.4.3 minipass: 7.1.2 + dev: true + + /path-scurry/2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} + dependencies: + lru-cache: 11.2.2 + minipass: 7.1.2 + dev: false /path-to-regexp/0.1.12: resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} @@ -13889,6 +13921,7 @@ packages: hasBin: true dependencies: glob: 10.5.0 + dev: true /rollup/4.53.3: resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==}