diff --git a/.talismanrc b/.talismanrc index 3f80d4430..2da7e187e 100644 --- a/.talismanrc +++ b/.talismanrc @@ -1,4 +1,4 @@ fileignoreconfig: - filename: pnpm-lock.yaml - checksum: ce5abaaafcfa33bb71e4af691eb1f5786b7851bfb7f936712374251cbffe2a32 + checksum: 48a1b9aca69c8decfed0f5333c8f4a8fa4689f1cb9c00a91dd18532418ea910f version: '1.0' diff --git a/packages/contentstack-bootstrap/package.json b/packages/contentstack-bootstrap/package.json index d0e8e55df..d4589a633 100644 --- a/packages/contentstack-bootstrap/package.json +++ b/packages/contentstack-bootstrap/package.json @@ -1,7 +1,7 @@ { "name": "@contentstack/cli-cm-bootstrap", "description": "Bootstrap contentstack apps", - "version": "1.19.1", + "version": "1.19.2", "author": "Contentstack", "bugs": "https://github.com/contentstack/cli/issues", "scripts": { @@ -16,7 +16,7 @@ "test:report": "nyc --reporter=lcov mocha \"test/**/*.test.js\"" }, "dependencies": { - "@contentstack/cli-cm-seed": "~1.15.1", + "@contentstack/cli-cm-seed": "~1.15.2", "@contentstack/cli-command": "~1.8.1", "@contentstack/cli-config": "~1.20.2", "@contentstack/cli-utilities": "~1.18.2", diff --git a/packages/contentstack-clone/package.json b/packages/contentstack-clone/package.json index b305d768a..242924f8f 100644 --- a/packages/contentstack-clone/package.json +++ b/packages/contentstack-clone/package.json @@ -1,13 +1,13 @@ { "name": "@contentstack/cli-cm-clone", "description": "Contentstack stack clone plugin", - "version": "1.21.2", + "version": "1.21.3", "author": "Contentstack", "bugs": "https://github.com/rohitmishra209/cli-cm-clone/issues", "dependencies": { "@colors/colors": "^1.6.0", - "@contentstack/cli-cm-export": "~1.24.2", - "@contentstack/cli-cm-import": "~1.32.2", + "@contentstack/cli-cm-export": "~1.25.0", + "@contentstack/cli-cm-import": "~1.33.0", "@contentstack/cli-command": "~1.8.1", "@contentstack/cli-utilities": "~1.18.2", "@oclif/core": "^4.10.5", diff --git a/packages/contentstack-export/package.json b/packages/contentstack-export/package.json index 610f2d0e0..94e9024c2 100644 --- a/packages/contentstack-export/package.json +++ b/packages/contentstack-export/package.json @@ -1,7 +1,7 @@ { "name": "@contentstack/cli-cm-export", "description": "Contentstack CLI plugin to export content from stack", - "version": "1.24.2", + "version": "1.25.0", "author": "Contentstack", "bugs": "https://github.com/contentstack/cli/issues", "dependencies": { diff --git a/packages/contentstack-export/src/commands/cm/stacks/export.ts b/packages/contentstack-export/src/commands/cm/stacks/export.ts index 15f88c573..4bd542e6d 100644 --- a/packages/contentstack-export/src/commands/cm/stacks/export.ts +++ b/packages/contentstack-export/src/commands/cm/stacks/export.ts @@ -121,14 +121,14 @@ export default class ExportCommand extends Command { try { const { flags } = await this.parse(ExportCommand); const exportConfig = await setupExportConfig(flags); - + // Store apiKey in configHandler for session.json (return value not needed) createLogContext( this.context?.info?.command || 'cm:stacks:export', exportConfig.apiKey, - exportConfig.authenticationMethod + exportConfig.authenticationMethod, ); - + // For log entries, only pass module (other fields are in session.json) exportConfig.context = { module: '' }; //log.info(`Using Cli Version: ${this.context?.cliVersion}`, exportConfig.context); @@ -143,9 +143,7 @@ export default class ExportCommand extends Command { if (!exportConfig.branches?.length) { writeExportMetaFile(exportConfig); } - log.success( - `The content of the stack ${exportConfig.apiKey} has been exported successfully!`, - ); + log.success(`The content of the stack ${exportConfig.apiKey} has been exported successfully!`); log.info(`The exported content has been stored at '${exportDir}'.`, exportConfig.context); log.success(`The log has been stored at '${getLogPath()}'.`, exportConfig.context); } catch (error) { @@ -154,7 +152,6 @@ export default class ExportCommand extends Command { } } - // Assign values to exportConfig private assignExportConfig(exportConfig: ExportConfig): void { // Note setting host to create cma client diff --git a/packages/contentstack-export/src/config/index.ts b/packages/contentstack-export/src/config/index.ts index 85aa028fa..e3c4d12a2 100644 --- a/packages/contentstack-export/src/config/index.ts +++ b/packages/contentstack-export/src/config/index.ts @@ -36,6 +36,7 @@ const config: DefaultConfig = { 'content-types', 'custom-roles', 'workflows', + 'publishing-rules', 'personalize', 'entries', 'labels', @@ -86,6 +87,11 @@ const config: DefaultConfig = { fileName: 'workflows.json', invalidKeys: ['stackHeaders', 'urlPath', 'created_at', 'updated_at', 'created_by', 'updated_by'], }, + 'publishing-rules': { + dirName: 'workflows', + fileName: 'publishing-rules.json', + invalidKeys: ['stackHeaders', 'urlPath', 'created_at', 'updated_at', 'created_by', 'updated_by'], + }, globalfields: { dirName: 'global_fields', fileName: 'globalfields.json', diff --git a/packages/contentstack-export/src/export/modules/content-types.ts b/packages/contentstack-export/src/export/modules/content-types.ts index ad571929d..7baf2b4ca 100644 --- a/packages/contentstack-export/src/export/modules/content-types.ts +++ b/packages/contentstack-export/src/export/modules/content-types.ts @@ -1,11 +1,5 @@ import * as path from 'path'; -import { - ContentstackClient, - handleAndLogError, - messageHandler, - log, - sanitizePath, -} from '@contentstack/cli-utilities'; +import { ContentstackClient, handleAndLogError, messageHandler, log, sanitizePath } from '@contentstack/cli-utilities'; import BaseClass from './base-class'; import { fsUtil, executeTask } from '../../utils'; @@ -86,7 +80,9 @@ export default class ContentTypesExport extends BaseClass { const contentTypeSearchResponse = await this.stackAPIClient.contentType().query(this.qs).find(); log.debug( - `Fetched ${contentTypeSearchResponse.items?.length || 0} content types out of total ${contentTypeSearchResponse.count}`, + `Fetched ${contentTypeSearchResponse.items?.length || 0} content types out of total ${ + contentTypeSearchResponse.count + }`, this.exportConfig.context, ); diff --git a/packages/contentstack-export/src/export/modules/custom-roles.ts b/packages/contentstack-export/src/export/modules/custom-roles.ts index 1d9d10529..6db21485a 100644 --- a/packages/contentstack-export/src/export/modules/custom-roles.ts +++ b/packages/contentstack-export/src/export/modules/custom-roles.ts @@ -37,23 +37,26 @@ export default class ExportCustomRoles extends BaseClass { this.customRolesConfig.dirName, ); log.debug(`Custom roles folder path is: ${this.rolesFolderPath}`, this.exportConfig.context); - + await fsUtil.makeDirectory(this.rolesFolderPath); log.debug('Custom roles directory created.', this.exportConfig.context); - + this.customRolesLocalesFilepath = pResolve(this.rolesFolderPath, this.customRolesConfig.customRolesLocalesFileName); log.debug(`Custom roles locales file path is: ${this.customRolesLocalesFilepath}`, this.exportConfig.context); - + await this.getCustomRoles(); await this.getLocales(); await this.getCustomRolesLocales(); - - log.debug(`Custom roles export completed. Total custom roles: ${Object.keys(this.customRoles).length}`, this.exportConfig.context); + + log.debug( + `Custom roles export completed. Total custom roles: ${Object.keys(this.customRoles).length}`, + this.exportConfig.context, + ); } async getCustomRoles(): Promise { log.debug('Fetching all roles from the stack...', this.exportConfig.context); - + const roles = await this.stack .role() .fetchAll({ include_rules: true, include_permissions: true }) @@ -65,9 +68,12 @@ export default class ExportCustomRoles extends BaseClass { log.debug('An error occurred while fetching roles.', this.exportConfig.context); return handleAndLogError(err, { ...this.exportConfig.context }); }); - + const customRoles = roles.items.filter((role: any) => !this.existingRoles[role.name]); - log.debug(`Found ${customRoles.length} custom roles from ${roles.items?.length || 0} total roles.`, this.exportConfig.context); + log.debug( + `Found ${customRoles.length} custom roles from ${roles.items?.length || 0} total roles.`, + this.exportConfig.context, + ); if (!customRoles.length) { log.info(messageHandler.parse('ROLES_NO_CUSTOM_ROLES'), this.exportConfig.context); @@ -79,7 +85,7 @@ export default class ExportCustomRoles extends BaseClass { log.info(messageHandler.parse('ROLES_EXPORTING_ROLE', role?.name), this.exportConfig.context); this.customRoles[role.uid] = role; }); - + const customRolesFilePath = pResolve(this.rolesFolderPath, this.customRolesConfig.fileName); log.debug(`Writing custom roles to: ${customRolesFilePath}.`, this.exportConfig.context); fsUtil.writeFile(customRolesFilePath, this.customRoles); @@ -87,7 +93,7 @@ export default class ExportCustomRoles extends BaseClass { async getLocales() { log.debug('Fetching locales for custom roles mapping...', this.exportConfig.context); - + const locales = await this.stack .locale() .query({}) @@ -100,25 +106,28 @@ export default class ExportCustomRoles extends BaseClass { log.debug('An error occurred while fetching locales.', this.exportConfig.context); return handleAndLogError(err, { ...this.exportConfig.context }); }); - + for (const locale of locales.items) { log.debug(`Mapping locale: ${locale?.name} (${locale?.uid})`, this.exportConfig.context); this.sourceLocalesMap[locale.uid] = locale; } - + log.debug(`Mapped ${Object.keys(this.sourceLocalesMap).length} source locales.`, this.exportConfig.context); } async getCustomRolesLocales() { log.debug('Processing custom roles locales mapping...', this.exportConfig.context); - + for (const role of values(this.customRoles)) { const customRole = role as Record; log.debug(`Processing locales for custom role: ${customRole?.name}`, this.exportConfig.context); - + const rulesLocales = find(customRole.rules, (rule: any) => rule.module === 'locale'); if (rulesLocales?.locales?.length) { - log.debug(`Found ${rulesLocales.locales.length} locales for the role: ${customRole?.name}.`, this.exportConfig.context); + log.debug( + `Found ${rulesLocales.locales.length} locales for the role: ${customRole?.name}.`, + this.exportConfig.context, + ); forEach(rulesLocales.locales, (locale: any) => { log.debug(`Adding locale ${locale} to the custom roles mapping.`, this.exportConfig.context); this.localesMap[locale] = 1; @@ -128,7 +137,7 @@ export default class ExportCustomRoles extends BaseClass { if (keys(this.localesMap)?.length) { log.debug(`Processing ${Object.keys(this.localesMap).length} mapped locales.`, this.exportConfig.context); - + for (const locale in this.localesMap) { if (this.sourceLocalesMap[locale] !== undefined) { const sourceLocale = this.sourceLocalesMap[locale] as Record; @@ -137,7 +146,7 @@ export default class ExportCustomRoles extends BaseClass { } this.localesMap[locale] = this.sourceLocalesMap[locale]; } - + log.debug(`Writing custom roles locales to: ${this.customRolesLocalesFilepath}.`, this.exportConfig.context); fsUtil.writeFile(this.customRolesLocalesFilepath, this.localesMap); } else { diff --git a/packages/contentstack-export/src/export/modules/environments.ts b/packages/contentstack-export/src/export/modules/environments.ts index 85e9f8af3..43c3745df 100644 --- a/packages/contentstack-export/src/export/modules/environments.ts +++ b/packages/contentstack-export/src/export/modules/environments.ts @@ -35,7 +35,7 @@ export default class ExportEnvironments extends BaseClass { await fsUtil.makeDirectory(this.environmentsFolderPath); log.debug('Environments directory created.', this.exportConfig.context); - + await this.getEnvironments(); log.debug(`Retrieved ${Object.keys(this.environments).length} environments.`, this.exportConfig.context); @@ -59,9 +59,9 @@ export default class ExportEnvironments extends BaseClass { } else { log.debug('Fetching environments with initial query...', this.exportConfig.context); } - + log.debug(`Query parameters: ${JSON.stringify(this.qs)}`, this.exportConfig.context); - + await this.stack .environment() .query(this.qs) @@ -69,7 +69,7 @@ export default class ExportEnvironments extends BaseClass { .then(async (data: any) => { const { items, count } = data; log.debug(`Fetched ${items?.length || 0} environments out of ${count} total.`, this.exportConfig.context); - + if (items?.length) { log.debug(`Processing ${items.length} environments.`, this.exportConfig.context); this.sanitizeAttribs(items); @@ -92,16 +92,19 @@ export default class ExportEnvironments extends BaseClass { sanitizeAttribs(environments: Record[]) { log.debug(`Sanitizing ${environments.length} environments...`, this.exportConfig.context); - + for (let index = 0; index < environments?.length; index++) { const extUid = environments[index].uid; const envName = environments[index]?.name; log.debug(`Processing environment: ${envName} (${extUid})`, this.exportConfig.context); - + this.environments[extUid] = omit(environments[index], ['ACL']); - log.success(messageHandler.parse('ENVIRONMENT_EXPORT_SUCCESS', envName ), this.exportConfig.context); + log.success(messageHandler.parse('ENVIRONMENT_EXPORT_SUCCESS', envName), this.exportConfig.context); } - - log.debug(`Sanitization complete. Total environments processed: ${Object.keys(this.environments).length}`, this.exportConfig.context); + + log.debug( + `Sanitization complete. Total environments processed: ${Object.keys(this.environments).length}`, + this.exportConfig.context, + ); } } diff --git a/packages/contentstack-export/src/export/modules/extensions.ts b/packages/contentstack-export/src/export/modules/extensions.ts index 5da610ec2..efe3e1060 100644 --- a/packages/contentstack-export/src/export/modules/extensions.ts +++ b/packages/contentstack-export/src/export/modules/extensions.ts @@ -27,7 +27,7 @@ export default class ExportExtensions extends BaseClass { async start(): Promise { log.debug('Starting extensions export process...', this.exportConfig.context); - + this.extensionsFolderPath = pResolve( this.exportConfig.data, this.exportConfig.branchName || '', @@ -37,7 +37,7 @@ export default class ExportExtensions extends BaseClass { await fsUtil.makeDirectory(this.extensionsFolderPath); log.debug('Extensions directory created.', this.exportConfig.context); - + await this.getExtensions(); log.debug(`Retrieved ${Object.keys(this.extensions).length} extensions.`, this.exportConfig.context); @@ -48,7 +48,7 @@ export default class ExportExtensions extends BaseClass { log.debug(`Writing extensions to: ${extensionsFilePath}.`, this.exportConfig.context); fsUtil.writeFile(extensionsFilePath, this.extensions); log.success( - messageHandler.parse('EXTENSION_EXPORT_COMPLETE', Object.keys(this.extensions).length ), + messageHandler.parse('EXTENSION_EXPORT_COMPLETE', Object.keys(this.extensions).length), this.exportConfig.context, ); } @@ -61,9 +61,9 @@ export default class ExportExtensions extends BaseClass { } else { log.debug('Fetching extensions with initial query...', this.exportConfig.context); } - + log.debug(`Query parameters: ${JSON.stringify(this.qs)}.`, this.exportConfig.context); - + await this.stack .extension() .query(this.qs) @@ -71,7 +71,7 @@ export default class ExportExtensions extends BaseClass { .then(async (data: any) => { const { items, count } = data; log.debug(`Fetched ${items?.length || 0} extensions out of ${count}.`, this.exportConfig.context); - + if (items?.length) { log.debug(`Processing ${items.length} extensions...`, this.exportConfig.context); this.sanitizeAttribs(items); @@ -94,16 +94,19 @@ export default class ExportExtensions extends BaseClass { sanitizeAttribs(extensions: Record[]) { log.debug(`Sanitizing ${extensions.length} extensions...`, this.exportConfig.context); - + for (let index = 0; index < extensions?.length; index++) { const extUid = extensions[index].uid; const extTitle = extensions[index]?.title; log.debug(`Processing extension: '${extTitle}' (UID: ${extUid})...`, this.exportConfig.context); - + this.extensions[extUid] = omit(extensions[index], ['SYS_ACL']); log.info(messageHandler.parse('EXTENSION_EXPORT_SUCCESS', extTitle), this.exportConfig.context); } - - log.debug(`Sanitization complete. Total extensions processed: ${Object.keys(this.extensions).length}.`, this.exportConfig.context); + + log.debug( + `Sanitization complete. Total extensions processed: ${Object.keys(this.extensions).length}.`, + this.exportConfig.context, + ); } } diff --git a/packages/contentstack-export/src/export/modules/global-fields.ts b/packages/contentstack-export/src/export/modules/global-fields.ts index 421665cfc..aae016f7d 100644 --- a/packages/contentstack-export/src/export/modules/global-fields.ts +++ b/packages/contentstack-export/src/export/modules/global-fields.ts @@ -1,11 +1,5 @@ import * as path from 'path'; -import { - ContentstackClient, - handleAndLogError, - messageHandler, - log, - sanitizePath, -} from '@contentstack/cli-utilities'; +import { ContentstackClient, handleAndLogError, messageHandler, log, sanitizePath } from '@contentstack/cli-utilities'; import { fsUtil } from '../../utils'; import { ExportConfig, ModuleClassParams } from '../../types'; @@ -56,17 +50,17 @@ export default class GlobalFieldsExport extends BaseClass { async start() { try { log.debug('Starting export process for global fields...', this.exportConfig.context); - log.debug(`Global fields directory path: '${this.globalFieldsDirPath}'`, this.exportConfig.context); + log.debug(`Global fields directory path: '${this.globalFieldsDirPath}'`, this.exportConfig.context); await fsUtil.makeDirectory(this.globalFieldsDirPath); log.debug('Created global fields directory.', this.exportConfig.context); - + await this.getGlobalFields(); log.debug(`Retrieved ${this.globalFields.length} global fields.`, this.exportConfig.context); - + const globalFieldsFilePath = path.join(this.globalFieldsDirPath, this.globalFieldsConfig.fileName); log.debug(`Writing global fields to: '${globalFieldsFilePath}'`, this.exportConfig.context); fsUtil.writeFile(globalFieldsFilePath, this.globalFields); - + log.success( messageHandler.parse('GLOBAL_FIELDS_EXPORT_COMPLETE', this.globalFields.length), this.exportConfig.context, @@ -83,11 +77,16 @@ export default class GlobalFieldsExport extends BaseClass { log.debug(`Fetching global fields with skip: ${skip}.`, this.exportConfig.context); } log.debug(`Query parameters: ${JSON.stringify(this.qs)}.`, this.exportConfig.context); - + let globalFieldsFetchResponse = await this.stackAPIClient.globalField({ api_version: '3.2' }).query(this.qs).find(); - - log.debug(`Fetched ${globalFieldsFetchResponse.items?.length || 0} global fields out of ${globalFieldsFetchResponse.count}.`, this.exportConfig.context); - + + log.debug( + `Fetched ${globalFieldsFetchResponse.items?.length || 0} global fields out of ${ + globalFieldsFetchResponse.count + }.`, + this.exportConfig.context, + ); + if (Array.isArray(globalFieldsFetchResponse.items) && globalFieldsFetchResponse.items.length > 0) { log.debug(`Processing ${globalFieldsFetchResponse.items.length} global fields...`, this.exportConfig.context); this.sanitizeAttribs(globalFieldsFetchResponse.items); @@ -105,10 +104,10 @@ export default class GlobalFieldsExport extends BaseClass { sanitizeAttribs(globalFields: Record[]) { log.debug(`Sanitizing ${globalFields.length} global fields...`, this.exportConfig.context); - + globalFields.forEach((globalField: Record) => { log.debug(`Processing global field: '${globalField.uid || 'unknown'}'...`, this.exportConfig.context); - + for (let key in globalField) { if (this.globalFieldsConfig.validKeys.indexOf(key) === -1) { delete globalField[key]; @@ -116,7 +115,10 @@ export default class GlobalFieldsExport extends BaseClass { } this.globalFields.push(globalField); }); - - log.debug(`Sanitization complete. Total global fields processed: ${this.globalFields.length}.`, this.exportConfig.context); + + log.debug( + `Sanitization complete. Total global fields processed: ${this.globalFields.length}.`, + this.exportConfig.context, + ); } } diff --git a/packages/contentstack-export/src/export/modules/index.ts b/packages/contentstack-export/src/export/modules/index.ts index f13bc4fa3..ca0896b44 100644 --- a/packages/contentstack-export/src/export/modules/index.ts +++ b/packages/contentstack-export/src/export/modules/index.ts @@ -11,7 +11,7 @@ export default async function startModuleExport(modulePayload: ModuleClassParams ...modulePayload.exportConfig.context, module: modulePayload.moduleName, }); - throw error; + throw error; } } diff --git a/packages/contentstack-export/src/export/modules/labels.ts b/packages/contentstack-export/src/export/modules/labels.ts index 414f13077..c639aca60 100644 --- a/packages/contentstack-export/src/export/modules/labels.ts +++ b/packages/contentstack-export/src/export/modules/labels.ts @@ -26,7 +26,7 @@ export default class ExportLabels extends BaseClass { async start(): Promise { log.debug('Starting export process for labels...', this.exportConfig.context); - + this.labelsFolderPath = pResolve( this.exportConfig.data, this.exportConfig.branchName || '', @@ -36,10 +36,10 @@ export default class ExportLabels extends BaseClass { await fsUtil.makeDirectory(this.labelsFolderPath); log.debug('Created labels directory.', this.exportConfig.context); - + await this.getLabels(); log.debug(`Retrieved ${Object.keys(this.labels).length} labels.`, this.exportConfig.context); - + if (this.labels === undefined || isEmpty(this.labels)) { log.info(messageHandler.parse('LABELS_NOT_FOUND'), this.exportConfig.context); } else { @@ -60,9 +60,9 @@ export default class ExportLabels extends BaseClass { } else { log.debug('Fetching labels with initial query...', this.exportConfig.context); } - + log.debug(`Query parameters: ${JSON.stringify(this.qs)}.`, this.exportConfig.context); - + await this.stack .label() .query(this.qs) @@ -70,7 +70,7 @@ export default class ExportLabels extends BaseClass { .then(async (data: any) => { const { items, count } = data; log.debug(`Fetched ${items?.length || 0} labels out of ${count}.`, this.exportConfig.context); - + if (items?.length) { log.debug(`Processing ${items.length} labels...`, this.exportConfig.context); this.sanitizeAttribs(items); @@ -93,16 +93,19 @@ export default class ExportLabels extends BaseClass { sanitizeAttribs(labels: Record[]) { log.debug(`Sanitizing ${labels.length} labels...`, this.exportConfig.context); - + for (let index = 0; index < labels?.length; index++) { const labelUid = labels[index].uid; const labelName = labels[index]?.name; log.debug(`Processing label: '${labelName}' (UID: ${labelUid})...`, this.exportConfig.context); - + this.labels[labelUid] = omit(labels[index], this.labelConfig.invalidKeys); log.info(messageHandler.parse('LABEL_EXPORT_SUCCESS', labelName), this.exportConfig.context); } - - log.debug(`Sanitization complete. Total labels processed: ${Object.keys(this.labels).length}.`, this.exportConfig.context); + + log.debug( + `Sanitization complete. Total labels processed: ${Object.keys(this.labels).length}.`, + this.exportConfig.context, + ); } } diff --git a/packages/contentstack-export/src/export/modules/locales.ts b/packages/contentstack-export/src/export/modules/locales.ts index 97f8d16fe..4ff73240b 100644 --- a/packages/contentstack-export/src/export/modules/locales.ts +++ b/packages/contentstack-export/src/export/modules/locales.ts @@ -1,11 +1,5 @@ import * as path from 'path'; -import { - ContentstackClient, - handleAndLogError, - messageHandler, - log, - sanitizePath, -} from '@contentstack/cli-utilities'; +import { ContentstackClient, handleAndLogError, messageHandler, log, sanitizePath } from '@contentstack/cli-utilities'; import { fsUtil } from '../../utils'; import BaseClass from './base-class'; @@ -61,22 +55,27 @@ export default class LocaleExport extends BaseClass { try { log.debug('Starting export process for locales...', this.exportConfig.context); log.debug(`Locales path: '${this.localesPath}'`, this.exportConfig.context); - + await fsUtil.makeDirectory(this.localesPath); log.debug('Created locales directory.', this.exportConfig.context); - + await this.getLocales(); - log.debug(`Retrieved ${Object.keys(this.locales).length} locales and ${Object.keys(this.masterLocale).length} master locales.`, this.exportConfig.context); - + log.debug( + `Retrieved ${Object.keys(this.locales).length} locales and ${ + Object.keys(this.masterLocale).length + } master locales.`, + this.exportConfig.context, + ); + const localesFilePath = path.join(this.localesPath, this.localeConfig.fileName); const masterLocaleFilePath = path.join(this.localesPath, this.masterLocaleConfig.fileName); - + log.debug(`Writing locales to: '${localesFilePath}'`, this.exportConfig.context); fsUtil.writeFile(localesFilePath, this.locales); - + log.debug(`Writing master locale to: '${masterLocaleFilePath}'`, this.exportConfig.context); fsUtil.writeFile(masterLocaleFilePath, this.masterLocale); - + log.success( messageHandler.parse( 'LOCALES_EXPORT_COMPLETE', @@ -97,15 +96,18 @@ export default class LocaleExport extends BaseClass { log.debug(`Fetching locales with skip: ${skip}.`, this.exportConfig.context); } log.debug(`Query parameters: ${JSON.stringify(this.qs)}.`, this.exportConfig.context); - + let localesFetchResponse = await this.stackAPIClient.locale().query(this.qs).find(); - - log.debug(`Fetched ${localesFetchResponse.items?.length || 0} locales out of ${localesFetchResponse.count}.`, this.exportConfig.context); - + + log.debug( + `Fetched ${localesFetchResponse.items?.length || 0} locales out of ${localesFetchResponse.count}.`, + this.exportConfig.context, + ); + if (Array.isArray(localesFetchResponse.items) && localesFetchResponse.items.length > 0) { log.debug(`Processing ${localesFetchResponse.items.length} locales...`, this.exportConfig.context); this.sanitizeAttribs(localesFetchResponse.items); - + skip += this.localeConfig.limit || 100; if (skip > localesFetchResponse.count) { log.debug('Completed fetching all locales.', this.exportConfig.context); @@ -120,7 +122,7 @@ export default class LocaleExport extends BaseClass { sanitizeAttribs(locales: Record[]) { log.debug(`Sanitizing ${locales.length} locales...`, this.exportConfig.context); - + locales.forEach((locale: Record) => { for (let key in locale) { if (this.localeConfig.requiredKeys.indexOf(key) === -1) { @@ -136,7 +138,12 @@ export default class LocaleExport extends BaseClass { this.locales[locale.uid] = locale; } }); - - log.debug(`Sanitization complete. Master locales: ${Object.keys(this.masterLocale).length}, Regular locales: ${Object.keys(this.locales).length}.`, this.exportConfig.context); + + log.debug( + `Sanitization complete. Master locales: ${Object.keys(this.masterLocale).length}, Regular locales: ${ + Object.keys(this.locales).length + }.`, + this.exportConfig.context, + ); } } diff --git a/packages/contentstack-export/src/export/modules/marketplace-apps.ts b/packages/contentstack-export/src/export/modules/marketplace-apps.ts index 094c05a22..50cd0b761 100644 --- a/packages/contentstack-export/src/export/modules/marketplace-apps.ts +++ b/packages/contentstack-export/src/export/modules/marketplace-apps.ts @@ -39,7 +39,7 @@ export default class ExportMarketplaceApps { async start(): Promise { log.debug('Starting export process for Marketplace Apps...', this.exportConfig.context); - + if (!isAuthenticated()) { cliux.print( 'WARNING!!! To export Marketplace apps, you must be logged in. Please check csdx auth:login --help to log in', @@ -54,13 +54,13 @@ export default class ExportMarketplaceApps { this.marketplaceAppConfig.dirName, ); log.debug(`Marketplace apps folder path: '${this.marketplaceAppPath}'`, this.exportConfig.context); - + await fsUtil.makeDirectory(this.marketplaceAppPath); log.debug('Created Marketplace Apps directory.', this.exportConfig.context); - + this.developerHubBaseUrl = this.exportConfig.developerHubBaseUrl || (await getDeveloperHubUrl(this.exportConfig)); log.debug(`Developer Hub base URL: '${this.developerHubBaseUrl}'`, this.exportConfig.context); - + this.exportConfig.org_uid = await getOrgUid(this.exportConfig); this.query = { target_uids: this.exportConfig.source_stack }; log.debug(`Organization UID: '${this.exportConfig.org_uid}'.`, this.exportConfig.context); @@ -90,10 +90,10 @@ export default class ExportMarketplaceApps { this.query.installation_uids = externalQuery.installation_uid?.$in?.join(','); } } - + await this.getStackSpecificApps(); log.debug(`Retrieved ${this.installedApps.length} stack-specific apps.`, this.exportConfig.context); - + await this.getAppManifestAndAppConfig(); log.debug('Completed app manifest and configuration processing.', this.exportConfig.context); @@ -109,7 +109,7 @@ export default class ExportMarketplaceApps { } return app; }); - + log.debug(`Processed ${this.installedApps.length} Marketplace Apps.`, this.exportConfig.context); } @@ -122,7 +122,7 @@ export default class ExportMarketplaceApps { log.info(messageHandler.parse('MARKETPLACE_APPS_NOT_FOUND'), this.exportConfig.context); } else { log.debug(`Processing ${this.installedApps.length} installed apps...`, this.exportConfig.context); - + for (const [index, app] of entries(this.installedApps)) { if (app.manifest.visibility === 'private') { log.debug(`Processing private app manifest: '${app.manifest.name}'...`, this.exportConfig.context); @@ -131,7 +131,10 @@ export default class ExportMarketplaceApps { } for (const [index, app] of entries(this.installedApps)) { - log.debug(`Processing app configurations for: '${app.manifest?.name || app.uid}'...`, this.exportConfig.context); + log.debug( + `Processing app configurations for: '${app.manifest?.name || app.uid}'...`, + this.exportConfig.context, + ); await this.getAppConfigurations(+index, app); } @@ -156,14 +159,20 @@ export default class ExportMarketplaceApps { * app's manifest. */ async getPrivateAppsManifest(index: number, appInstallation: Installation) { - log.debug(`Fetching private app manifest for: '${appInstallation.manifest.name}' (UID: ${appInstallation.manifest.uid})...`, this.exportConfig.context); - + log.debug( + `Fetching private app manifest for: '${appInstallation.manifest.name}' (UID: ${appInstallation.manifest.uid})...`, + this.exportConfig.context, + ); + const manifest = await this.appSdk .marketplace(this.exportConfig.org_uid) .app(appInstallation.manifest.uid) .fetch({ include_oauth: true }) .catch((error) => { - log.debug(`Failed to fetch private app manifest for: '${appInstallation.manifest.name}'.`, this.exportConfig.context); + log.debug( + `Failed to fetch private app manifest for: '${appInstallation.manifest.name}'.`, + this.exportConfig.context, + ); handleAndLogError( error, { @@ -174,7 +183,10 @@ export default class ExportMarketplaceApps { }); if (manifest) { - log.debug(`Successfully fetched private app manifest for: '${appInstallation.manifest.name}'.`, this.exportConfig.context); + log.debug( + `Successfully fetched private app manifest for: '${appInstallation.manifest.name}'.`, + this.exportConfig.context, + ); this.installedApps[index].manifest = manifest as unknown as Manifest; } } @@ -205,7 +217,7 @@ export default class ExportMarketplaceApps { if (has(data, 'server_configuration') || has(data, 'configuration')) { log.debug(`Found configuration data for app: '${app}'.`, this.exportConfig.context); - + if (!this.nodeCrypto && (has(data, 'server_configuration') || has(data, 'configuration'))) { log.debug(`Initializing NodeCrypto for app: '${app}'...`, this.exportConfig.context); this.nodeCrypto = await createNodeCryptoInstance(this.exportConfig); @@ -254,7 +266,7 @@ export default class ExportMarketplaceApps { * the API. In this code, it is initially set to 0, indicating that no items should be skipped in */ async getStackSpecificApps(skip = 0) { - log.debug(`Fetching stack-specific apps with skip: ${skip}`, this.exportConfig.context); + log.debug(`Fetching stack-specific apps with skip: ${skip}`, this.exportConfig.context); const collection = await this.appSdk .marketplace(this.exportConfig.org_uid) .installation() @@ -269,7 +281,7 @@ export default class ExportMarketplaceApps { if (collection) { const { items: apps, count } = collection; log.debug(`Fetched ${apps?.length || 0} apps out of ${count}.`, this.exportConfig.context); - + // NOTE Remove all the chain functions const installation = map(apps, (app) => omitBy(app, (val, _key) => { @@ -277,7 +289,7 @@ export default class ExportMarketplaceApps { return false; }), ) as unknown as Installation[]; - + log.debug(`Processed ${installation.length} app installations.`, this.exportConfig.context); this.installedApps = this.installedApps.concat(installation); diff --git a/packages/contentstack-export/src/export/modules/personalize.ts b/packages/contentstack-export/src/export/modules/personalize.ts index 51656635c..deffcd50b 100644 --- a/packages/contentstack-export/src/export/modules/personalize.ts +++ b/packages/contentstack-export/src/export/modules/personalize.ts @@ -22,27 +22,31 @@ export default class ExportPersonalize { async start(): Promise { try { log.debug('Starting export process for Personalize...', this.exportConfig.context); - + if (!this.personalizeConfig.baseURL[this.exportConfig.region.name]) { log.debug(`Personalize URL not set for region: '${this.exportConfig.region.name}'.`, this.exportConfig.context); log.info(messageHandler.parse('PERSONALIZE_URL_NOT_SET'), this.exportConfig.context); this.exportConfig.personalizationEnabled = false; return; } - + if (this.exportConfig.management_token) { log.debug('Management token detected, skipping personalize export.', this.exportConfig.context); log.info(messageHandler.parse('PERSONALIZE_SKIPPING_WITH_MANAGEMENT_TOKEN'), this.exportConfig.context); this.exportConfig.personalizationEnabled = false; return; } - + log.debug('Starting export process for personalization projects...', this.exportConfig.context); await new ExportProjects(this.exportConfig).start(); - + if (this.exportConfig.personalizationEnabled) { - log.debug('Personalization is enabled, processing personalize modules... ' + this.exportConfig.modules.personalize.exportOrder.join(', '), this.exportConfig.context); - + log.debug( + 'Personalization is enabled, processing personalize modules... ' + + this.exportConfig.modules.personalize.exportOrder.join(', '), + this.exportConfig.context, + ); + const moduleMapper = { events: new ExportEvents(this.exportConfig), attributes: new ExportAttributes(this.exportConfig), @@ -54,23 +58,20 @@ export default class ExportPersonalize { .exportOrder as (keyof typeof moduleMapper)[]; log.debug(`Personalize export order: ${order.join(', ')}.`, this.exportConfig.context); - + for (const module of order) { log.debug(`Processing personalization module: '${module}'...`, this.exportConfig.context); - + if (moduleMapper[module]) { log.debug(`Starting export for module: '${module}'...`, this.exportConfig.context); await moduleMapper[module].start(); log.debug(`Completed export for module: '${module}'.`, this.exportConfig.context); } else { log.debug(`Module not implemented: '${module}'.`, this.exportConfig.context); - log.info( - messageHandler.parse('PERSONALIZE_MODULE_NOT_IMPLEMENTED', module), - this.exportConfig.context, - ); + log.info(messageHandler.parse('PERSONALIZE_MODULE_NOT_IMPLEMENTED', module), this.exportConfig.context); } } - + log.debug('Completed all personalization module exports.', this.exportConfig.context); } else { log.debug('Personalization is disabled, skipping personalize module exports.', this.exportConfig.context); diff --git a/packages/contentstack-export/src/export/modules/publishing-rules.ts b/packages/contentstack-export/src/export/modules/publishing-rules.ts new file mode 100644 index 000000000..86be31dee --- /dev/null +++ b/packages/contentstack-export/src/export/modules/publishing-rules.ts @@ -0,0 +1,83 @@ +import omit from 'lodash/omit'; +import isEmpty from 'lodash/isEmpty'; +import { resolve as pResolve } from 'node:path'; +import { handleAndLogError, log } from '@contentstack/cli-utilities'; + +import BaseClass from './base-class'; +import { fsUtil } from '../../utils'; +import { PublishingRulesConfig, ModuleClassParams } from '../../types'; + +export default class ExportPublishingRules extends BaseClass { + private readonly publishingRules: Record> = {}; + private readonly publishingRulesConfig: PublishingRulesConfig; + private publishingRulesFolderPath: string; + private readonly qs: { include_count: boolean; skip?: number }; + + constructor({ exportConfig, stackAPIClient }: ModuleClassParams) { + super({ exportConfig, stackAPIClient }); + this.publishingRulesConfig = exportConfig.modules['publishing-rules']; + this.qs = { include_count: true }; + this.exportConfig.context.module = 'publishing-rules'; + } + + async start(): Promise { + this.publishingRulesFolderPath = pResolve( + this.exportConfig.data, + this.exportConfig.branchName || '', + this.publishingRulesConfig.dirName, + ); + log.debug(`Publishing rules folder path: ${this.publishingRulesFolderPath}`, this.exportConfig.context); + + await fsUtil.makeDirectory(this.publishingRulesFolderPath); + log.debug('Created publishing rules directory', this.exportConfig.context); + + await this.fetchAllPublishingRules(); + + if (isEmpty(this.publishingRules)) { + log.info('No Publishing Rules found', this.exportConfig.context); + return; + } + + const outPath = pResolve(this.publishingRulesFolderPath, this.publishingRulesConfig.fileName); + fsUtil.writeFile(outPath, this.publishingRules); + log.success( + `Publishing rules exported successfully! Total count: ${Object.keys(this.publishingRules).length}`, + this.exportConfig.context, + ); + } + + private async fetchAllPublishingRules(skip = 0): Promise { + try { + if (skip > 0) { + this.qs.skip = skip; + } + + const data: { items?: Record[]; count?: number } = await this.stack + .workflow() + .publishRule() + .fetchAll(this.qs); + + const items = data.items ?? []; + const total = data.count ?? items.length; + + if (!items.length) { + log.debug('No publishing rules returned for this page', this.exportConfig.context); + return; + } + + for (const rule of items) { + const uid = rule.uid as string | undefined; + if (uid) { + this.publishingRules[uid] = omit(rule, this.publishingRulesConfig.invalidKeys) as Record; + } + } + + const nextSkip = skip + items.length; + if (nextSkip < total) { + await this.fetchAllPublishingRules(nextSkip); + } + } catch (error: unknown) { + handleAndLogError(error as Error, { ...this.exportConfig.context }); + } + } +} diff --git a/packages/contentstack-export/src/export/modules/stack.ts b/packages/contentstack-export/src/export/modules/stack.ts index cb1d75fb0..bfe6d7c93 100644 --- a/packages/contentstack-export/src/export/modules/stack.ts +++ b/packages/contentstack-export/src/export/modules/stack.ts @@ -77,7 +77,10 @@ export default class ExportStack extends BaseClass { .stack({ api_key: this.exportConfig.source_stack }) .fetch() .then((data: any) => { - log.debug(`Successfully fetched stack data for: '${this.exportConfig.source_stack}'.`, this.exportConfig.context); + log.debug( + `Successfully fetched stack data for: '${this.exportConfig.source_stack}'.`, + this.exportConfig.context, + ); return data; }) .catch((error: any) => { @@ -168,7 +171,10 @@ export default class ExportStack extends BaseClass { return resp; }) .catch((error: any) => { - log.debug(`An error occurred while exporting stack: '${this.exportConfig.source_stack}'.`, this.exportConfig.context); + log.debug( + `An error occurred while exporting stack: '${this.exportConfig.source_stack}'.`, + this.exportConfig.context, + ); handleAndLogError(error, { ...this.exportConfig.context }); }); } diff --git a/packages/contentstack-export/src/export/modules/taxonomies.ts b/packages/contentstack-export/src/export/modules/taxonomies.ts index 69acea863..11ed9bb5c 100644 --- a/packages/contentstack-export/src/export/modules/taxonomies.ts +++ b/packages/contentstack-export/src/export/modules/taxonomies.ts @@ -45,7 +45,7 @@ export default class ExportTaxonomies extends BaseClass { async start(): Promise { log.debug('Starting export process for taxonomies...', this.exportConfig.context); - + //create taxonomies folder this.taxonomiesFolderPath = pResolve( this.exportConfig.data, @@ -53,7 +53,7 @@ export default class ExportTaxonomies extends BaseClass { this.taxonomiesConfig.dirName, ); log.debug(`Taxonomies folder path: '${this.taxonomiesFolderPath}'`, this.exportConfig.context); - + await fsUtil.makeDirectory(this.taxonomiesFolderPath); log.debug('Created taxonomies directory.', this.exportConfig.context); diff --git a/packages/contentstack-export/src/export/modules/webhooks.ts b/packages/contentstack-export/src/export/modules/webhooks.ts index 42d657490..ef59a13c2 100644 --- a/packages/contentstack-export/src/export/modules/webhooks.ts +++ b/packages/contentstack-export/src/export/modules/webhooks.ts @@ -27,7 +27,7 @@ export default class ExportWebhooks extends BaseClass { async start(): Promise { log.debug('Starting webhooks export process...', this.exportConfig.context); - + this.webhooksFolderPath = pResolve( this.exportConfig.data, this.exportConfig.branchName || '', @@ -37,10 +37,10 @@ export default class ExportWebhooks extends BaseClass { await fsUtil.makeDirectory(this.webhooksFolderPath); log.debug('Created webhooks directory', this.exportConfig.context); - + await this.getWebhooks(); log.debug(`Retrieved ${Object.keys(this.webhooks).length} webhooks`, this.exportConfig.context); - + if (this.webhooks === undefined || isEmpty(this.webhooks)) { log.info(messageHandler.parse('WEBHOOK_NOT_FOUND'), this.exportConfig.context); } else { @@ -61,7 +61,7 @@ export default class ExportWebhooks extends BaseClass { } else { log.debug('Fetching webhooks with initial query', this.exportConfig.context); } - + log.debug(`Query parameters: ${JSON.stringify(this.qs)}`, this.exportConfig.context); await this.stack @@ -70,7 +70,7 @@ export default class ExportWebhooks extends BaseClass { .then(async (data: any) => { const { items, count } = data; log.debug(`Fetched ${items?.length || 0} webhooks out of total ${count}`, this.exportConfig.context); - + if (items?.length) { log.debug(`Processing ${items.length} webhooks`, this.exportConfig.context); this.sanitizeAttribs(items); @@ -93,16 +93,19 @@ export default class ExportWebhooks extends BaseClass { sanitizeAttribs(webhooks: Record[]) { log.debug(`Sanitizing ${webhooks.length} webhooks`, this.exportConfig.context); - + for (let index = 0; index < webhooks?.length; index++) { const webhookUid = webhooks[index].uid; const webhookName = webhooks[index]?.name; log.debug(`Processing webhook: ${webhookName} (${webhookUid})`, this.exportConfig.context); - + this.webhooks[webhookUid] = omit(webhooks[index], ['SYS_ACL']); log.success(messageHandler.parse('WEBHOOK_EXPORT_SUCCESS', webhookName), this.exportConfig.context); } - - log.debug(`Sanitization complete. Total webhooks processed: ${Object.keys(this.webhooks).length}`, this.exportConfig.context); + + log.debug( + `Sanitization complete. Total webhooks processed: ${Object.keys(this.webhooks).length}`, + this.exportConfig.context, + ); } } diff --git a/packages/contentstack-export/src/export/modules/workflows.ts b/packages/contentstack-export/src/export/modules/workflows.ts index 6a9fedb8a..f51b0d34a 100644 --- a/packages/contentstack-export/src/export/modules/workflows.ts +++ b/packages/contentstack-export/src/export/modules/workflows.ts @@ -24,7 +24,7 @@ export default class ExportWorkFlows extends BaseClass { this.exportConfig.context.module = 'workflows'; } - async start(): Promise { + async start(): Promise { this.webhooksFolderPath = pResolve( this.exportConfig.data, this.exportConfig.branchName || '', @@ -34,7 +34,7 @@ export default class ExportWorkFlows extends BaseClass { await fsUtil.makeDirectory(this.webhooksFolderPath); log.debug('Created workflows directory', this.exportConfig.context); - + await this.getWorkflows(); log.debug(`Retrieved ${Object.keys(this.workflows).length} workflows`, this.exportConfig.context); @@ -45,7 +45,7 @@ export default class ExportWorkFlows extends BaseClass { log.debug(`Writing workflows to: ${workflowsFilePath}`, this.exportConfig.context); fsUtil.writeFile(workflowsFilePath, this.workflows); log.success( - messageHandler.parse('WORKFLOW_EXPORT_COMPLETE', Object.keys(this.workflows).length ), + messageHandler.parse('WORKFLOW_EXPORT_COMPLETE', Object.keys(this.workflows).length), this.exportConfig.context, ); } @@ -66,7 +66,7 @@ export default class ExportWorkFlows extends BaseClass { //NOTE - Handle the case where old workflow api is enabled in that case getting responses as objects. const workflowCount = count !== undefined ? count : items.length; log.debug(`Fetched ${items?.length || 0} workflows out of total ${workflowCount}`, this.exportConfig.context); - + if (items?.length) { log.debug(`Processing ${items.length} workflows`, this.exportConfig.context); await this.sanitizeAttribs(items); @@ -89,29 +89,29 @@ export default class ExportWorkFlows extends BaseClass { async sanitizeAttribs(workflows: Record[]) { log.debug(`Sanitizing ${workflows.length} workflows`, this.exportConfig.context); - + for (let index = 0; index < workflows?.length; index++) { const workflowUid = workflows[index].uid; const workflowName = workflows[index]?.name || ''; log.debug(`Processing workflow: ${workflowName} (${workflowUid})`, this.exportConfig.context); - + await this.getWorkflowRoles(workflows[index]); this.workflows[workflowUid] = omit(workflows[index], this.workflowConfig.invalidKeys); - log.success( - messageHandler.parse('WORKFLOW_EXPORT_SUCCESS', workflowName), - this.exportConfig.context, - ); + log.success(messageHandler.parse('WORKFLOW_EXPORT_SUCCESS', workflowName), this.exportConfig.context); } - - log.debug(`Sanitization complete. Total workflows processed: ${Object.keys(this.workflows).length}`, this.exportConfig.context); + + log.debug( + `Sanitization complete. Total workflows processed: ${Object.keys(this.workflows).length}`, + this.exportConfig.context, + ); } async getWorkflowRoles(workflow: Record) { log.debug(`Processing workflow roles for workflow: ${workflow.uid}`, this.exportConfig.context); - + for (const stage of workflow?.workflow_stages) { log.debug(`Processing workflow stage: ${stage.name}`, this.exportConfig.context); - + for (let i = 0; i < stage?.SYS_ACL?.roles?.uids?.length; i++) { const roleUid = stage.SYS_ACL.roles.uids[i]; log.debug(`Fetching role data for role UID: ${roleUid}`, this.exportConfig.context); @@ -123,7 +123,7 @@ export default class ExportWorkFlows extends BaseClass { async getRoles(roleUid: number): Promise { log.debug(`Fetching role with UID: ${roleUid}`, this.exportConfig.context); - + return await this.stack .role(roleUid) .fetch({ include_rules: true, include_permissions: true }) @@ -133,10 +133,7 @@ export default class ExportWorkFlows extends BaseClass { }) .catch((err: any) => { log.debug(`Failed to fetch role data for UID: ${roleUid}`, this.exportConfig.context); - handleAndLogError( - err, - { ...this.exportConfig.context } - ); + handleAndLogError(err, { ...this.exportConfig.context }); }); } } diff --git a/packages/contentstack-export/src/types/default-config.ts b/packages/contentstack-export/src/types/default-config.ts index 12090760c..01cb84c89 100644 --- a/packages/contentstack-export/src/types/default-config.ts +++ b/packages/contentstack-export/src/types/default-config.ts @@ -67,6 +67,13 @@ export default interface DefaultConfig { invalidKeys: string[]; dependencies?: Modules[]; }; + 'publishing-rules': { + dirName: string; + fileName: string; + invalidKeys: string[]; + dependencies?: Modules[]; + limit?: number; + }; globalfields: { dirName: string; fileName: string; diff --git a/packages/contentstack-export/src/types/index.ts b/packages/contentstack-export/src/types/index.ts index ec2118510..fd13364a5 100644 --- a/packages/contentstack-export/src/types/index.ts +++ b/packages/contentstack-export/src/types/index.ts @@ -46,6 +46,7 @@ export type Modules = | 'content-types' | 'custom-roles' | 'workflows' + | 'publishing-rules' | 'labels' | 'marketplace-apps' | 'taxonomies' @@ -117,6 +118,14 @@ export interface WorkflowConfig { limit?: number; } +export interface PublishingRulesConfig { + dirName: string; + fileName: string; + invalidKeys: string[]; + dependencies?: Modules[]; + limit?: number; +} + export interface CustomRoleConfig { dirName: string; fileName: string; diff --git a/packages/contentstack-export/src/utils/export-config-handler.ts b/packages/contentstack-export/src/utils/export-config-handler.ts index c67b6c12b..2303f3937 100644 --- a/packages/contentstack-export/src/utils/export-config-handler.ts +++ b/packages/contentstack-export/src/utils/export-config-handler.ts @@ -1,6 +1,6 @@ import merge from 'merge'; import * as path from 'path'; -import { configHandler, isAuthenticated,cliux, sanitizePath, log } from '@contentstack/cli-utilities'; +import { configHandler, isAuthenticated, cliux, sanitizePath, log } from '@contentstack/cli-utilities'; import defaultConfig from '../config'; import { readFile } from './file-helper'; import { askExportDir, askAPIKey } from './interactive'; @@ -97,7 +97,7 @@ const setupConfig = async (exportCmdFlags: any): Promise => { if (exportCmdFlags['branch-alias']) { config.branchAlias = exportCmdFlags['branch-alias']; - } + } if (exportCmdFlags['branch']) { config.branchName = exportCmdFlags['branch']; } @@ -133,7 +133,7 @@ const setupConfig = async (exportCmdFlags: any): Promise => { } } - // Add authentication details to config for context tracking + // Add authentication details to config for context tracking config.authenticationMethod = authenticationMethod; log.debug('Export configuration setup completed.', { ...config }); diff --git a/packages/contentstack-export/src/utils/logger.ts b/packages/contentstack-export/src/utils/logger.ts index 07aa897b8..337a9d897 100644 --- a/packages/contentstack-export/src/utils/logger.ts +++ b/packages/contentstack-export/src/utils/logger.ts @@ -24,7 +24,7 @@ function returnString(args: unknown[]) { if (item && typeof item === 'object') { try { const redactedObject = redactObject(item); - if(redactedObject && typeof redactedObject === 'object') { + if (redactedObject && typeof redactedObject === 'object') { return JSON.stringify(redactedObject); } } catch (error) {} diff --git a/packages/contentstack-export/src/utils/marketplace-app-helper.ts b/packages/contentstack-export/src/utils/marketplace-app-helper.ts index 970f35527..f77c2ccb1 100644 --- a/packages/contentstack-export/src/utils/marketplace-app-helper.ts +++ b/packages/contentstack-export/src/utils/marketplace-app-helper.ts @@ -1,4 +1,10 @@ -import { cliux, handleAndLogError, NodeCrypto, managementSDKClient, createDeveloperHubUrl } from '@contentstack/cli-utilities'; +import { + cliux, + handleAndLogError, + NodeCrypto, + managementSDKClient, + createDeveloperHubUrl, +} from '@contentstack/cli-utilities'; import { ExportConfig } from '../types'; @@ -12,7 +18,7 @@ export async function getOrgUid(config: ExportConfig): Promise { .stack({ api_key: config.source_stack }) .fetch() .catch((error: any) => { - handleAndLogError(error, {...config.context}); + handleAndLogError(error, { ...config.context }); }); return tempStackData?.org_uid; diff --git a/packages/contentstack-export/src/utils/setup-export-dir.ts b/packages/contentstack-export/src/utils/setup-export-dir.ts index 76de5b364..cc49ee2c8 100644 --- a/packages/contentstack-export/src/utils/setup-export-dir.ts +++ b/packages/contentstack-export/src/utils/setup-export-dir.ts @@ -8,7 +8,9 @@ export default async function setupExportDir(exportConfig: ExportConfig) { makeDirectory(exportConfig.exportDir); if (exportConfig.branches) { return Promise.all( - exportConfig.branches.map((branch) => makeDirectory(path.join(sanitizePath(exportConfig.exportDir), sanitizePath(branch.uid)))), + exportConfig.branches.map((branch) => + makeDirectory(path.join(sanitizePath(exportConfig.exportDir), sanitizePath(branch.uid))), + ), ); } } diff --git a/packages/contentstack-export/test/unit/export/modules/assets.test.ts b/packages/contentstack-export/test/unit/export/modules/assets.test.ts index 56ef04ef7..408c9a63c 100644 --- a/packages/contentstack-export/test/unit/export/modules/assets.test.ts +++ b/packages/contentstack-export/test/unit/export/modules/assets.test.ts @@ -113,6 +113,11 @@ describe('ExportAssets', () => { fileName: 'workflows.json', invalidKeys: [] }, + 'publishing-rules': { + dirName: 'workflows', + fileName: 'publishing-rules.json', + invalidKeys: [], + }, globalfields: { dirName: 'global_fields', fileName: 'globalfields.json', diff --git a/packages/contentstack-export/test/unit/export/modules/base-class.test.ts b/packages/contentstack-export/test/unit/export/modules/base-class.test.ts index 0ffe4187f..4b62b5230 100644 --- a/packages/contentstack-export/test/unit/export/modules/base-class.test.ts +++ b/packages/contentstack-export/test/unit/export/modules/base-class.test.ts @@ -131,6 +131,11 @@ describe('BaseClass', () => { fileName: 'workflows.json', invalidKeys: [] }, + 'publishing-rules': { + dirName: 'workflows', + fileName: 'publishing-rules.json', + invalidKeys: [], + }, globalfields: { dirName: 'global_fields', fileName: 'globalfields.json', diff --git a/packages/contentstack-export/test/unit/export/modules/publishing-rules.test.ts b/packages/contentstack-export/test/unit/export/modules/publishing-rules.test.ts new file mode 100644 index 000000000..e6ab1321b --- /dev/null +++ b/packages/contentstack-export/test/unit/export/modules/publishing-rules.test.ts @@ -0,0 +1,180 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { resolve as pResolve } from 'node:path'; +import { FsUtility } from '@contentstack/cli-utilities'; +import ExportPublishingRules from '../../../../src/export/modules/publishing-rules'; +import ExportConfig from '../../../../src/types/export-config'; + +describe('ExportPublishingRules', () => { + let exportPublishingRules: ExportPublishingRules; + let mockStackClient: any; + let mockExportConfig: ExportConfig; + + beforeEach(() => { + mockStackClient = { + workflow: sinon.stub().returns({ + publishRule: sinon.stub().returns({ + fetchAll: sinon.stub().resolves({ items: [], count: 0 }), + }), + }), + }; + + mockExportConfig = { + contentVersion: 1, + versioning: false, + host: 'https://api.contentstack.io', + developerHubUrls: {}, + apiKey: 'test-api-key', + exportDir: '/test/export', + data: '/test/data', + branchName: '', + context: { + command: 'cm:stacks:export', + module: 'publishing-rules', + userId: 'user-123', + email: 'test@example.com', + sessionId: 'session-123', + apiKey: 'test-api-key', + orgId: 'org-123', + authenticationMethod: 'Basic Auth', + }, + cliLogsPath: '/test/logs', + forceStopMarketplaceAppsPrompt: false, + master_locale: { code: 'en-us' }, + region: { + name: 'us', + cma: 'https://api.contentstack.io', + cda: 'https://cdn.contentstack.io', + uiHost: 'https://app.contentstack.com', + }, + skipStackSettings: false, + skipDependencies: false, + languagesCode: ['en'], + apis: {}, + preserveStackVersion: false, + personalizationEnabled: false, + fetchConcurrency: 5, + writeConcurrency: 5, + developerHubBaseUrl: '', + marketplaceAppEncryptionKey: '', + onlyTSModules: [], + modules: { + types: ['publishing-rules'], + 'publishing-rules': { + dirName: 'workflows', + fileName: 'publishing-rules.json', + invalidKeys: ['stackHeaders', 'created_at'], + }, + }, + } as any; + + exportPublishingRules = new ExportPublishingRules({ + exportConfig: mockExportConfig, + stackAPIClient: mockStackClient, + moduleName: 'publishing-rules', + }); + + sinon.stub(FsUtility.prototype, 'writeFile').resolves(); + sinon.stub(FsUtility.prototype, 'makeDirectory').resolves(); + }); + + afterEach(() => { + sinon.restore(); + }); + + describe('Constructor', () => { + it('sets context.module to publishing-rules and reads module config', () => { + expect(exportPublishingRules).to.be.instanceOf(ExportPublishingRules); + expect(exportPublishingRules.exportConfig.context.module).to.equal('publishing-rules'); + expect((exportPublishingRules as any).publishingRulesConfig.fileName).to.equal('publishing-rules.json'); + expect((exportPublishingRules as any).publishingRulesConfig.dirName).to.equal('workflows'); + }); + }); + + describe('start()', () => { + it('resolves output path from data, branchName, and publishing-rules dirName', async () => { + const fetchAll = sinon.stub().resolves({ items: [], count: 0 }); + mockStackClient.workflow.returns({ + publishRule: sinon.stub().returns({ fetchAll }), + }); + + await exportPublishingRules.start(); + + const expectedFolder = pResolve(mockExportConfig.data, mockExportConfig.branchName || '', 'workflows'); + expect((FsUtility.prototype.makeDirectory as sinon.SinonStub).calledWith(expectedFolder)).to.be.true; + }); + + it('writes publishing-rules.json with rules omitting invalidKeys when API returns items', async () => { + const writeFileStub = FsUtility.prototype.writeFile as sinon.SinonStub; + const items = [ + { + uid: 'pr-1', + name: 'Rule 1', + stackHeaders: { h: 1 }, + created_at: '2020-01-01', + }, + ]; + mockStackClient.workflow.returns({ + publishRule: sinon.stub().returns({ + fetchAll: sinon.stub().resolves({ items, count: 1 }), + }), + }); + + await exportPublishingRules.start(); + + const expectedPath = pResolve( + mockExportConfig.data, + mockExportConfig.branchName || '', + 'workflows', + 'publishing-rules.json', + ); + expect(writeFileStub.calledOnce).to.be.true; + expect(writeFileStub.firstCall.args[0]).to.equal(expectedPath); + const written = writeFileStub.firstCall.args[1] as Record>; + expect(written['pr-1']).to.deep.equal({ uid: 'pr-1', name: 'Rule 1' }); + expect(written['pr-1'].stackHeaders).to.equal(undefined); + expect(written['pr-1'].created_at).to.equal(undefined); + }); + + it('does not write the rules file when no rules are returned', async () => { + const writeFileStub = FsUtility.prototype.writeFile as sinon.SinonStub; + mockStackClient.workflow.returns({ + publishRule: sinon.stub().returns({ + fetchAll: sinon.stub().resolves({ items: [], count: 0 }), + }), + }); + + await exportPublishingRules.start(); + + expect(writeFileStub.called).to.be.false; + }); + + it('requests the next page when count exceeds items length (pagination)', async () => { + const fetchAll = sinon.stub(); + fetchAll.onFirstCall().resolves({ + items: [ + { uid: 'a', name: 'A' }, + { uid: 'b', name: 'B' }, + ], + count: 3, + }); + fetchAll.onSecondCall().resolves({ + items: [{ uid: 'c', name: 'C' }], + count: 3, + }); + + mockStackClient.workflow.returns({ + publishRule: sinon.stub().returns({ fetchAll }), + }); + + await exportPublishingRules.start(); + + expect(fetchAll.callCount).to.equal(2); + expect(fetchAll.secondCall.args[0]).to.deep.include({ skip: 2, include_count: true }); + + const writeFileStub = FsUtility.prototype.writeFile as sinon.SinonStub; + const written = writeFileStub.firstCall.args[1] as Record>; + expect(Object.keys(written).sort((x, y) => x.localeCompare(y))).to.deep.equal(['a', 'b', 'c']); + }); + }); +}); diff --git a/packages/contentstack-import/package.json b/packages/contentstack-import/package.json index c8c4c56ca..7d52187f6 100644 --- a/packages/contentstack-import/package.json +++ b/packages/contentstack-import/package.json @@ -1,7 +1,7 @@ { "name": "@contentstack/cli-cm-import", "description": "Contentstack CLI plugin to import content into stack", - "version": "1.32.2", + "version": "1.33.0", "author": "Contentstack", "bugs": "https://github.com/contentstack/cli/issues", "dependencies": { diff --git a/packages/contentstack-import/src/config/index.ts b/packages/contentstack-import/src/config/index.ts index 76b70e112..e8c9e2179 100644 --- a/packages/contentstack-import/src/config/index.ts +++ b/packages/contentstack-import/src/config/index.ts @@ -41,6 +41,7 @@ const config: DefaultConfig = { 'personalize', 'custom-roles', 'workflows', + 'publishing-rules', 'entries', 'variant-entries', 'labels', @@ -88,6 +89,11 @@ const config: DefaultConfig = { fileName: 'workflows.json', invalidKeys: ['stackHeaders', 'urlPath', 'created_at', 'updated_at', 'created_by', 'updated_by'], }, + 'publishing-rules': { + dirName: 'workflows', + fileName: 'publishing-rules.json', + invalidKeys: ['stackHeaders', 'urlPath', 'created_at', 'updated_at', 'created_by', 'updated_by'], + }, assets: { dirName: 'assets', assetBatchLimit: 1, @@ -455,5 +461,7 @@ const config: DefaultConfig = { globalModules: ['webhooks'], entriesPublish: true, }; +export const PUBLISHING_RULES_APPROVERS_SKIP_MSG = + 'Skipping import of publish rule approver(s) (roles/users); reconfigure approvers on the target stack.'; export default config; diff --git a/packages/contentstack-import/src/import/modules/base-class.ts b/packages/contentstack-import/src/import/modules/base-class.ts index a0eb77075..1981f1887 100644 --- a/packages/contentstack-import/src/import/modules/base-class.ts +++ b/packages/contentstack-import/src/import/modules/base-class.ts @@ -51,7 +51,8 @@ export type ApiModuleType = | 'delete-entries' | 'create-taxonomies' | 'create-terms' - | 'import-taxonomy'; + | 'import-taxonomy' + | 'create-publishing-rule'; export type ApiOptions = { uid?: string; @@ -374,6 +375,13 @@ export default abstract class BaseClass { .create({ workflow: apiData as WorkflowData }) .then(onSuccess) .catch(onReject); + case 'create-publishing-rule': + return this.stack + .workflow() + .publishRule() + .create({ publishing_rule: omit(apiData, ['uid']) as any }) + .then(onSuccess) + .catch(onReject); case 'create-custom-role': return this.stack .role() diff --git a/packages/contentstack-import/src/import/modules/publishing-rules.ts b/packages/contentstack-import/src/import/modules/publishing-rules.ts new file mode 100644 index 000000000..ca7734585 --- /dev/null +++ b/packages/contentstack-import/src/import/modules/publishing-rules.ts @@ -0,0 +1,275 @@ +import chalk from 'chalk'; +import values from 'lodash/values'; +import isEmpty from 'lodash/isEmpty'; +import { join } from 'node:path'; + +import BaseClass, { ApiOptions } from './base-class'; +import { PUBLISHING_RULES_APPROVERS_SKIP_MSG } from '../../config'; +import { fsUtil, fileHelper, parseErrorPayload, isDuplicatePublishingRuleError } from '../../utils'; +import { log, handleAndLogError } from '@contentstack/cli-utilities'; +import { ModuleClassParams, PublishingRulesConfig } from '../../types'; + +export default class ImportPublishingRules extends BaseClass { + private readonly mapperDirPath: string; + private readonly publishingRulesFolderPath: string; + private readonly publishingRulesUidMapperPath: string; + private readonly createdPublishingRulesPath: string; + private readonly failedPublishingRulesPath: string; + private readonly publishingRulesConfig: PublishingRulesConfig; + private publishingRules: Record; + private publishingRulesUidMapper: Record; + private readonly createdPublishingRules: Record[]; + private readonly failedPublishingRules: Record[]; + private envUidMapper: Record; + private workflowUidMapper: Record; + private readonly stageUidMapper: Record = {}; + + constructor({ importConfig, stackAPIClient }: ModuleClassParams) { + super({ importConfig, stackAPIClient }); + this.importConfig.context.module = 'publishing-rules'; + this.publishingRulesConfig = importConfig.modules['publishing-rules']; + this.mapperDirPath = join(this.importConfig.backupDir, 'mapper', 'publishing-rules'); + this.publishingRulesFolderPath = join(this.importConfig.backupDir, this.publishingRulesConfig.dirName); + this.publishingRulesUidMapperPath = join(this.mapperDirPath, 'uid-mapping.json'); + this.createdPublishingRulesPath = join(this.mapperDirPath, 'success.json'); + this.failedPublishingRulesPath = join(this.mapperDirPath, 'fails.json'); + this.publishingRules = {}; + this.publishingRulesUidMapper = {}; + this.createdPublishingRules = []; + this.failedPublishingRules = []; + this.envUidMapper = {}; + this.workflowUidMapper = {}; + } + + private static collectOldStageUidToName( + exportedWorkflows: Record, + ): Record { + const map: Record = {}; + for (const workflow of Object.values(exportedWorkflows)) { + for (const stage of workflow.workflow_stages ?? []) { + if (stage.uid && stage.name) { + map[stage.uid] = stage.name; + } + } + } + return map; + } + + /** + * Returns `{ noSuccessMsg: true }` if any rule failed, so the import command skips the generic stack success line. + */ + async start(): Promise<{ noSuccessMsg: true } | void> { + const rulesFilePath = join(this.publishingRulesFolderPath, this.publishingRulesConfig.fileName); + + if (!fileHelper.fileExistsSync(rulesFilePath)) { + log.info(`No Publishing Rules found - '${rulesFilePath}'`, this.importConfig.context); + return; + } + + this.publishingRules = (fsUtil.readFile(rulesFilePath, true) as Record) ?? {}; + if (isEmpty(this.publishingRules)) { + log.info('No Publishing Rules found', this.importConfig.context); + return; + } + + await fsUtil.makeDirectory(this.mapperDirPath); + + this.publishingRulesUidMapper = this.readUidMappingFile(this.publishingRulesUidMapperPath); + this.envUidMapper = this.readMapper('environments'); + this.workflowUidMapper = this.readMapper('workflows'); + + await this.buildStageUidMapper(); + await this.importPublishingRules(); + + if (this.createdPublishingRules?.length) { + fsUtil.writeFile(this.createdPublishingRulesPath, this.createdPublishingRules); + } + if (this.failedPublishingRules?.length) { + fsUtil.writeFile(this.failedPublishingRulesPath, this.failedPublishingRules); + } + + const successCount = this.createdPublishingRules.length; + const failCount = this.failedPublishingRules.length; + + if (failCount > 0 && successCount === 0) { + log.error( + `Publishing rules import failed! ${failCount} rule(s) could not be imported. Check '${this.failedPublishingRulesPath}' for details.`, + this.importConfig.context, + ); + } else if (failCount > 0) { + log.warn( + `Publishing rules import completed with errors. Imported: ${successCount}, Failed: ${failCount}. Check '${this.failedPublishingRulesPath}' for details.`, + this.importConfig.context, + ); + } else { + log.success('Publishing rules have been imported successfully!', this.importConfig.context); + } + + if (failCount > 0) { + return { noSuccessMsg: true }; + } + } + + private readUidMappingFile(path: string): Record { + return fileHelper.fileExistsSync(path) ? (fsUtil.readFile(path, true) as Record) ?? {} : {}; + } + + private readMapper(moduleDir: string): Record { + const p = join(this.importConfig.backupDir, 'mapper', moduleDir, 'uid-mapping.json'); + return this.readUidMappingFile(p); + } + + private async importPublishingRules(): Promise { + const apiContent = values(this.publishingRules) as Record[]; + log.debug(`Importing ${apiContent.length} publishing rule(s)`, this.importConfig.context); + + const onSuccess = ({ response, apiData }: { response: { uid: string }; apiData: { uid: string } }) => { + const { uid } = apiData; + this.createdPublishingRules.push(response as unknown as Record); + this.publishingRulesUidMapper[uid] = response.uid; + log.success(`Publishing rule imported successfully (${uid} → ${response.uid})`, this.importConfig.context); + fsUtil.writeFile(this.publishingRulesUidMapperPath, this.publishingRulesUidMapper); + }; + + const onReject = ({ error, apiData }: { error: unknown; apiData: Record }) => { + const uid = apiData.uid as string; + const parsed = parseErrorPayload(error); + + if (isDuplicatePublishingRuleError(parsed, error)) { + log.info(`Publishing rule '${uid}' already exists`, this.importConfig.context); + return; + } + + this.failedPublishingRules.push(apiData); + handleAndLogError( + error as Error, + { ...this.importConfig.context, publishingRuleUid: uid }, + `Publishing rule '${uid}' failed to import`, + ); + }; + + await this.makeConcurrentCall( + { + apiContent, + processName: 'import publishing rules', + apiParams: { + serializeData: this.serializePublishingRules.bind(this), + reject: onReject, + resolve: onSuccess, + entity: 'create-publishing-rule', + includeParamOnCompletion: true, + }, + concurrencyLimit: this.importConfig.fetchConcurrency || 1, + }, + undefined, + false, + ); + } + + private mergeFetchedWorkflowStages( + workflow: { workflow_stages?: { uid?: string; name?: string }[] }, + oldStageUidToName: Record, + ): void { + for (const newStage of workflow.workflow_stages ?? []) { + const oldUid = Object.keys(oldStageUidToName).find((u) => oldStageUidToName[u] === newStage.name); + if (oldUid && newStage.uid) { + this.stageUidMapper[oldUid] = newStage.uid; + } + } + } + + private async buildStageUidMapper(): Promise { + const wf = this.importConfig.modules.workflows as { dirName: string; fileName: string }; + const workflowsFilePath = join(this.importConfig.backupDir, wf.dirName, wf.fileName); + + if (!fileHelper.fileExistsSync(workflowsFilePath)) { + log.debug('No exported workflows file; stage UID mapping skipped', this.importConfig.context); + return; + } + + const exportedWorkflows = fsUtil.readFile(workflowsFilePath, true) as Record< + string, + { workflow_stages?: { uid?: string; name?: string }[] } + > | null; + if (!exportedWorkflows) return; + + const oldStageUidToName = ImportPublishingRules.collectOldStageUidToName(exportedWorkflows); + + for (const newWorkflowUid of Object.values(this.workflowUidMapper)) { + try { + const workflow = await this.stack.workflow(newWorkflowUid as string).fetch(); + this.mergeFetchedWorkflowStages( + workflow as { workflow_stages?: { uid?: string; name?: string }[] }, + oldStageUidToName, + ); + } catch (error: unknown) { + log.debug(`Stage mapping: could not fetch workflow '${newWorkflowUid}'`, this.importConfig.context); + handleAndLogError(error as Error, { ...this.importConfig.context }); + } + } + + log.debug(`Stage UID mapper: ${Object.keys(this.stageUidMapper).length} entr(y/ies)`, this.importConfig.context); + } + + private stripApprovers(rule: Record): void { + if (rule.approvers == null) return; + + const a = rule.approvers as { roles?: unknown[]; users?: unknown[] }; + const hadContent = (Array.isArray(a.roles) && a.roles.length > 0) || (Array.isArray(a.users) && a.users.length > 0); + if (hadContent) { + log.info(chalk.yellow(PUBLISHING_RULES_APPROVERS_SKIP_MSG), this.importConfig.context); + } + rule.approvers = { roles: [], users: [] }; + } + + private remapReference( + rule: Record, + field: 'workflow' | 'environment', + mapper: Record, + ): void { + const current = rule[field] as string | undefined; + if (!current) return; + const mapped = mapper[current] as string | undefined; + if (mapped) { + rule[field] = mapped; + log.debug(`${field} UID remapped`, this.importConfig.context); + } else { + log.debug(`No ${field} mapping for ${current}; leaving as-is`, this.importConfig.context); + } + } + + serializePublishingRules(apiOptions: ApiOptions): ApiOptions { + const rule = apiOptions.apiData as Record; + const ruleUid = rule.uid as string; + + if (ruleUid in this.publishingRulesUidMapper) { + log.info( + `Publishing rule '${ruleUid}' already exists. Skipping it to avoid duplicates!`, + this.importConfig.context, + ); + apiOptions.entity = undefined; + return apiOptions; + } + + const oldUid = ruleUid; + delete rule.uid; + + this.stripApprovers(rule); + this.remapReference(rule, 'workflow', this.workflowUidMapper); + this.remapReference(rule, 'environment', this.envUidMapper); + + if (rule.workflow_stage) { + const stage = rule.workflow_stage as string; + const mappedStage = this.stageUidMapper[stage]; + if (mappedStage) { + rule.workflow_stage = mappedStage; + log.debug('workflow_stage UID remapped', this.importConfig.context); + } else { + log.debug(`No workflow_stage mapping for ${stage}; leaving as-is`, this.importConfig.context); + } + } + + apiOptions.apiData = { ...rule, uid: oldUid }; + return apiOptions; + } +} diff --git a/packages/contentstack-import/src/types/default-config.ts b/packages/contentstack-import/src/types/default-config.ts index aa4867d29..e27968b9b 100644 --- a/packages/contentstack-import/src/types/default-config.ts +++ b/packages/contentstack-import/src/types/default-config.ts @@ -49,6 +49,11 @@ export default interface DefaultConfig { fileName: string; invalidKeys: string[]; }; + 'publishing-rules': { + dirName: string; + fileName: string; + invalidKeys: string[]; + }; assets: { dirName: string; assetBatchLimit: number; diff --git a/packages/contentstack-import/src/types/index.ts b/packages/contentstack-import/src/types/index.ts index ee9062465..8dac29ffa 100644 --- a/packages/contentstack-import/src/types/index.ts +++ b/packages/contentstack-import/src/types/index.ts @@ -46,6 +46,7 @@ export type Modules = | 'content-types' | 'custom-roles' | 'workflows' + | 'publishing-rules' | 'labels' | 'marketplace-apps' | 'taxonomies' @@ -94,6 +95,12 @@ export interface WorkflowConfig { invalidKeys: string[]; } +export interface PublishingRulesConfig { + dirName: string; + fileName: string; + invalidKeys: string[]; +} + export interface CustomRoleConfig { dirName: string; fileName: string; diff --git a/packages/contentstack-import/src/utils/index.ts b/packages/contentstack-import/src/utils/index.ts index fcf452a26..8232acd5d 100644 --- a/packages/contentstack-import/src/utils/index.ts +++ b/packages/contentstack-import/src/utils/index.ts @@ -33,3 +33,4 @@ export { export * from './common-helper'; export * from './log'; export { lookUpTaxonomy, lookUpTerms } from './taxonomies-helper'; +export { parseErrorPayload, isDuplicatePublishingRuleError } from './publishing-rules-helper'; diff --git a/packages/contentstack-import/src/utils/publishing-rules-helper.ts b/packages/contentstack-import/src/utils/publishing-rules-helper.ts new file mode 100644 index 000000000..b7f3b44ec --- /dev/null +++ b/packages/contentstack-import/src/utils/publishing-rules-helper.ts @@ -0,0 +1,32 @@ +/** + * Helpers for publishing rules import (API error shape, duplicate detection). + */ + +export function parseErrorPayload(error: unknown): { + errors?: Record; + error_message?: string; +} | null { + if (!error || typeof error !== 'object') return null; + const e = error as { message?: string; errors?: Record }; + if (e.errors) return e; + if (e.message && typeof e.message === 'string') { + try { + return JSON.parse(e.message) as { errors?: Record; error_message?: string }; + } catch { + return null; + } + } + return null; +} + +export function isDuplicatePublishingRuleError( + parsed: { errors?: Record; error_message?: string } | null, + raw: unknown, +): boolean { + const errors = parsed?.errors ?? (raw as { errors?: Record })?.errors; + if (errors?.name || errors?.['publishing_rule.name'] || errors?.['publish_rule.name']) { + return true; + } + const msg = parsed?.error_message; + return typeof msg === 'string' && /already exists|duplicate/i.test(msg); +} diff --git a/packages/contentstack-import/test/unit/import/modules/locales.test.ts b/packages/contentstack-import/test/unit/import/modules/locales.test.ts index fc3631e81..1f5da35cf 100644 --- a/packages/contentstack-import/test/unit/import/modules/locales.test.ts +++ b/packages/contentstack-import/test/unit/import/modules/locales.test.ts @@ -51,6 +51,11 @@ describe('ImportLocales', () => { webhooks: { dirName: 'webhooks', fileName: 'webhooks.json' }, releases: { dirName: 'releases', fileName: 'releases.json', invalidKeys: ['uid'] }, workflows: { dirName: 'workflows', fileName: 'workflows.json', invalidKeys: ['uid'] }, + 'publishing-rules': { + dirName: 'workflows', + fileName: 'publishing-rules.json', + invalidKeys: ['uid'], + }, assets: { dirName: 'assets', assetBatchLimit: 10, diff --git a/packages/contentstack-import/test/unit/import/modules/publishing-rules.test.ts b/packages/contentstack-import/test/unit/import/modules/publishing-rules.test.ts new file mode 100644 index 000000000..26aa3f4a7 --- /dev/null +++ b/packages/contentstack-import/test/unit/import/modules/publishing-rules.test.ts @@ -0,0 +1,333 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { join } from 'node:path'; +import ImportPublishingRules from '../../../../src/import/modules/publishing-rules'; +import { ImportConfig } from '../../../../src/types'; +describe('ImportPublishingRules', () => { + const BACKUP = '/test/backup'; + const rulesFile = join(BACKUP, 'workflows', 'publishing-rules.json'); + const workflowsExportFile = join(BACKUP, 'workflows', 'workflows.json'); + const workflowMapperFile = join(BACKUP, 'mapper', 'workflows', 'uid-mapping.json'); + const envMapperFile = join(BACKUP, 'mapper', 'environments', 'uid-mapping.json'); + const publishingMapperFile = join(BACKUP, 'mapper', 'publishing-rules', 'uid-mapping.json'); + + let importPublishingRules: ImportPublishingRules; + let mockStackClient: any; + let mockImportConfig: ImportConfig; + let fsUtilStub: any; + let fileHelperStub: any; + let makeConcurrentCallStub: sinon.SinonStub; + let logStub: { info: sinon.SinonStub; debug: sinon.SinonStub; success: sinon.SinonStub; error: sinon.SinonStub; warn: sinon.SinonStub }; + beforeEach(() => { + fsUtilStub = { + readFile: sinon.stub(), + writeFile: sinon.stub(), + makeDirectory: sinon.stub().resolves(), + }; + + fileHelperStub = { + fileExistsSync: sinon.stub(), + }; + + sinon.replace(require('../../../../src/utils'), 'fileHelper', fileHelperStub); + sinon.replaceGetter(require('../../../../src/utils'), 'fsUtil', () => fsUtilStub); + + const fetchWorkflowStub = sinon.stub().resolves({ + workflow_stages: [{ uid: 'stage-new', name: 'Review' }], + }); + mockStackClient = { + workflow: sinon.stub().returns({ + fetch: fetchWorkflowStub, + }), + }; + + mockImportConfig = { + apiKey: 'test', + backupDir: BACKUP, + data: '/test/content', + contentVersion: 1, + region: 'us', + fetchConcurrency: 2, + context: { + command: 'cm:stacks:import', + module: 'publishing-rules', + userId: 'user-123', + email: 'test@example.com', + sessionId: 'session-123', + apiKey: 'test', + orgId: 'org-123', + authenticationMethod: 'Basic Auth', + }, + modules: { + workflows: { + dirName: 'workflows', + fileName: 'workflows.json', + invalidKeys: ['uid'], + }, + 'publishing-rules': { + dirName: 'workflows', + fileName: 'publishing-rules.json', + invalidKeys: ['uid'], + }, + }, + } as any; + + importPublishingRules = new ImportPublishingRules({ + importConfig: mockImportConfig as any, + stackAPIClient: mockStackClient, + moduleName: 'publishing-rules', + }); + + makeConcurrentCallStub = sinon.stub(importPublishingRules as any, 'makeConcurrentCall').resolves(); + + const cliUtilities = require('@contentstack/cli-utilities'); + logStub = { + info: sinon.stub(), + debug: sinon.stub(), + success: sinon.stub(), + error: sinon.stub(), + warn: sinon.stub(), + }; + sinon.stub(cliUtilities, 'log').value(logStub); + }); + + afterEach(() => { + sinon.restore(); + }); + + describe('Constructor', () => { + it('sets context.module to publishing-rules and derives exact paths from backupDir and config', () => { + expect(mockImportConfig.context.module).to.equal('publishing-rules'); + expect(importPublishingRules['mapperDirPath']).to.equal(join(BACKUP, 'mapper', 'publishing-rules')); + expect(importPublishingRules['publishingRulesFolderPath']).to.equal(join(BACKUP, 'workflows')); + expect(importPublishingRules['publishingRulesUidMapperPath']).to.equal(publishingMapperFile); + expect(importPublishingRules['createdPublishingRulesPath']).to.equal( + join(BACKUP, 'mapper', 'publishing-rules', 'success.json'), + ); + expect(importPublishingRules['failedPublishingRulesPath']).to.equal( + join(BACKUP, 'mapper', 'publishing-rules', 'fails.json'), + ); + }); + + it('initializes empty rules, mappers, and result arrays', () => { + expect(importPublishingRules['publishingRules']).to.deep.equal({}); + expect(importPublishingRules['publishingRulesUidMapper']).to.deep.equal({}); + expect(importPublishingRules['createdPublishingRules']).to.deep.equal([]); + expect(importPublishingRules['failedPublishingRules']).to.deep.equal([]); + expect(importPublishingRules['envUidMapper']).to.deep.equal({}); + expect(importPublishingRules['workflowUidMapper']).to.deep.equal({}); + expect(importPublishingRules['stageUidMapper']).to.deep.equal({}); + }); + }); + + describe('start()', () => { + it('returns undefined and logs missing file path when rules file does not exist', async () => { + fileHelperStub.fileExistsSync.withArgs(rulesFile).returns(false); + + const result = await importPublishingRules.start(); + + expect(result).to.equal(undefined); + expect(makeConcurrentCallStub.called).to.be.false; + expect(logStub.info.firstCall.args[0]).to.include(rulesFile); + }); + + it('returns undefined when rules file exists but payload is empty; arrays stay empty', async () => { + fileHelperStub.fileExistsSync.withArgs(rulesFile).returns(true); + fsUtilStub.readFile.withArgs(rulesFile, true).returns({}); + + const result = await importPublishingRules.start(); + + expect(result).to.equal(undefined); + expect(makeConcurrentCallStub.called).to.be.false; + expect(importPublishingRules['createdPublishingRules']).to.deep.equal([]); + expect(importPublishingRules['failedPublishingRules']).to.deep.equal([]); + }); + + it('passes one apiContent item per rule and binds serializeData to serializePublishingRules', async () => { + const rules = { + r1: { uid: 'r1', name: 'Rule 1' }, + r2: { uid: 'r2', name: 'Rule 2' }, + }; + fileHelperStub.fileExistsSync.callsFake((p: string) => { + if (p === rulesFile) return true; + if (p === workflowsExportFile || p === workflowMapperFile || p === envMapperFile || p === publishingMapperFile) { + return false; + } + return false; + }); + fsUtilStub.readFile.callsFake((p: string) => { + if (p === rulesFile) return rules; + return {}; + }); + + await importPublishingRules.start(); + + expect(makeConcurrentCallStub.calledOnce).to.be.true; + const callArgs = makeConcurrentCallStub.firstCall.args[0]; + expect(callArgs.apiContent).to.have.length(2); + expect(callArgs.processName).to.equal('import publishing rules'); + expect(callArgs.apiParams.entity).to.equal('create-publishing-rule'); + const serialized = callArgs.apiParams.serializeData({ + apiData: { uid: 'r1', name: 'Rule 1' }, + entity: 'create-publishing-rule', + }); + expect(serialized.apiData).to.deep.include({ name: 'Rule 1', uid: 'r1' }); + expect(serialized.entity).to.equal('create-publishing-rule'); + }); + + it('builds stageUidMapper from exported workflows and fetched target workflow stages by name', async () => { + fileHelperStub.fileExistsSync.callsFake((p: string) => { + if (p === rulesFile) return true; + if (p === workflowsExportFile) return true; + if (p === workflowMapperFile) return true; + if (p === envMapperFile || p === publishingMapperFile) return false; + return false; + }); + fsUtilStub.readFile.callsFake((p: string) => { + if (p === rulesFile) return { r1: { uid: 'r1', name: 'R' } }; + if (p === workflowsExportFile) { + return { + expWf: { workflow_stages: [{ uid: 'stage-old', name: 'Review' }] }, + }; + } + if (p === workflowMapperFile) return { oldWf: 'newWf' }; + return {}; + }); + + await importPublishingRules.start(); + + expect(importPublishingRules['stageUidMapper']).to.deep.equal({ 'stage-old': 'stage-new' }); + expect(mockStackClient.workflow.calledWith('newWf')).to.be.true; + }); + + it('returns { noSuccessMsg: true } when a rule fails to import (non-duplicate error)', async () => { + fileHelperStub.fileExistsSync.callsFake((p: string) => p === rulesFile); + fsUtilStub.readFile.callsFake((p: string) => (p === rulesFile ? { r1: { uid: 'r1', name: 'R' } } : {})); + + makeConcurrentCallStub.callsFake(async (env: any) => { + const { apiParams, apiContent } = env; + for (const element of apiContent) { + apiParams.apiData = element; + let opts = { ...apiParams, apiData: { ...element } }; + opts = apiParams.serializeData(opts); + if (opts.entity) { + await apiParams.reject({ error: new Error('network'), apiData: opts.apiData }); + } + } + }); + + const result = await importPublishingRules.start(); + + expect(result).to.deep.equal({ noSuccessMsg: true }); + expect(importPublishingRules['failedPublishingRules']).to.have.length(1); + expect(importPublishingRules['failedPublishingRules'][0].uid).to.equal('r1'); + expect(String(logStub.error.firstCall?.args[0] ?? '')).to.include('could not be imported'); + }); + + it('returns undefined when import succeeds with no failures', async () => { + fileHelperStub.fileExistsSync.callsFake((p: string) => p === rulesFile); + fsUtilStub.readFile.callsFake((p: string) => (p === rulesFile ? { r1: { uid: 'r1', name: 'R' } } : {})); + + makeConcurrentCallStub.callsFake(async (env: any) => { + const { apiParams, apiContent } = env; + for (const element of apiContent) { + apiParams.apiData = element; + let opts = { ...apiParams, apiData: { ...element } }; + opts = apiParams.serializeData(opts); + if (opts.entity) { + await apiParams.resolve({ response: { uid: 'new-r1' }, apiData: opts.apiData }); + } + } + }); + + const result = await importPublishingRules.start(); + + expect(result).to.equal(undefined); + expect(importPublishingRules['failedPublishingRules']).to.deep.equal([]); + expect(importPublishingRules['publishingRulesUidMapper']).to.deep.equal({ r1: 'new-r1' }); + expect(logStub.success.calledWith('Publishing rules have been imported successfully!', mockImportConfig.context)).to.be + .true; + }); + }); + + describe('serializePublishingRules', () => { + it('clears entity when rule uid already in mapper; leaves apiData.uid unchanged', () => { + importPublishingRules['publishingRulesUidMapper'] = { 'rule-1': 'mapped-1' }; + + const apiOptions: any = { + apiData: { uid: 'rule-1', name: 'N' }, + entity: 'create-publishing-rule', + }; + + const out = importPublishingRules.serializePublishingRules(apiOptions); + + expect(out.entity).to.equal(undefined); + expect(out.apiData).to.deep.equal({ uid: 'rule-1', name: 'N' }); + expect(String(logStub.info.firstCall?.args[0] ?? '')).to.match(/already exists\. Skipping/); + }); + + it('remaps workflow, environment, workflow_stage and strips approvers; apiData carries uid for completion handler', () => { + const pr = importPublishingRules as any; + pr.workflowUidMapper = { wfOld: 'wfNew' }; + pr.envUidMapper = { envOld: 'envNew' }; + Object.keys(pr.stageUidMapper).forEach((k) => delete pr.stageUidMapper[k]); + pr.stageUidMapper.stOld = 'stNew'; + + const apiOptions: any = { + apiData: { + uid: 'pr-1', + name: 'PR', + workflow: 'wfOld', + environment: 'envOld', + workflow_stage: 'stOld', + approvers: { roles: ['r1'], users: ['u1'] }, + }, + entity: 'create-publishing-rule', + }; + + const out = importPublishingRules.serializePublishingRules(apiOptions); + + expect(out.entity).to.equal('create-publishing-rule'); + expect(out.apiData).to.deep.equal({ + uid: 'pr-1', + name: 'PR', + workflow: 'wfNew', + environment: 'envNew', + workflow_stage: 'stNew', + approvers: { roles: [], users: [] }, + }); + const infoArgs = logStub.info.getCalls().map((c) => c.args[0]); + expect(infoArgs.some((msg) => String(msg).includes('Skipping import of publish rule approver'))).to.be.true; + }); + }); + + describe('importPublishingRules callbacks', () => { + beforeEach(() => { + importPublishingRules['publishingRules'] = { r1: { uid: 'r1', name: 'R' } }; + }); + + it('onSuccess updates mapper and persists uid-mapping.json with expected payload', async () => { + await (importPublishingRules as any).importPublishingRules(); + + const onSuccess = makeConcurrentCallStub.firstCall.args[0].apiParams.resolve; + await onSuccess({ response: { uid: 'created-uid', name: 'R' }, apiData: { uid: 'r1', name: 'R' } }); + + expect(importPublishingRules['createdPublishingRules']).to.deep.equal([{ uid: 'created-uid', name: 'R' }]); + expect(importPublishingRules['publishingRulesUidMapper']).to.deep.equal({ r1: 'created-uid' }); + expect(fsUtilStub.writeFile.calledWith(publishingMapperFile, { r1: 'created-uid' })).to.be.true; + }); + + it('onReject for duplicate error does not append to failedPublishingRules', async () => { + await (importPublishingRules as any).importPublishingRules(); + + const onReject = makeConcurrentCallStub.firstCall.args[0].apiParams.reject; + await onReject({ + error: { errors: { name: 'taken' } }, + apiData: { uid: 'r1', name: 'R' }, + }); + + expect(importPublishingRules['failedPublishingRules']).to.deep.equal([]); + expect(logStub.info.calledWith(`Publishing rule 'r1' already exists`, mockImportConfig.context)).to.be.true; + }); + }); +}); diff --git a/packages/contentstack-import/test/unit/utils/extension-helper.test.ts b/packages/contentstack-import/test/unit/utils/extension-helper.test.ts index c9b567218..fa537a84e 100644 --- a/packages/contentstack-import/test/unit/utils/extension-helper.test.ts +++ b/packages/contentstack-import/test/unit/utils/extension-helper.test.ts @@ -54,6 +54,11 @@ describe('Extension Helper', () => { webhooks: { dirName: 'webhooks', fileName: 'webhooks.json' }, releases: { dirName: 'releases', fileName: 'releases.json', invalidKeys: ['uid'] }, workflows: { dirName: 'workflows', fileName: 'workflows.json', invalidKeys: ['uid'] }, + 'publishing-rules': { + dirName: 'workflows', + fileName: 'publishing-rules.json', + invalidKeys: ['uid'], + }, assets: { dirName: 'assets', assetBatchLimit: 10, diff --git a/packages/contentstack-import/test/unit/utils/publishing-rules-helper.test.ts b/packages/contentstack-import/test/unit/utils/publishing-rules-helper.test.ts new file mode 100644 index 000000000..1453b68eb --- /dev/null +++ b/packages/contentstack-import/test/unit/utils/publishing-rules-helper.test.ts @@ -0,0 +1,57 @@ +import { expect } from 'chai'; +import { parseErrorPayload, isDuplicatePublishingRuleError } from '../../../src/utils/publishing-rules-helper'; + +describe('publishing-rules-helper', () => { + describe('parseErrorPayload', () => { + it('returns the object when error has errors', () => { + const err = { errors: { name: 'taken' } }; + expect(parseErrorPayload(err)).to.deep.equal({ errors: { name: 'taken' } }); + }); + + it('parses JSON from message string', () => { + const err = { message: JSON.stringify({ error_message: 'already exists' }) }; + expect(parseErrorPayload(err)).to.deep.equal({ error_message: 'already exists' }); + }); + + it('returns null for invalid JSON in message', () => { + expect(parseErrorPayload({ message: 'not-json{' })).to.equal(null); + }); + + it('returns null for non-object', () => { + expect(parseErrorPayload(null)).to.equal(null); + expect(parseErrorPayload('x')).to.equal(null); + }); + + it('returns null when object has no errors or parseable message', () => { + expect(parseErrorPayload({ foo: 1 })).to.equal(null); + }); + }); + + describe('isDuplicatePublishingRuleError', () => { + it('returns true when errors.name is set', () => { + expect(isDuplicatePublishingRuleError({ errors: { name: 'x' } }, {})).to.equal(true); + }); + + it('returns true when errors.publishing_rule.name is set', () => { + expect(isDuplicatePublishingRuleError({ errors: { 'publishing_rule.name': 'x' } }, {})).to.equal(true); + }); + + it('returns true when errors.publish_rule.name is set', () => { + expect(isDuplicatePublishingRuleError({ errors: { 'publish_rule.name': 'x' } }, {})).to.equal(true); + }); + + it('returns true when error_message matches duplicate wording', () => { + expect(isDuplicatePublishingRuleError({ error_message: 'Rule already exists' }, {})).to.equal(true); + }); + + it('reads errors from raw when parsed is null', () => { + const raw = { errors: { name: 'dup' } }; + expect(isDuplicatePublishingRuleError(null, raw)).to.equal(true); + }); + + it('returns false when no duplicate signals', () => { + expect(isDuplicatePublishingRuleError({ errors: { other: 'x' } }, {})).to.equal(false); + expect(isDuplicatePublishingRuleError({ error_message: 'timeout' }, {})).to.equal(false); + }); + }); +}); diff --git a/packages/contentstack-seed/package.json b/packages/contentstack-seed/package.json index 31f7a7b2f..26401c0da 100644 --- a/packages/contentstack-seed/package.json +++ b/packages/contentstack-seed/package.json @@ -1,11 +1,11 @@ { "name": "@contentstack/cli-cm-seed", "description": "create a Stack from existing content types, entries, assets, etc.", - "version": "1.15.1", + "version": "1.15.2", "author": "Contentstack", "bugs": "https://github.com/contentstack/cli/issues", "dependencies": { - "@contentstack/cli-cm-import": "~1.32.1", + "@contentstack/cli-cm-import": "~1.33.0", "@contentstack/cli-command": "~1.8.1", "@contentstack/cli-utilities": "~1.18.2", "inquirer": "8.2.7", diff --git a/packages/contentstack-variants/package.json b/packages/contentstack-variants/package.json index 14639db2c..fe5cadb48 100644 --- a/packages/contentstack-variants/package.json +++ b/packages/contentstack-variants/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/cli-variants", - "version": "1.4.2", + "version": "1.4.3", "description": "Variants plugin", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/contentstack-variants/src/types/export-config.ts b/packages/contentstack-variants/src/types/export-config.ts index f9336bc96..8b782c2b0 100644 --- a/packages/contentstack-variants/src/types/export-config.ts +++ b/packages/contentstack-variants/src/types/export-config.ts @@ -17,6 +17,7 @@ export type Modules = | 'content-types' | 'custom-roles' | 'workflows' + | 'publishing-rules' | 'labels' | 'marketplace-apps' | 'taxonomies' @@ -88,6 +89,13 @@ export interface DefaultConfig { invalidKeys: string[]; dependencies?: Modules[]; }; + 'publishing-rules': { + dirName: string; + fileName: string; + invalidKeys: string[]; + dependencies?: Modules[]; + limit?: number; + }; globalfields: { dirName: string; fileName: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0c288ef05..5847da2b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,7 +18,7 @@ importers: version: 9.1.7 pnpm: specifier: ^10.28.0 - version: 10.33.0 + version: 10.33.2 packages/contentstack-audit: dependencies: @@ -108,7 +108,7 @@ importers: packages/contentstack-bootstrap: dependencies: '@contentstack/cli-cm-seed': - specifier: ~1.15.1 + specifier: ~1.15.2 version: link:../contentstack-seed '@contentstack/cli-command': specifier: ~1.8.1 @@ -306,10 +306,10 @@ importers: specifier: ^1.6.0 version: 1.6.0 '@contentstack/cli-cm-export': - specifier: ~1.24.2 + specifier: ~1.25.0 version: link:../contentstack-export '@contentstack/cli-cm-import': - specifier: ~1.32.2 + specifier: ~1.33.0 version: link:../contentstack-import '@contentstack/cli-command': specifier: ~1.8.1 @@ -879,7 +879,7 @@ importers: packages/contentstack-seed: dependencies: '@contentstack/cli-cm-import': - specifier: ~1.32.1 + specifier: ~1.33.0 version: link:../contentstack-import '@contentstack/cli-command': specifier: ~1.8.1 @@ -1022,44 +1022,44 @@ packages: resolution: {integrity: sha512-0XLrOT4Cm3NEhhiME7l/8LbTXS4KdsbR4dSrY207KNKTcHLLTZ9EXt4ZpgnTfLvWQF3pGP2us4Zi1fYLo0N+Ow==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.974.2': - resolution: {integrity: sha512-oav5AOAz+1XkwUfp6SrEm42UPDpUP5D4jNYXkDwFR1VfWqYX62+jpytdfzURmJ9McSoJIQwi0OJlC4oCi6t0VQ==} + '@aws-sdk/core@3.974.5': + resolution: {integrity: sha512-lMPlYlYfQdNZhlkJgnkmESwrY+hNh3PljmZ+37oAqLNdJ6rnILAwFSyc6B3bJeDOtMORNnMQIej0aTRuOlDyhQ==} engines: {node: '>=20.0.0'} '@aws-sdk/crc64-nvme@3.972.7': resolution: {integrity: sha512-QUagVVBbC8gODCF6e1aV0mE2TXWB9Opz4k8EJFdNrujUVQm5R4AjJa1mpOqzwOuROBzqJU9zawzig7M96L8Ejg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.28': - resolution: {integrity: sha512-87GdRJ2OR0qR4VkMjXN/SZi66DZsunW2qQCbtw9rKw3Y7JurFi6tQWYKOSLY/gOADrU6OxGqFmdw3hKzZqDZOQ==} + '@aws-sdk/credential-provider-env@3.972.31': + resolution: {integrity: sha512-X/yGB73LmDW/6MdDJGCDzZBUXnM3ys4vs9l+5ZTJmiEswDdP1OjeoAFlFjVGS9o4KB2wZWQ9KOfdVNSSK6Ep3w==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.30': - resolution: {integrity: sha512-6quozmW2PKwBJTUQLb+lk1q8w5Pm45qaqhx4Tld9EIqYYQOVGj+MT0a8NRVS7QgWJj7rzGlB7rQu3KYBFHemJw==} + '@aws-sdk/credential-provider-http@3.972.33': + resolution: {integrity: sha512-c0ZF+lwoWVvX5iCaGKL5T/4DnIw88CGqxA0BcBs3U86mIp5EZYPVg+KSPkMXOyokmADvNewiMUfSG2uFwjRp0g==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.32': - resolution: {integrity: sha512-Nkr+UKtczZlocUjc6g96WzQadZSIZO/HVXPki4qbfaVOZYSbfLQKWKfADtJ0kGYsCvSYOZrO66tSc9dkboUt/w==} + '@aws-sdk/credential-provider-ini@3.972.35': + resolution: {integrity: sha512-jsU4u/cRkKFLKQS0k918FQ27fzXLG5ENiLWQMYE6581zLeI2hWh04ptlrvZMB3wJT/5d+vSzJk74X1CMFr4y8Q==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.32': - resolution: {integrity: sha512-UxgwT1HmZz1QPXuBy5ZUPJNFXOSlhwdQL61eGhWRthF0xRrT02BCOVJ1p5Ejg5AXfnESTWoKPJ7v/sCkNUtB9g==} + '@aws-sdk/credential-provider-login@3.972.35': + resolution: {integrity: sha512-5oa3j0cA50jPqgNhZ9XdJVopuzUf1klRb28/2MfLYWWiPi9DRVvbrBWT+DidbHTT36520VuXZJahQwR+YgSjrg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.33': - resolution: {integrity: sha512-6pGQnEdSeRvBViTQh/FwaRKB38a3Th+W2mVxuvqAd2Z1Ayo3e6eJ5QqJoZwEMwR6xoxkl3wz3qAfiB1xRhMC+w==} + '@aws-sdk/credential-provider-node@3.972.36': + resolution: {integrity: sha512-4nT2T8Z7vH8KE9EdjEsuIlHpZSlcaK2PrKbQBjuUGU46BCCzF3WvP0u0Uiosni3Ykmmn4rWLVawoOCLotUtCbg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.28': - resolution: {integrity: sha512-CRAlD8u6oNBhjnX/3ekVGocarD+lFmEn/qeDzytgIdmwrmwMJGFPqS9lGwEfhOTihZKrQ0xSp3z6paX+iXJJhA==} + '@aws-sdk/credential-provider-process@3.972.31': + resolution: {integrity: sha512-eKeT4MXumpBJsrDLCYcSzIkFPVTFn/es7It2oogp2OhU/ic7P/+xzFpQx9ZhwtXS57Mc5S42BPWi7lHmvs/nYg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.32': - resolution: {integrity: sha512-whhmQghRYOt9mJxFyVMhX7eB8n0oA25OCvqoR7dzFAZjmioCkf7WVB22Bc6llM5cFpBXFX7s4Jv+xVq32VPGWg==} + '@aws-sdk/credential-provider-sso@3.972.35': + resolution: {integrity: sha512-bCuBdfnj0KGDMdLp6utMTLiJcFN2ek9EgZinxQZZSc3FxjJ/HSqeqab2cjbnoNfy8RM6suDCsRkmVY1izp9I+A==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.32': - resolution: {integrity: sha512-Z0Y0LDaqyQDznlmr9gv6n4+eWKKWNgmi9j5L6RENr6wyOCguhO8FRPmqDbVLSw0DPdMqICKnA3PurJiS8bD6Cw==} + '@aws-sdk/credential-provider-web-identity@3.972.35': + resolution: {integrity: sha512-swW6Bwvl8lanyEMtZOWE/oR6yqcRQH4HTQZUVsnDVgoXvRjRywpYpLv2BWwjUFyjPrqsdX6FeTkf4tMSe/qFTQ==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-bucket-endpoint@3.972.10': @@ -1070,8 +1070,8 @@ packages: resolution: {integrity: sha512-2Yn0f1Qiq/DjxYR3wfI3LokXnjOhFM7Ssn4LTdFDIxRMCE6I32MAsVnhPX1cUZsuVA9tiZtwwhlSLAtFGxAZlQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.974.10': - resolution: {integrity: sha512-R9oqyD1hR7aF2UQaYBo90/ILNn8Sq7gl/2Y4WkDDvsaqklqPomso++sFbgYgNmN/Kfx6gqvJwcjSkxJHEBK1tQ==} + '@aws-sdk/middleware-flexible-checksums@3.974.13': + resolution: {integrity: sha512-b6QUe2hQX9XsnCzp6mtzVaERhganDKeb8lmGL6pVhr7rRVH9S9keDFW7uKytuuqmcY5943FixoGqn/QL+sbUBA==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-host-header@3.972.10': @@ -1090,32 +1090,32 @@ packages: resolution: {integrity: sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-s3@3.972.31': - resolution: {integrity: sha512-5hS08Fp0Rm+59uGCmkWhZmveXiA7OUV7Wa+IARejdzf9JTZ1qAVeIOE9JoBpsLPvUgEjmsGNHBuFbtGmYyqiqQ==} + '@aws-sdk/middleware-sdk-s3@3.972.34': + resolution: {integrity: sha512-/UL96JKjsjdodcRRMKl99tLQvK6Oi9ptLC9iU1yiTF/ruaDX0mtBBtnLNZDxIZRJOCVOtB49ed1YaTadqygk8Q==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-ssec@3.972.10': resolution: {integrity: sha512-Gli9A0u8EVVb+5bFDGS/QbSVg28w/wpEidg1ggVcSj65BDTdGR6punsOcVjqdiu1i42WHWo51MCvARPIIz9juw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.32': - resolution: {integrity: sha512-HQ0x9DDKqLZOGhDiL2eicYXXkYT5dogE4mw0lAfHCpJ6t7MM0PNIsJl2TZzWKU9SpBzOMXHRa7K6ZLKUJu1y0w==} + '@aws-sdk/middleware-user-agent@3.972.35': + resolution: {integrity: sha512-hOFWNOjVmOocpRlrU04nYxjMOeoe0Obu5AXEuhB8zblMCPl3cG1hdluQCZERRKFyhMQjwZnDbhSHjoMUjetFGw==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.997.0': - resolution: {integrity: sha512-4bI5GHjUiY5R8N6PtchpG6tW2Dl8I2IcZNg3JwqwxHRXjfvQlPoo4VMknG4qkd5W0t3Y20rQ6C7pSR561YG5JQ==} + '@aws-sdk/nested-clients@3.997.3': + resolution: {integrity: sha512-SivE6GP228IVgfsrr2c/vqTg95X0Qj39Yw4uIrcddpkUzIltNMoNOR62leHOLhODfjv9K8X2mPTwS69A5kT0nQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.972.12': - resolution: {integrity: sha512-QQI43Mxd53nBij0pm8HXC+t4IOC6gnhhZfzxE0OATQyO6QfPV4e+aTIRRuAJKA6Nig/cR8eLwPryqYTX9ZrjAQ==} + '@aws-sdk/region-config-resolver@3.972.13': + resolution: {integrity: sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==} engines: {node: '>=20.0.0'} - '@aws-sdk/signature-v4-multi-region@3.996.19': - resolution: {integrity: sha512-7Sy8+GhfwUi06NQNLplxuJuXMKJURDsNQfK8yTW6E9wN2J1B+8S5dWZG7vg3InvPPhaXqkcYTr8pzeE+dLjMbQ==} + '@aws-sdk/signature-v4-multi-region@3.996.22': + resolution: {integrity: sha512-/rXhMXteD+BqhFd0nYprAgcZ/KtU+963uftPqd3tiFcFfooHZINXUGtOmo2SQjRVauCTNqIEzkwuSETdZFqTTA==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1033.0': - resolution: {integrity: sha512-/TsXhqjyRAFb0xVgmbFAha3cJfZdWjnyn6ohJ3AB4E3peLgxNcmKfYr45hruHymyJAydiHoXC3N1a8qgl41cog==} + '@aws-sdk/token-providers@3.1036.0': + resolution: {integrity: sha512-aNSJ6jjDYayxN9ZA1JpycVScX93Lx03kKZ1EXt3DGOTahcWVLJj3oLAlop0xKP+vP2Ga2t49p1tEaMkTbCCaZA==} engines: {node: '>=20.0.0'} '@aws-sdk/types@3.973.8': @@ -1126,8 +1126,8 @@ packages: resolution: {integrity: sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==} engines: {node: '>=20.0.0'} - '@aws-sdk/util-endpoints@3.996.7': - resolution: {integrity: sha512-ty4LQxN1QC+YhUP28NfEgZDEGXkyqOQy+BDriBozqHsrYO4JMgiPhfizqOGF7P+euBTZ5Ez6SKlLAMCLo8tzmw==} + '@aws-sdk/util-endpoints@3.996.8': + resolution: {integrity: sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==} engines: {node: '>=20.0.0'} '@aws-sdk/util-locate-window@3.965.5': @@ -1137,8 +1137,8 @@ packages: '@aws-sdk/util-user-agent-browser@3.972.10': resolution: {integrity: sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==} - '@aws-sdk/util-user-agent-node@3.973.18': - resolution: {integrity: sha512-Nh4YvAL0Mzv5jBvzXLFL0tLf7WPrRMnYZQ5jlFuyS0xiVJQsObMUKAkbYjmt/e04wpQqUaa+Is7k+mBr89A9yA==} + '@aws-sdk/util-user-agent-node@3.973.21': + resolution: {integrity: sha512-Av4UHTcAWgdvbN0IP9pbtf4Qa1+6LtJqQdZWj5pLn5J67w0pnJJAZZ+7JPPcj2KN3378zD2JDM9DwJKEyvyMTQ==} engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -1146,8 +1146,8 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.972.18': - resolution: {integrity: sha512-BMDNVG1ETXRhl1tnisQiYBef3RShJ1kfZA7x7afivTFMLirfHNTb6U71K569HNXhSXbQZsweHvSDZ6euBw8hPA==} + '@aws-sdk/xml-builder@3.972.19': + resolution: {integrity: sha512-Cw8IOMdBUEIl8ZlhRC3Dc/E64D5B5/8JhV6vhPLiPfJwcRC84S6F8aBOIi/N4vR9ZyA4I5Cc0Ateb/9EHaJXeQ==} engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.4': @@ -1915,6 +1915,9 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@nodable/entities@2.1.0': + resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2048,8 +2051,8 @@ packages: resolution: {integrity: sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==} engines: {node: '>=18.0.0'} - '@smithy/core@3.23.16': - resolution: {integrity: sha512-JStomOrINQA1VqNEopLsgcdgwd42au7mykKqVr30XFw89wLt9sDxJDi4djVPRwQmmzyTGy/uOvTc2ultMpFi1w==} + '@smithy/core@3.23.17': + resolution: {integrity: sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ==} engines: {node: '>=18.0.0'} '@smithy/credential-provider-imds@4.2.14': @@ -2112,16 +2115,16 @@ packages: resolution: {integrity: sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.31': - resolution: {integrity: sha512-KJPdCIN2kOE2aGmqZd7eUTr4WQwOGgtLWgUkswGJggs7rBcQYQjcZMEDa3C0DwbOiXS9L8/wDoQHkfxBYLfiLw==} + '@smithy/middleware-endpoint@4.4.32': + resolution: {integrity: sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.5.4': - resolution: {integrity: sha512-/z7nIFK+ZRW3Ie/l3NEVGdy34LvmEOzBrtBAvgWZ/4PrKX0xP3kWm8pkfcwUk523SqxZhdbQP9JSXgjF77Uhpw==} + '@smithy/middleware-retry@4.5.5': + resolution: {integrity: sha512-wnYOpB5vATFKWrY2Z9Alb0KhjZI6AbzU6Fbz3Hq2GnURdRYWB4q+qWivQtSTwXcmWUA3MZ6krfwL6Cq5MAbxsA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.2.19': - resolution: {integrity: sha512-Q6y+W9h3iYVMCKWDoVge+OC1LKFqbEKaq8SIWG2X2bWJRpd/6dDLyICcNLT6PbjH3Rr6bmg/SeDB25XFOFfeEw==} + '@smithy/middleware-serde@4.2.20': + resolution: {integrity: sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==} engines: {node: '>=18.0.0'} '@smithy/middleware-stack@4.2.14': @@ -2132,8 +2135,8 @@ packages: resolution: {integrity: sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.6.0': - resolution: {integrity: sha512-P734cAoTFtuGfWa/R3jgBnGlURt2w9bYEBwQNMKf58sRM9RShirB2mKwLsVP+jlG/wxpCu8abv8NxdUts8tdLA==} + '@smithy/node-http-handler@4.6.1': + resolution: {integrity: sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg==} engines: {node: '>=18.0.0'} '@smithy/property-provider@4.2.14': @@ -2164,8 +2167,8 @@ packages: resolution: {integrity: sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.12.12': - resolution: {integrity: sha512-daO7SJn4eM6ArbmrEs+/BTbH7af8AEbSL3OMQdcRvvn8tuUcR5rU2n6DgxIV53aXMS42uwK8NgKKCh5XgqYOPQ==} + '@smithy/smithy-client@4.12.13': + resolution: {integrity: sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==} engines: {node: '>=18.0.0'} '@smithy/types@4.14.1': @@ -2200,12 +2203,12 @@ packages: resolution: {integrity: sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.48': - resolution: {integrity: sha512-hxVRVPYaRDWa6YQdse1aWX1qrksmLsvNyGBKdc32q4jFzSjxYVNWfstknAfR228TnzS4tzgswXRuYIbhXBuXFQ==} + '@smithy/util-defaults-mode-browser@4.3.49': + resolution: {integrity: sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.53': - resolution: {integrity: sha512-ybgCk+9JdBq8pYC8Y6U5fjyS8e4sboyAShetxPNL0rRBtaVl56GSFAxsolVBIea1tXR4LPIzL8i6xqmcf0+DCQ==} + '@smithy/util-defaults-mode-node@4.2.54': + resolution: {integrity: sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==} engines: {node: '>=18.0.0'} '@smithy/util-endpoints@3.4.2': @@ -2220,12 +2223,12 @@ packages: resolution: {integrity: sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==} engines: {node: '>=18.0.0'} - '@smithy/util-retry@4.3.3': - resolution: {integrity: sha512-idjUvd4M9Jj6rXkhqw4H4reHoweuK4ZxYWyOrEp4N2rOF5VtaOlQGLDQJva/8WanNXk9ScQtsAb7o5UHGvFm4A==} + '@smithy/util-retry@4.3.4': + resolution: {integrity: sha512-FY1UQQ1VFmMwiYp1GVS4MeaGD5O0blLNYK0xCRHU+mJgeoH/hSY8Ld8sJWKQ6uznkh14HveRGQJncgPyNl9J+A==} engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.5.24': - resolution: {integrity: sha512-na5vv2mBSDzXewLEEoWGI7LQQkfpmFEomBsmOpzLFjqGctm0iMwXY5lAwesY9pIaErkccW0qzEOUcYP+WKneXg==} + '@smithy/util-stream@4.5.25': + resolution: {integrity: sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==} engines: {node: '>=18.0.0'} '@smithy/util-uri-escape@4.2.2': @@ -2746,8 +2749,8 @@ packages: ajv: optional: true - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} @@ -2966,8 +2969,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.20: - resolution: {integrity: sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==} + baseline-browser-mapping@2.10.21: + resolution: {integrity: sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA==} engines: {node: '>=6.0.0'} hasBin: true @@ -3078,8 +3081,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001788: - resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==} + caniuse-lite@1.0.30001790: + resolution: {integrity: sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw==} capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} @@ -3533,8 +3536,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.340: - resolution: {integrity: sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==} + electron-to-chromium@1.5.344: + resolution: {integrity: sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==} elegant-spinner@1.0.1: resolution: {integrity: sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==} @@ -3553,8 +3556,8 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enhanced-resolve@5.20.1: - resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + enhanced-resolve@5.21.0: + resolution: {integrity: sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -3925,8 +3928,8 @@ packages: fast-xml-builder@1.1.5: resolution: {integrity: sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==} - fast-xml-parser@5.5.8: - resolution: {integrity: sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ==} + fast-xml-parser@5.7.1: + resolution: {integrity: sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA==} hasBin: true fastest-levenshtein@1.0.16: @@ -5226,8 +5229,8 @@ packages: resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} engines: {node: '>=8'} - node-releases@2.0.37: - resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} + node-releases@2.0.38: + resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -5494,8 +5497,8 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} - pnpm@10.33.0: - resolution: {integrity: sha512-EFaLtKavtYyes2MNqQzJUWQXq+vT+rvmc58K55VyjaFJHp21pUTHatjrdXD1xLs9bGN7LLQb/c20f6gjyGSTGQ==} + pnpm@10.33.2: + resolution: {integrity: sha512-qQ+vb+6rca1sblf5Tg/hoS9dzCLNdU20CulZPraj4LaxLjVAIYuzeuCDQEsfLObbKkEh6XmCm0r/lLmfSdoc+A==} engines: {node: '>=18.12'} hasBin: true @@ -6109,8 +6112,8 @@ packages: resolution: {integrity: sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==} engines: {node: '>=4.0.0'} - tapable@2.3.2: - resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==} + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} tar@7.5.13: @@ -6651,42 +6654,42 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.2 - '@aws-sdk/credential-provider-node': 3.972.33 + '@aws-sdk/core': 3.974.5 + '@aws-sdk/credential-provider-node': 3.972.36 '@aws-sdk/middleware-host-header': 3.972.10 '@aws-sdk/middleware-logger': 3.972.10 '@aws-sdk/middleware-recursion-detection': 3.972.11 - '@aws-sdk/middleware-user-agent': 3.972.32 - '@aws-sdk/region-config-resolver': 3.972.12 + '@aws-sdk/middleware-user-agent': 3.972.35 + '@aws-sdk/region-config-resolver': 3.972.13 '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.7 + '@aws-sdk/util-endpoints': 3.996.8 '@aws-sdk/util-user-agent-browser': 3.972.10 - '@aws-sdk/util-user-agent-node': 3.973.18 + '@aws-sdk/util-user-agent-node': 3.973.21 '@smithy/config-resolver': 4.4.17 - '@smithy/core': 3.23.16 + '@smithy/core': 3.23.17 '@smithy/fetch-http-handler': 5.3.17 '@smithy/hash-node': 4.2.14 '@smithy/invalid-dependency': 4.2.14 '@smithy/middleware-content-length': 4.2.14 - '@smithy/middleware-endpoint': 4.4.31 - '@smithy/middleware-retry': 4.5.4 - '@smithy/middleware-serde': 4.2.19 + '@smithy/middleware-endpoint': 4.4.32 + '@smithy/middleware-retry': 4.5.5 + '@smithy/middleware-serde': 4.2.20 '@smithy/middleware-stack': 4.2.14 '@smithy/node-config-provider': 4.3.14 - '@smithy/node-http-handler': 4.6.0 + '@smithy/node-http-handler': 4.6.1 '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.12 + '@smithy/smithy-client': 4.12.13 '@smithy/types': 4.14.1 '@smithy/url-parser': 4.2.14 '@smithy/util-base64': 4.3.2 '@smithy/util-body-length-browser': 4.2.2 '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.48 - '@smithy/util-defaults-mode-node': 4.2.53 + '@smithy/util-defaults-mode-browser': 4.3.49 + '@smithy/util-defaults-mode-node': 4.2.54 '@smithy/util-endpoints': 3.4.2 '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.3 - '@smithy/util-stream': 4.5.24 + '@smithy/util-retry': 4.3.4 + '@smithy/util-stream': 4.5.25 '@smithy/util-utf8': 4.2.2 '@smithy/util-waiter': 4.2.16 tslib: 2.8.1 @@ -6698,26 +6701,26 @@ snapshots: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.2 - '@aws-sdk/credential-provider-node': 3.972.33 + '@aws-sdk/core': 3.974.5 + '@aws-sdk/credential-provider-node': 3.972.36 '@aws-sdk/middleware-bucket-endpoint': 3.972.10 '@aws-sdk/middleware-expect-continue': 3.972.10 - '@aws-sdk/middleware-flexible-checksums': 3.974.10 + '@aws-sdk/middleware-flexible-checksums': 3.974.13 '@aws-sdk/middleware-host-header': 3.972.10 '@aws-sdk/middleware-location-constraint': 3.972.10 '@aws-sdk/middleware-logger': 3.972.10 '@aws-sdk/middleware-recursion-detection': 3.972.11 - '@aws-sdk/middleware-sdk-s3': 3.972.31 + '@aws-sdk/middleware-sdk-s3': 3.972.34 '@aws-sdk/middleware-ssec': 3.972.10 - '@aws-sdk/middleware-user-agent': 3.972.32 - '@aws-sdk/region-config-resolver': 3.972.12 - '@aws-sdk/signature-v4-multi-region': 3.996.19 + '@aws-sdk/middleware-user-agent': 3.972.35 + '@aws-sdk/region-config-resolver': 3.972.13 + '@aws-sdk/signature-v4-multi-region': 3.996.22 '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.7 + '@aws-sdk/util-endpoints': 3.996.8 '@aws-sdk/util-user-agent-browser': 3.972.10 - '@aws-sdk/util-user-agent-node': 3.973.18 + '@aws-sdk/util-user-agent-node': 3.973.21 '@smithy/config-resolver': 4.4.17 - '@smithy/core': 3.23.16 + '@smithy/core': 3.23.17 '@smithy/eventstream-serde-browser': 4.2.14 '@smithy/eventstream-serde-config-resolver': 4.3.14 '@smithy/eventstream-serde-node': 4.2.14 @@ -6728,44 +6731,45 @@ snapshots: '@smithy/invalid-dependency': 4.2.14 '@smithy/md5-js': 4.2.14 '@smithy/middleware-content-length': 4.2.14 - '@smithy/middleware-endpoint': 4.4.31 - '@smithy/middleware-retry': 4.5.4 - '@smithy/middleware-serde': 4.2.19 + '@smithy/middleware-endpoint': 4.4.32 + '@smithy/middleware-retry': 4.5.5 + '@smithy/middleware-serde': 4.2.20 '@smithy/middleware-stack': 4.2.14 '@smithy/node-config-provider': 4.3.14 - '@smithy/node-http-handler': 4.6.0 + '@smithy/node-http-handler': 4.6.1 '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.12 + '@smithy/smithy-client': 4.12.13 '@smithy/types': 4.14.1 '@smithy/url-parser': 4.2.14 '@smithy/util-base64': 4.3.2 '@smithy/util-body-length-browser': 4.2.2 '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.48 - '@smithy/util-defaults-mode-node': 4.2.53 + '@smithy/util-defaults-mode-browser': 4.3.49 + '@smithy/util-defaults-mode-node': 4.2.54 '@smithy/util-endpoints': 3.4.2 '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.3 - '@smithy/util-stream': 4.5.24 + '@smithy/util-retry': 4.3.4 + '@smithy/util-stream': 4.5.25 '@smithy/util-utf8': 4.2.2 '@smithy/util-waiter': 4.2.16 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.974.2': + '@aws-sdk/core@3.974.5': dependencies: '@aws-sdk/types': 3.973.8 - '@aws-sdk/xml-builder': 3.972.18 - '@smithy/core': 3.23.16 + '@aws-sdk/xml-builder': 3.972.19 + '@smithy/core': 3.23.17 '@smithy/node-config-provider': 4.3.14 '@smithy/property-provider': 4.2.14 '@smithy/protocol-http': 5.3.14 '@smithy/signature-v4': 5.3.14 - '@smithy/smithy-client': 4.12.12 + '@smithy/smithy-client': 4.12.13 '@smithy/types': 4.14.1 '@smithy/util-base64': 4.3.2 '@smithy/util-middleware': 4.2.14 + '@smithy/util-retry': 4.3.4 '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 @@ -6774,37 +6778,37 @@ snapshots: '@smithy/types': 4.14.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.28': + '@aws-sdk/credential-provider-env@3.972.31': dependencies: - '@aws-sdk/core': 3.974.2 + '@aws-sdk/core': 3.974.5 '@aws-sdk/types': 3.973.8 '@smithy/property-provider': 4.2.14 '@smithy/types': 4.14.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.30': + '@aws-sdk/credential-provider-http@3.972.33': dependencies: - '@aws-sdk/core': 3.974.2 + '@aws-sdk/core': 3.974.5 '@aws-sdk/types': 3.973.8 '@smithy/fetch-http-handler': 5.3.17 - '@smithy/node-http-handler': 4.6.0 + '@smithy/node-http-handler': 4.6.1 '@smithy/property-provider': 4.2.14 '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.12 + '@smithy/smithy-client': 4.12.13 '@smithy/types': 4.14.1 - '@smithy/util-stream': 4.5.24 + '@smithy/util-stream': 4.5.25 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.32': + '@aws-sdk/credential-provider-ini@3.972.35': dependencies: - '@aws-sdk/core': 3.974.2 - '@aws-sdk/credential-provider-env': 3.972.28 - '@aws-sdk/credential-provider-http': 3.972.30 - '@aws-sdk/credential-provider-login': 3.972.32 - '@aws-sdk/credential-provider-process': 3.972.28 - '@aws-sdk/credential-provider-sso': 3.972.32 - '@aws-sdk/credential-provider-web-identity': 3.972.32 - '@aws-sdk/nested-clients': 3.997.0 + '@aws-sdk/core': 3.974.5 + '@aws-sdk/credential-provider-env': 3.972.31 + '@aws-sdk/credential-provider-http': 3.972.33 + '@aws-sdk/credential-provider-login': 3.972.35 + '@aws-sdk/credential-provider-process': 3.972.31 + '@aws-sdk/credential-provider-sso': 3.972.35 + '@aws-sdk/credential-provider-web-identity': 3.972.35 + '@aws-sdk/nested-clients': 3.997.3 '@aws-sdk/types': 3.973.8 '@smithy/credential-provider-imds': 4.2.14 '@smithy/property-provider': 4.2.14 @@ -6814,10 +6818,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.972.32': + '@aws-sdk/credential-provider-login@3.972.35': dependencies: - '@aws-sdk/core': 3.974.2 - '@aws-sdk/nested-clients': 3.997.0 + '@aws-sdk/core': 3.974.5 + '@aws-sdk/nested-clients': 3.997.3 '@aws-sdk/types': 3.973.8 '@smithy/property-provider': 4.2.14 '@smithy/protocol-http': 5.3.14 @@ -6827,14 +6831,14 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.972.33': + '@aws-sdk/credential-provider-node@3.972.36': dependencies: - '@aws-sdk/credential-provider-env': 3.972.28 - '@aws-sdk/credential-provider-http': 3.972.30 - '@aws-sdk/credential-provider-ini': 3.972.32 - '@aws-sdk/credential-provider-process': 3.972.28 - '@aws-sdk/credential-provider-sso': 3.972.32 - '@aws-sdk/credential-provider-web-identity': 3.972.32 + '@aws-sdk/credential-provider-env': 3.972.31 + '@aws-sdk/credential-provider-http': 3.972.33 + '@aws-sdk/credential-provider-ini': 3.972.35 + '@aws-sdk/credential-provider-process': 3.972.31 + '@aws-sdk/credential-provider-sso': 3.972.35 + '@aws-sdk/credential-provider-web-identity': 3.972.35 '@aws-sdk/types': 3.973.8 '@smithy/credential-provider-imds': 4.2.14 '@smithy/property-provider': 4.2.14 @@ -6844,20 +6848,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.972.28': + '@aws-sdk/credential-provider-process@3.972.31': dependencies: - '@aws-sdk/core': 3.974.2 + '@aws-sdk/core': 3.974.5 '@aws-sdk/types': 3.973.8 '@smithy/property-provider': 4.2.14 '@smithy/shared-ini-file-loader': 4.4.9 '@smithy/types': 4.14.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.32': + '@aws-sdk/credential-provider-sso@3.972.35': dependencies: - '@aws-sdk/core': 3.974.2 - '@aws-sdk/nested-clients': 3.997.0 - '@aws-sdk/token-providers': 3.1033.0 + '@aws-sdk/core': 3.974.5 + '@aws-sdk/nested-clients': 3.997.3 + '@aws-sdk/token-providers': 3.1036.0 '@aws-sdk/types': 3.973.8 '@smithy/property-provider': 4.2.14 '@smithy/shared-ini-file-loader': 4.4.9 @@ -6866,10 +6870,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.32': + '@aws-sdk/credential-provider-web-identity@3.972.35': dependencies: - '@aws-sdk/core': 3.974.2 - '@aws-sdk/nested-clients': 3.997.0 + '@aws-sdk/core': 3.974.5 + '@aws-sdk/nested-clients': 3.997.3 '@aws-sdk/types': 3.973.8 '@smithy/property-provider': 4.2.14 '@smithy/shared-ini-file-loader': 4.4.9 @@ -6895,12 +6899,12 @@ snapshots: '@smithy/types': 4.14.1 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.974.10': + '@aws-sdk/middleware-flexible-checksums@3.974.13': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.974.2 + '@aws-sdk/core': 3.974.5 '@aws-sdk/crc64-nvme': 3.972.7 '@aws-sdk/types': 3.973.8 '@smithy/is-array-buffer': 4.2.2 @@ -6908,7 +6912,7 @@ snapshots: '@smithy/protocol-http': 5.3.14 '@smithy/types': 4.14.1 '@smithy/util-middleware': 4.2.14 - '@smithy/util-stream': 4.5.24 + '@smithy/util-stream': 4.5.25 '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 @@ -6939,20 +6943,20 @@ snapshots: '@smithy/types': 4.14.1 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.972.31': + '@aws-sdk/middleware-sdk-s3@3.972.34': dependencies: - '@aws-sdk/core': 3.974.2 + '@aws-sdk/core': 3.974.5 '@aws-sdk/types': 3.973.8 '@aws-sdk/util-arn-parser': 3.972.3 - '@smithy/core': 3.23.16 + '@smithy/core': 3.23.17 '@smithy/node-config-provider': 4.3.14 '@smithy/protocol-http': 5.3.14 '@smithy/signature-v4': 5.3.14 - '@smithy/smithy-client': 4.12.12 + '@smithy/smithy-client': 4.12.13 '@smithy/types': 4.14.1 '@smithy/util-config-provider': 4.2.2 '@smithy/util-middleware': 4.2.14 - '@smithy/util-stream': 4.5.24 + '@smithy/util-stream': 4.5.25 '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 @@ -6962,62 +6966,62 @@ snapshots: '@smithy/types': 4.14.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.32': + '@aws-sdk/middleware-user-agent@3.972.35': dependencies: - '@aws-sdk/core': 3.974.2 + '@aws-sdk/core': 3.974.5 '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.7 - '@smithy/core': 3.23.16 + '@aws-sdk/util-endpoints': 3.996.8 + '@smithy/core': 3.23.17 '@smithy/protocol-http': 5.3.14 '@smithy/types': 4.14.1 - '@smithy/util-retry': 4.3.3 + '@smithy/util-retry': 4.3.4 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.997.0': + '@aws-sdk/nested-clients@3.997.3': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.2 + '@aws-sdk/core': 3.974.5 '@aws-sdk/middleware-host-header': 3.972.10 '@aws-sdk/middleware-logger': 3.972.10 '@aws-sdk/middleware-recursion-detection': 3.972.11 - '@aws-sdk/middleware-user-agent': 3.972.32 - '@aws-sdk/region-config-resolver': 3.972.12 - '@aws-sdk/signature-v4-multi-region': 3.996.19 + '@aws-sdk/middleware-user-agent': 3.972.35 + '@aws-sdk/region-config-resolver': 3.972.13 + '@aws-sdk/signature-v4-multi-region': 3.996.22 '@aws-sdk/types': 3.973.8 - '@aws-sdk/util-endpoints': 3.996.7 + '@aws-sdk/util-endpoints': 3.996.8 '@aws-sdk/util-user-agent-browser': 3.972.10 - '@aws-sdk/util-user-agent-node': 3.973.18 + '@aws-sdk/util-user-agent-node': 3.973.21 '@smithy/config-resolver': 4.4.17 - '@smithy/core': 3.23.16 + '@smithy/core': 3.23.17 '@smithy/fetch-http-handler': 5.3.17 '@smithy/hash-node': 4.2.14 '@smithy/invalid-dependency': 4.2.14 '@smithy/middleware-content-length': 4.2.14 - '@smithy/middleware-endpoint': 4.4.31 - '@smithy/middleware-retry': 4.5.4 - '@smithy/middleware-serde': 4.2.19 + '@smithy/middleware-endpoint': 4.4.32 + '@smithy/middleware-retry': 4.5.5 + '@smithy/middleware-serde': 4.2.20 '@smithy/middleware-stack': 4.2.14 '@smithy/node-config-provider': 4.3.14 - '@smithy/node-http-handler': 4.6.0 + '@smithy/node-http-handler': 4.6.1 '@smithy/protocol-http': 5.3.14 - '@smithy/smithy-client': 4.12.12 + '@smithy/smithy-client': 4.12.13 '@smithy/types': 4.14.1 '@smithy/url-parser': 4.2.14 '@smithy/util-base64': 4.3.2 '@smithy/util-body-length-browser': 4.2.2 '@smithy/util-body-length-node': 4.2.3 - '@smithy/util-defaults-mode-browser': 4.3.48 - '@smithy/util-defaults-mode-node': 4.2.53 + '@smithy/util-defaults-mode-browser': 4.3.49 + '@smithy/util-defaults-mode-node': 4.2.54 '@smithy/util-endpoints': 3.4.2 '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.3 + '@smithy/util-retry': 4.3.4 '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.972.12': + '@aws-sdk/region-config-resolver@3.972.13': dependencies: '@aws-sdk/types': 3.973.8 '@smithy/config-resolver': 4.4.17 @@ -7025,19 +7029,19 @@ snapshots: '@smithy/types': 4.14.1 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.996.19': + '@aws-sdk/signature-v4-multi-region@3.996.22': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.972.31 + '@aws-sdk/middleware-sdk-s3': 3.972.34 '@aws-sdk/types': 3.973.8 '@smithy/protocol-http': 5.3.14 '@smithy/signature-v4': 5.3.14 '@smithy/types': 4.14.1 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1033.0': + '@aws-sdk/token-providers@3.1036.0': dependencies: - '@aws-sdk/core': 3.974.2 - '@aws-sdk/nested-clients': 3.997.0 + '@aws-sdk/core': 3.974.5 + '@aws-sdk/nested-clients': 3.997.3 '@aws-sdk/types': 3.973.8 '@smithy/property-provider': 4.2.14 '@smithy/shared-ini-file-loader': 4.4.9 @@ -7055,7 +7059,7 @@ snapshots: dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.996.7': + '@aws-sdk/util-endpoints@3.996.8': dependencies: '@aws-sdk/types': 3.973.8 '@smithy/types': 4.14.1 @@ -7074,19 +7078,19 @@ snapshots: bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.973.18': + '@aws-sdk/util-user-agent-node@3.973.21': dependencies: - '@aws-sdk/middleware-user-agent': 3.972.32 + '@aws-sdk/middleware-user-agent': 3.972.35 '@aws-sdk/types': 3.973.8 '@smithy/node-config-provider': 4.3.14 '@smithy/types': 4.14.1 '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.18': + '@aws-sdk/xml-builder@3.972.19': dependencies: '@smithy/types': 4.14.1 - fast-xml-parser: 5.5.8 + fast-xml-parser: 5.7.1 tslib: 2.8.1 '@aws/lambda-invoke-store@0.2.4': {} @@ -7699,7 +7703,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: - ajv: 6.14.0 + ajv: 6.15.0 debug: 4.4.3(supports-color@8.1.1) espree: 9.6.1 globals: 13.24.0 @@ -7713,7 +7717,7 @@ snapshots: '@eslint/eslintrc@3.3.5': dependencies: - ajv: 6.14.0 + ajv: 6.15.0 debug: 4.4.3(supports-color@8.1.1) espree: 10.4.0 globals: 14.0.0 @@ -8422,6 +8426,8 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true + '@nodable/entities@2.1.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -8627,7 +8633,7 @@ snapshots: '@smithy/util-middleware': 4.2.14 tslib: 2.8.1 - '@smithy/core@3.23.16': + '@smithy/core@3.23.17': dependencies: '@smithy/protocol-http': 5.3.14 '@smithy/types': 4.14.1 @@ -8635,7 +8641,7 @@ snapshots: '@smithy/util-base64': 4.3.2 '@smithy/util-body-length-browser': 4.2.2 '@smithy/util-middleware': 4.2.14 - '@smithy/util-stream': 4.5.24 + '@smithy/util-stream': 4.5.25 '@smithy/util-utf8': 4.2.2 '@smithy/uuid': 1.1.2 tslib: 2.8.1 @@ -8731,10 +8737,10 @@ snapshots: '@smithy/types': 4.14.1 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.31': + '@smithy/middleware-endpoint@4.4.32': dependencies: - '@smithy/core': 3.23.16 - '@smithy/middleware-serde': 4.2.19 + '@smithy/core': 3.23.17 + '@smithy/middleware-serde': 4.2.20 '@smithy/node-config-provider': 4.3.14 '@smithy/shared-ini-file-loader': 4.4.9 '@smithy/types': 4.14.1 @@ -8742,22 +8748,22 @@ snapshots: '@smithy/util-middleware': 4.2.14 tslib: 2.8.1 - '@smithy/middleware-retry@4.5.4': + '@smithy/middleware-retry@4.5.5': dependencies: - '@smithy/core': 3.23.16 + '@smithy/core': 3.23.17 '@smithy/node-config-provider': 4.3.14 '@smithy/protocol-http': 5.3.14 '@smithy/service-error-classification': 4.3.0 - '@smithy/smithy-client': 4.12.12 + '@smithy/smithy-client': 4.12.13 '@smithy/types': 4.14.1 '@smithy/util-middleware': 4.2.14 - '@smithy/util-retry': 4.3.3 + '@smithy/util-retry': 4.3.4 '@smithy/uuid': 1.1.2 tslib: 2.8.1 - '@smithy/middleware-serde@4.2.19': + '@smithy/middleware-serde@4.2.20': dependencies: - '@smithy/core': 3.23.16 + '@smithy/core': 3.23.17 '@smithy/protocol-http': 5.3.14 '@smithy/types': 4.14.1 tslib: 2.8.1 @@ -8774,7 +8780,7 @@ snapshots: '@smithy/types': 4.14.1 tslib: 2.8.1 - '@smithy/node-http-handler@4.6.0': + '@smithy/node-http-handler@4.6.1': dependencies: '@smithy/protocol-http': 5.3.14 '@smithy/querystring-builder': 4.2.14 @@ -8822,14 +8828,14 @@ snapshots: '@smithy/util-utf8': 4.2.2 tslib: 2.8.1 - '@smithy/smithy-client@4.12.12': + '@smithy/smithy-client@4.12.13': dependencies: - '@smithy/core': 3.23.16 - '@smithy/middleware-endpoint': 4.4.31 + '@smithy/core': 3.23.17 + '@smithy/middleware-endpoint': 4.4.32 '@smithy/middleware-stack': 4.2.14 '@smithy/protocol-http': 5.3.14 '@smithy/types': 4.14.1 - '@smithy/util-stream': 4.5.24 + '@smithy/util-stream': 4.5.25 tslib: 2.8.1 '@smithy/types@4.14.1': @@ -8870,20 +8876,20 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.48': + '@smithy/util-defaults-mode-browser@4.3.49': dependencies: '@smithy/property-provider': 4.2.14 - '@smithy/smithy-client': 4.12.12 + '@smithy/smithy-client': 4.12.13 '@smithy/types': 4.14.1 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.53': + '@smithy/util-defaults-mode-node@4.2.54': dependencies: '@smithy/config-resolver': 4.4.17 '@smithy/credential-provider-imds': 4.2.14 '@smithy/node-config-provider': 4.3.14 '@smithy/property-provider': 4.2.14 - '@smithy/smithy-client': 4.12.12 + '@smithy/smithy-client': 4.12.13 '@smithy/types': 4.14.1 tslib: 2.8.1 @@ -8902,16 +8908,16 @@ snapshots: '@smithy/types': 4.14.1 tslib: 2.8.1 - '@smithy/util-retry@4.3.3': + '@smithy/util-retry@4.3.4': dependencies: '@smithy/service-error-classification': 4.3.0 '@smithy/types': 4.14.1 tslib: 2.8.1 - '@smithy/util-stream@4.5.24': + '@smithy/util-stream@4.5.25': dependencies: '@smithy/fetch-http-handler': 5.3.17 - '@smithy/node-http-handler': 4.6.0 + '@smithy/node-http-handler': 4.6.1 '@smithy/types': 4.14.1 '@smithy/util-base64': 4.3.2 '@smithy/util-buffer-from': 4.2.2 @@ -9703,7 +9709,7 @@ snapshots: optionalDependencies: ajv: 8.18.0 - ajv@6.14.0: + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -9947,7 +9953,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.20: {} + baseline-browser-mapping@2.10.21: {} big-json@3.2.0: dependencies: @@ -9995,10 +10001,10 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.20 - caniuse-lite: 1.0.30001788 - electron-to-chromium: 1.5.340 - node-releases: 2.0.37 + baseline-browser-mapping: 2.10.21 + caniuse-lite: 1.0.30001790 + electron-to-chromium: 1.5.344 + node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) bs-logger@0.2.6: @@ -10080,7 +10086,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001788: {} + caniuse-lite@1.0.30001790: {} capital-case@1.0.4: dependencies: @@ -10558,7 +10564,7 @@ snapshots: dependencies: jake: 10.9.4 - electron-to-chromium@1.5.340: {} + electron-to-chromium@1.5.344: {} elegant-spinner@1.0.1: {} @@ -10572,10 +10578,10 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@5.20.1: + enhanced-resolve@5.21.0: dependencies: graceful-fs: 4.2.11 - tapable: 2.3.2 + tapable: 2.3.3 entities@4.5.0: {} @@ -10740,7 +10746,7 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) eslint-config-xo-space: 0.35.0(eslint@8.57.1) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-mocha: 10.5.0(eslint@8.57.1) eslint-plugin-n: 15.7.0(eslint@8.57.1) eslint-plugin-perfectionist: 2.11.0(eslint@8.57.1)(typescript@5.9.3) @@ -10802,7 +10808,7 @@ snapshots: eslint-config-xo: 0.49.0(eslint@8.57.1) eslint-config-xo-space: 0.35.0(eslint@8.57.1) eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-jsdoc: 50.8.0(eslint@8.57.1) eslint-plugin-mocha: 10.5.0(eslint@8.57.1) eslint-plugin-n: 17.24.0(eslint@8.57.1)(typescript@5.9.3) @@ -10854,7 +10860,7 @@ snapshots: tinyglobby: 0.2.16 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -10959,7 +10965,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -11017,7 +11023,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -11084,7 +11090,7 @@ snapshots: eslint-plugin-n@17.24.0(eslint@8.57.1)(typescript@4.9.5): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - enhanced-resolve: 5.20.1 + enhanced-resolve: 5.21.0 eslint: 8.57.1 eslint-plugin-es-x: 7.8.0(eslint@8.57.1) get-tsconfig: 4.14.0 @@ -11099,7 +11105,7 @@ snapshots: eslint-plugin-n@17.24.0(eslint@8.57.1)(typescript@5.9.3): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - enhanced-resolve: 5.20.1 + enhanced-resolve: 5.21.0 eslint: 8.57.1 eslint-plugin-es-x: 7.8.0(eslint@8.57.1) get-tsconfig: 4.14.0 @@ -11234,7 +11240,7 @@ snapshots: '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 '@ungap/structured-clone': 1.3.0 - ajv: 6.14.0 + ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3(supports-color@8.1.1) @@ -11281,7 +11287,7 @@ snapshots: '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - ajv: 6.14.0 + ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3(supports-color@8.1.1) @@ -11416,8 +11422,9 @@ snapshots: dependencies: path-expression-matcher: 1.5.0 - fast-xml-parser@5.5.8: + fast-xml-parser@5.7.1: dependencies: + '@nodable/entities': 2.1.0 fast-xml-builder: 1.1.5 path-expression-matcher: 1.5.0 strnum: 2.2.3 @@ -13005,7 +13012,7 @@ snapshots: dependencies: process-on-spawn: 1.1.0 - node-releases@2.0.37: {} + node-releases@2.0.38: {} normalize-package-data@2.5.0: dependencies: @@ -13392,7 +13399,7 @@ snapshots: pluralize@8.0.0: {} - pnpm@10.33.0: {} + pnpm@10.33.2: {} possible-typed-array-names@1.1.0: {} @@ -14053,7 +14060,7 @@ snapshots: typical: 2.6.1 wordwrapjs: 3.0.0 - tapable@2.3.2: {} + tapable@2.3.3: {} tar@7.5.13: dependencies: