From c0a30c572ca6064711dcaac4e8c33baaa3ca0181 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Mon, 4 May 2026 21:08:48 +0800 Subject: [PATCH 01/22] Add stack sync commands for local-server development MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `altis-cli stack sync database `, `stack sync uploads `, and `stack sync all ` to pull remote Vantage backups into a local altis/local-server environment. Key features: - Choose between an existing backup or triggering a new remote backup - SSE stream for live backup progress with 10s poll fallback to handle fast backups that complete before the stream connects - Search-replace on the SQL stream via @automattic/vip-search-replace before import, configured under extra.altis.cloud.search-replace in composer.json (default key: local-server) - Uploads extracted directly to content/uploads/ then synced into the local-server S3 bucket via composer server s3 import-uploads - Cache flush and optional wp altis post-sync hook after DB import; post-sync is silently skipped if the command is not registered - --dry-run-search-replace, --skip-search-replace, --skip-post-sync, --resume, --keep-archive, --yes, --debug, --json options Also fixes lib/stream.js which was calling got.default.stream() — got.default is undefined in got v14 ESM, so SSE streaming was silently broken. Fixed to got.stream(). Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 240 +++++ lib/commands/stack/sync/database.js | 216 +++++ lib/commands/stack/sync/index.js | 11 + lib/commands/stack/sync/uploads.js | 170 ++++ lib/commands/stack/sync/util.js | 378 ++++++++ lib/commands/stack/util.js | 124 +-- lib/stream.js | 2 +- package-lock.json | 1263 ++++++++------------------- package.json | 1 + 9 files changed, 1443 insertions(+), 962 deletions(-) create mode 100644 lib/commands/stack/sync/all.js create mode 100644 lib/commands/stack/sync/database.js create mode 100644 lib/commands/stack/sync/index.js create mode 100644 lib/commands/stack/sync/uploads.js create mode 100644 lib/commands/stack/sync/util.js diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js new file mode 100644 index 0000000..2cda375 --- /dev/null +++ b/lib/commands/stack/sync/all.js @@ -0,0 +1,240 @@ +import chalk from 'chalk'; +import fs from 'fs'; +import ora from 'ora'; +import path from 'path'; +import { tmpdir } from 'os'; + +import Vantage from '../../../vantage.js'; +import { + addCommonOptions, + addDatabaseOptions, + addUploadsOptions, + confirm, + copyUploads, + downloadArchive, + extractArchive, + findCompletedBackup, + formatAge, + promptBackupChoice, + resolveMappings, + runComposerServer, + runPostSync, + searchReplaceAndImport, + startBackup, + validateLocalProject, + waitForBackup, +} from './util.js'; + +const handler = async function ( argv ) { + const { + app, + config, + path: pathOpt, + uploadsPath, + outputDir, + keepArchive, + yes, + resume, + debug, + tables, + searchReplaceKey, + replace: replacePairs, + skipSearchReplace, + dryRunSearchReplace, + skipPostSync, + } = argv; + + const localPath = pathOpt || process.cwd(); + const v = new Vantage( config ); + + // --dry-run-search-replace: just print mappings, no validation or network needed + if ( dryRunSearchReplace ) { + try { + const mappings = resolveMappings( localPath, searchReplaceKey, replacePairs, skipSearchReplace ); + if ( Object.keys( mappings ).length === 0 ) { + console.log( chalk.yellow( 'No search-replace mappings found.' ) ); + console.log( `Configure them under extra.altis.cloud.search-replace.${ searchReplaceKey } in composer.json, or use --replace.` ); + } else { + console.log( chalk.bold( 'Search-replace mappings that would be applied:' ) ); + for ( const [ from, to ] of Object.entries( mappings ) ) { + console.log( ` ${ chalk.red( from ) } → ${ chalk.green( to ) }` ); + } + } + } catch ( err ) { + console.error( chalk.red( err.message ) ); + process.exit( 1 ); + } + return; + } + + // 1. Validate local project + const spinner = ora( 'Validating local project…' ).start(); + try { + validateLocalProject( localPath ); + spinner.succeed( `Local project: ${ chalk.underline( localPath ) }` ); + } catch ( err ) { + spinner.fail( err.message ); + process.exit( 1 ); + } + + // 2. Resolve search-replace mappings (fail fast before any remote calls) + let mappings; + try { + mappings = resolveMappings( localPath, searchReplaceKey, replacePairs, skipSearchReplace ); + } catch ( err ) { + console.error( chalk.red( err.message ) ); + process.exit( 1 ); + } + + if ( Object.keys( mappings ).length === 0 ) { + console.log( chalk.yellow( + `No search-replace mappings found for key "${ searchReplaceKey }". Skipping search-replace. ` + + `Configure them under extra.altis.cloud.search-replace.${ searchReplaceKey } in composer.json, or use --replace.` + ) ); + } else { + console.log( chalk.dim( `Search-replace: ${ Object.keys( mappings ).length } mapping(s) from composer.json[${ searchReplaceKey }]` ) ); + } + + // 3. Choose backup (before confirm, so the user knows what they're agreeing to) + const startTime = new Date(); + let logId = resume; + let backup = null; + + if ( ! logId ) { + backup = await promptBackupChoice( v, app ); + } + + // 4. Confirm with full context + if ( ! yes ) { + let confirmMsg; + if ( backup ) { + const age = formatAge( new Date( backup.date ) ); + confirmMsg = `Import backup ${ chalk.bold( backup.id ) } (${ chalk.dim( age + ' old' ) }) — replace local DB and merge uploads?`; + } else if ( logId ) { + confirmMsg = `Resume backup ${ chalk.bold( logId ) } and import DB + uploads into local project?`; + } else { + confirmMsg = `Create a new backup of ${ chalk.bold( app ) } and replace local DB and uploads?`; + } + await confirm( confirmMsg ); + } + + const workDir = outputDir || fs.mkdtempSync( path.join( tmpdir(), 'altis-sync-' ) ); + const archivePath = path.join( workDir, `${ app }.tar` ); + const extractDir = path.join( workDir, `${ app }-extracted` ); + + try { + // 5. Create backup if needed + if ( ! backup && ! logId ) { + const backupSpinner = ora( `Creating remote backup for ${ chalk.bold( app ) }…` ).start(); + const opts = { + database: 1, + uploads: 1, + ...( tables ? { tables: tables.split( ',' ) } : {} ), + ...( uploadsPath ? { uploads_path: uploadsPath } : {} ), + }; + try { + logId = await startBackup( v, app, opts ); + backupSpinner.succeed( `Backup started (log: ${ chalk.dim( logId ) })` ); + console.log( chalk.dim( `Resume later with: altis-cli stack sync all ${ app } --resume ${ logId }` ) ); + } catch ( err ) { + backupSpinner.fail( `Failed to start backup: ${ err.message }` ); + process.exit( 1 ); + } + } + + // 6. Wait for backup to complete (if creating new or resuming) + if ( ! backup ) { + console.log( chalk.bold( 'Streaming backup progress…' ) ); + await waitForBackup( v, app, logId, startTime, debug ); + + // 7. Find completed backup + const findSpinner = ora( 'Finding completed backup…' ).start(); + backup = await findCompletedBackup( v, app, startTime ); + if ( ! backup ) { + findSpinner.fail( + `Backup completed but no download URL found. Run:\n altis-cli stack backups ${ app }` + ); + process.exit( 1 ); + } + findSpinner.succeed( `Backup: ${ chalk.dim( backup.id ) }` ); + } + + // 6. Download archive + fs.mkdirSync( workDir, { recursive: true } ); + await downloadArchive( backup.url, archivePath ); + + // 7. Extract + const extractSpinner = ora( 'Extracting archive…' ).start(); + await extractArchive( archivePath, extractDir ); + const sqlGzPath = path.join( extractDir, 'database.sql.gz' ); + const uploadsDir = path.join( extractDir, 'uploads' ); + if ( ! fs.existsSync( sqlGzPath ) ) { + extractSpinner.fail( 'database.sql.gz not found in archive.' ); + process.exit( 1 ); + } + if ( ! fs.existsSync( uploadsDir ) ) { + extractSpinner.fail( 'uploads/ not found in archive.' ); + process.exit( 1 ); + } + extractSpinner.succeed( 'Extracted.' ); + + // 8. Search-replace + import database + console.log( chalk.bold( 'Importing database…' ) ); + await searchReplaceAndImport( sqlGzPath, mappings, localPath ); + + // 9. Cache flush + console.log( chalk.dim( 'Flushing object cache…' ) ); + await runComposerServer( localPath, [ 'cli', '--', 'cache', 'flush' ] ); + + // 10. Post-sync hook + if ( ! skipPostSync ) { + console.log( chalk.dim( 'Running wp altis post-sync…' ) ); + await runPostSync( localPath ); + } + + // 11. Copy uploads + import into S3 + console.log( chalk.bold( 'Syncing uploads…' ) ); + const copySpinner = ora( 'Copying uploads to content/uploads…' ).start(); + copyUploads( extractDir, localPath ); + copySpinner.succeed( 'Uploads copied.' ); + + console.log( chalk.dim( 'Syncing uploads to local S3…' ) ); + try { + await runComposerServer( localPath, [ 's3', 'import-uploads' ] ); + } catch { + await runComposerServer( localPath, [ 'import-uploads' ] ); + } + + console.log( chalk.bold.green( `\n✓ Database and uploads synced from ${ app }` ) ); + + } catch ( err ) { + console.error( chalk.red( `\nSync failed: ${ err.message }` ) ); + if ( fs.existsSync( archivePath ) || fs.existsSync( extractDir ) ) { + console.log( chalk.dim( 'Kept files for debugging:' ) ); + if ( fs.existsSync( archivePath ) ) console.log( ` ${ archivePath }` ); + if ( fs.existsSync( extractDir ) ) console.log( ` ${ extractDir }` ); + } + process.exit( 1 ); + } + + // 12. Cleanup + if ( ! keepArchive ) { + try { + fs.rmSync( archivePath, { force: true } ); + fs.rmSync( extractDir, { recursive: true, force: true } ); + } catch { + // Non-fatal + } + } +}; + +export default { + command: 'all ', + description: 'Sync database and uploads from a remote Vantage app into local-server.', + builder: cmd => { + addCommonOptions( cmd ); + addDatabaseOptions( cmd ); + addUploadsOptions( cmd ); + }, + handler, +}; diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js new file mode 100644 index 0000000..6bce9f7 --- /dev/null +++ b/lib/commands/stack/sync/database.js @@ -0,0 +1,216 @@ +import chalk from 'chalk'; +import fs from 'fs'; +import ora from 'ora'; +import path from 'path'; +import { tmpdir } from 'os'; + +import Vantage from '../../../vantage.js'; +import { + addCommonOptions, + addDatabaseOptions, + confirm, + downloadArchive, + extractArchive, + findCompletedBackup, + formatAge, + promptBackupChoice, + resolveMappings, + runComposerServer, + runPostSync, + searchReplaceAndImport, + startBackup, + validateLocalProject, + waitForBackup, +} from './util.js'; + +const handler = async function ( argv ) { + const { + app, + config, + path: pathOpt, + outputDir, + keepArchive, + yes, + resume, + debug, + tables, + searchReplaceKey, + replace: replacePairs, + skipSearchReplace, + dryRunSearchReplace, + skipPostSync, + } = argv; + + const localPath = pathOpt || process.cwd(); + const v = new Vantage( config ); + + // --dry-run-search-replace: just print mappings, no validation or network needed + if ( dryRunSearchReplace ) { + try { + const mappings = resolveMappings( localPath, searchReplaceKey, replacePairs, skipSearchReplace ); + if ( Object.keys( mappings ).length === 0 ) { + console.log( chalk.yellow( 'No search-replace mappings found.' ) ); + console.log( `Configure them under extra.altis.cloud.search-replace.${ searchReplaceKey } in composer.json, or use --replace.` ); + } else { + console.log( chalk.bold( 'Search-replace mappings that would be applied:' ) ); + for ( const [ from, to ] of Object.entries( mappings ) ) { + console.log( ` ${ chalk.red( from ) } → ${ chalk.green( to ) }` ); + } + } + } catch ( err ) { + console.error( chalk.red( err.message ) ); + process.exit( 1 ); + } + return; + } + + // 1. Validate local project + const spinner = ora( 'Validating local project…' ).start(); + try { + validateLocalProject( localPath ); + spinner.succeed( `Local project: ${ chalk.underline( localPath ) }` ); + } catch ( err ) { + spinner.fail( err.message ); + process.exit( 1 ); + } + + // 2. Resolve search-replace mappings (fail fast before any remote calls) + let mappings; + try { + mappings = resolveMappings( localPath, searchReplaceKey, replacePairs, skipSearchReplace ); + } catch ( err ) { + console.error( chalk.red( err.message ) ); + process.exit( 1 ); + } + + if ( Object.keys( mappings ).length === 0 ) { + console.log( chalk.yellow( + `No search-replace mappings found for key "${ searchReplaceKey }". Skipping search-replace. ` + + `Configure them under extra.altis.cloud.search-replace.${ searchReplaceKey } in composer.json, or use --replace.` + ) ); + } else { + console.log( chalk.dim( `Search-replace: ${ Object.keys( mappings ).length } mapping(s) from composer.json[${ searchReplaceKey }]` ) ); + } + + // 3. Choose backup (before confirm, so the user knows what they're agreeing to) + const startTime = new Date(); + let logId = resume; + let backup = null; + + if ( ! logId ) { + backup = await promptBackupChoice( v, app ); + } + + // 4. Confirm with full context + if ( ! yes ) { + let confirmMsg; + if ( backup ) { + const age = formatAge( new Date( backup.date ) ); + confirmMsg = `Import backup ${ chalk.bold( backup.id ) } (${ chalk.dim( age + ' old' ) }) and replace local DB? This cannot be undone.`; + } else if ( logId ) { + confirmMsg = `Resume backup ${ chalk.bold( logId ) } and import into local DB? This cannot be undone.`; + } else { + confirmMsg = `Create a new backup of ${ chalk.bold( app ) } and replace local DB? This cannot be undone.`; + } + await confirm( confirmMsg ); + } + + const workDir = outputDir || fs.mkdtempSync( path.join( tmpdir(), 'altis-sync-' ) ); + const archivePath = path.join( workDir, `${ app }.tar` ); + const extractDir = path.join( workDir, `${ app }-extracted` ); + + try { + // 5. Create backup if needed + if ( ! backup && ! logId ) { + const backupSpinner = ora( `Creating remote backup for ${ chalk.bold( app ) }…` ).start(); + const opts = { + database: 1, + uploads: 0, + ...( tables ? { tables: tables.split( ',' ) } : {} ), + }; + try { + logId = await startBackup( v, app, opts ); + backupSpinner.succeed( `Backup started (log: ${ chalk.dim( logId ) })` ); + console.log( chalk.dim( `Resume later with: altis-cli stack sync database ${ app } --resume ${ logId }` ) ); + } catch ( err ) { + backupSpinner.fail( `Failed to start backup: ${ err.message }` ); + process.exit( 1 ); + } + } + + // 6. Wait for backup to complete (if creating new or resuming) + if ( ! backup ) { + console.log( chalk.bold( 'Streaming backup progress…' ) ); + await waitForBackup( v, app, logId, startTime, debug ); + + // 7. Find completed backup + const findSpinner = ora( 'Finding completed backup…' ).start(); + backup = await findCompletedBackup( v, app, startTime ); + if ( ! backup ) { + findSpinner.fail( + `Backup completed but no download URL found. Run:\n altis-cli stack backups ${ app }` + ); + process.exit( 1 ); + } + findSpinner.succeed( `Backup: ${ chalk.dim( backup.id ) }` ); + } + + // 8. Download archive + fs.mkdirSync( workDir, { recursive: true } ); + await downloadArchive( backup.url, archivePath ); + + // 9. Extract + const extractSpinner = ora( 'Extracting archive…' ).start(); + await extractArchive( archivePath, extractDir ); + const sqlGzPath = path.join( extractDir, 'database.sql.gz' ); + if ( ! fs.existsSync( sqlGzPath ) ) { + extractSpinner.fail( 'database.sql.gz not found in archive.' ); + process.exit( 1 ); + } + extractSpinner.succeed( 'Extracted.' ); + + // 8. Search-replace + import + await searchReplaceAndImport( sqlGzPath, mappings, localPath ); + + // 9. Cache flush + console.log( chalk.dim( 'Flushing object cache…' ) ); + await runComposerServer( localPath, [ 'cli', '--', 'cache', 'flush' ] ); + + // 10. Post-sync hook + if ( ! skipPostSync ) { + console.log( chalk.dim( 'Running wp altis post-sync…' ) ); + await runPostSync( localPath ); + } + + console.log( chalk.bold.green( `\n✓ Database synced from ${ app }` ) ); + + } catch ( err ) { + console.error( chalk.red( `\nSync failed: ${ err.message }` ) ); + if ( fs.existsSync( archivePath ) || fs.existsSync( extractDir ) ) { + console.log( chalk.dim( `Kept files for debugging:` ) ); + if ( fs.existsSync( archivePath ) ) console.log( ` ${ archivePath }` ); + if ( fs.existsSync( extractDir ) ) console.log( ` ${ extractDir }` ); + } + process.exit( 1 ); + } + + // 11. Cleanup + if ( ! keepArchive ) { + try { + fs.rmSync( archivePath, { force: true } ); + fs.rmSync( extractDir, { recursive: true, force: true } ); + } catch { + // Non-fatal + } + } +}; + +export default { + command: 'database ', + description: 'Sync the database from a remote Vantage app into local-server.', + builder: cmd => { + addCommonOptions( cmd ); + addDatabaseOptions( cmd ); + }, + handler, +}; diff --git a/lib/commands/stack/sync/index.js b/lib/commands/stack/sync/index.js new file mode 100644 index 0000000..865aed8 --- /dev/null +++ b/lib/commands/stack/sync/index.js @@ -0,0 +1,11 @@ +import buildSubcommands from '../../../buildSubcommands.js'; + +export default { + command: 'sync', + description: 'Sync data from a remote Vantage app into local-server.', + builder: async function ( command ) { + command.demandCommand( 1 ); + const subcommands = await buildSubcommands( new URL( '.', import.meta.url ).pathname ); + command.command( subcommands ); + }, +}; diff --git a/lib/commands/stack/sync/uploads.js b/lib/commands/stack/sync/uploads.js new file mode 100644 index 0000000..a56a8e1 --- /dev/null +++ b/lib/commands/stack/sync/uploads.js @@ -0,0 +1,170 @@ +import chalk from 'chalk'; +import fs from 'fs'; +import ora from 'ora'; +import path from 'path'; +import { tmpdir } from 'os'; + +import Vantage from '../../../vantage.js'; +import { + addCommonOptions, + addUploadsOptions, + confirm, + copyUploads, + downloadArchive, + extractArchive, + findCompletedBackup, + formatAge, + promptBackupChoice, + runComposerServer, + startBackup, + validateLocalProject, + waitForBackup, +} from './util.js'; + +const handler = async function ( argv ) { + const { + app, + config, + path: pathOpt, + uploadsPath, + outputDir, + keepArchive, + yes, + resume, + debug, + } = argv; + + const localPath = pathOpt || process.cwd(); + const v = new Vantage( config ); + + // 1. Validate local project + const spinner = ora( 'Validating local project…' ).start(); + try { + validateLocalProject( localPath ); + spinner.succeed( `Local project: ${ chalk.underline( localPath ) }` ); + } catch ( err ) { + spinner.fail( err.message ); + process.exit( 1 ); + } + + // 2. Choose backup (before confirm, so the user knows what they're agreeing to) + const startTime = new Date(); + let logId = resume; + let backup = null; + + if ( ! logId ) { + backup = await promptBackupChoice( v, app ); + } + + // 3. Confirm with full context + if ( ! yes ) { + let confirmMsg; + if ( backup ) { + const age = formatAge( new Date( backup.date ) ); + confirmMsg = `Merge uploads from backup ${ chalk.bold( backup.id ) } (${ chalk.dim( age + ' old' ) }) into ./content/uploads? Existing files may be overwritten.`; + } else if ( logId ) { + confirmMsg = `Resume backup ${ chalk.bold( logId ) } and sync uploads into ./content/uploads? Existing files may be overwritten.`; + } else { + confirmMsg = `Create a new uploads backup of ${ chalk.bold( app ) } and sync into ./content/uploads? Existing files may be overwritten.`; + } + await confirm( confirmMsg ); + } + + const workDir = outputDir || fs.mkdtempSync( path.join( tmpdir(), 'altis-sync-' ) ); + const archivePath = path.join( workDir, `${ app }.tar` ); + const extractDir = path.join( workDir, `${ app }-extracted` ); + + try { + // 4. Create backup if needed + if ( ! backup && ! logId ) { + const backupSpinner = ora( `Creating remote uploads backup for ${ chalk.bold( app ) }…` ).start(); + const opts = { + database: 0, + uploads: 1, + ...( uploadsPath ? { uploads_path: uploadsPath } : {} ), + }; + try { + logId = await startBackup( v, app, opts ); + backupSpinner.succeed( `Backup started (log: ${ chalk.dim( logId ) })` ); + console.log( chalk.dim( `Resume later with: altis-cli stack sync uploads ${ app } --resume ${ logId }` ) ); + } catch ( err ) { + backupSpinner.fail( `Failed to start backup: ${ err.message }` ); + process.exit( 1 ); + } + } + + // 5. Wait for backup to complete (if creating new or resuming) + if ( ! backup ) { + console.log( chalk.bold( 'Streaming backup progress…' ) ); + await waitForBackup( v, app, logId, startTime, debug ); + + // 6. Find completed backup + const findSpinner = ora( 'Finding completed backup…' ).start(); + backup = await findCompletedBackup( v, app, startTime ); + if ( ! backup ) { + findSpinner.fail( + `Backup completed but no download URL found. Run:\n altis-cli stack backups ${ app }` + ); + process.exit( 1 ); + } + findSpinner.succeed( `Backup: ${ chalk.dim( backup.id ) }` ); + } + + // 6. Download archive + fs.mkdirSync( workDir, { recursive: true } ); + await downloadArchive( backup.url, archivePath ); + + // 7. Extract and copy uploads + const extractSpinner = ora( 'Extracting archive…' ).start(); + await extractArchive( archivePath, extractDir ); + const uploadsDir = path.join( extractDir, 'uploads' ); + if ( ! fs.existsSync( uploadsDir ) ) { + extractSpinner.fail( 'uploads/ not found in archive.' ); + process.exit( 1 ); + } + extractSpinner.succeed( 'Extracted.' ); + + const copySpinner = ora( 'Copying uploads to content/uploads…' ).start(); + copyUploads( extractDir, localPath ); + copySpinner.succeed( 'Uploads copied.' ); + + // 8. Sync into local-server S3 + console.log( chalk.dim( 'Syncing uploads to local S3…' ) ); + try { + await runComposerServer( localPath, [ 's3', 'import-uploads' ] ); + } catch { + await runComposerServer( localPath, [ 'import-uploads' ] ); + } + + console.log( chalk.bold.green( `\n✓ Uploads synced from ${ app }` ) ); + + } catch ( err ) { + console.error( chalk.red( `\nSync failed: ${ err.message }` ) ); + if ( fs.existsSync( archivePath ) || fs.existsSync( extractDir ) ) { + console.log( chalk.dim( 'Kept files for debugging:' ) ); + if ( fs.existsSync( archivePath ) ) console.log( ` ${ archivePath }` ); + if ( fs.existsSync( extractDir ) ) console.log( ` ${ extractDir }` ); + } + process.exit( 1 ); + } + + // 9. Cleanup + if ( ! keepArchive ) { + try { + fs.rmSync( archivePath, { force: true } ); + fs.rmSync( extractDir, { recursive: true, force: true } ); + } catch { + // Non-fatal + } + } +}; + +export default { + command: 'uploads ', + description: 'Sync uploads from a remote Vantage app into local-server.', + builder: cmd => { + addCommonOptions( cmd ); + addUploadsOptions( cmd ); + }, + handler, +}; diff --git a/lib/commands/stack/sync/util.js b/lib/commands/stack/sync/util.js new file mode 100644 index 0000000..80c1a51 --- /dev/null +++ b/lib/commands/stack/sync/util.js @@ -0,0 +1,378 @@ +import bytes from 'bytes'; +import chalk from 'chalk'; +import { execSync, spawn } from 'child_process'; +import fs from 'fs'; +import inquirer from 'inquirer'; +import fetch from 'node-fetch'; +import ora from 'ora'; +import path from 'path'; +import progressStream from 'progress-stream'; +import { pipeline } from 'stream/promises'; +import { format, parse } from 'url'; +import { createGunzip } from 'zlib'; +import { createRequire } from 'module'; +import { streamLog } from '../util.js'; + +const require = createRequire( import.meta.url ); + +export const CONTAINER_ROOT = '/usr/src/app'; +export const SYNC_DIR = '.altis-sync'; + +// --- Local project validation --- + +export function validateLocalProject( localPath ) { + const composerJsonPath = path.join( localPath, 'composer.json' ); + if ( ! fs.existsSync( composerJsonPath ) ) { + throw new Error( `No composer.json found at ${ localPath }` ); + } + + const composer = JSON.parse( fs.readFileSync( composerJsonPath, 'utf8' ) ); + const deps = { ...( composer.require || {} ), ...( composer[ 'require-dev' ] || {} ) }; + if ( ! deps[ 'altis/local-server' ] ) { + throw new Error( 'altis/local-server is not a dependency in composer.json' ); + } + + if ( ! fs.existsSync( path.join( localPath, 'content' ) ) ) { + throw new Error( `content/ directory not found at ${ localPath }` ); + } + + try { + execSync( 'composer --version', { stdio: 'pipe' } ); + } catch { + throw new Error( 'composer not found on PATH. Install it from https://getcomposer.org' ); + } + + try { + execSync( 'composer server status', { cwd: localPath, stdio: 'pipe' } ); + } catch { + throw new Error( 'local-server is not running. Start it with: composer server start' ); + } +} + +// --- Destructive action confirmation --- + +export async function confirm( message ) { + const { ok } = await inquirer.prompt( { + type: 'confirm', + name: 'ok', + message, + default: false, + } ); + if ( ! ok ) { + console.log( chalk.yellow( 'Cancelled.' ) ); + process.exit( 0 ); + } +} + +// --- Remote backup creation --- + +export async function startBackup( v, stack, opts ) { + const urlBits = parse( `stack/applications/${ stack }/backups`, true ); + urlBits.query = { ...opts, stream: 'true' }; + const url = format( urlBits ); + + const resp = await v.fetch( url, { method: 'POST' } ); + const text = await resp.text(); + if ( ! resp.ok ) { + throw new Error( `Backup request failed: ${ text }` ); + } + return text.trim(); +} + +// --- Find completed backup --- + +export async function findCompletedBackup( v, stack, startTime ) { + const resp = await v.fetch( `stack/applications/${ stack }/backups` ); + if ( ! resp.ok ) { + throw new Error( `Failed to list backups: ${ resp.status }` ); + } + const backups = await resp.json(); + const candidates = backups + .filter( b => new Date( b.date ) >= startTime ) + .sort( ( a, b ) => new Date( b.date ) - new Date( a.date ) ); + + return candidates[ 0 ] || null; +} + +// --- Get latest existing backup --- + +export async function getLatestBackup( v, stack ) { + const resp = await v.fetch( `stack/applications/${ stack }/backups` ); + if ( ! resp.ok ) return null; + const backups = await resp.json(); + if ( ! backups.length ) return null; + return backups.sort( ( a, b ) => new Date( b.date ) - new Date( a.date ) )[ 0 ]; +} + +// --- Prompt: use latest backup or create new --- + +export async function promptBackupChoice( v, stack ) { + const latest = await getLatestBackup( v, stack ); + if ( ! latest ) return null; + + const age = formatAge( new Date( latest.date ) ); + const { choice } = await inquirer.prompt( { + type: 'list', + name: 'choice', + message: `Use an existing backup or create a new one for ${ chalk.bold( stack ) }?`, + choices: [ + { name: `Use latest backup ${ chalk.dim( `${ latest.id } · ${ age } old` ) }`, value: 'latest' }, + { name: 'Create a new backup', value: 'new' }, + ], + } ); + + return choice === 'latest' ? latest : null; +} + +export function formatAge( date ) { + const diffMs = Date.now() - date.getTime(); + const diffMins = Math.floor( diffMs / 60000 ); + if ( diffMins < 60 ) return `${ diffMins }m`; + const diffHours = Math.floor( diffMins / 60 ); + if ( diffHours < 24 ) return `${ diffHours }h`; + return `${ Math.floor( diffHours / 24 ) }d`; +} + +// --- Stream backup progress with poll fallback --- +// The SSE stream can hang if the backup completes before we connect. +// Poll every 10s as a fallback so we don't block forever. + +export function waitForBackup( v, stack, logId, startTime, debug ) { + return new Promise( ( resolve, reject ) => { + let done = false; + + const finish = ( err ) => { + if ( done ) return; + done = true; + clearInterval( pollTimer ); + err ? reject( err ) : resolve(); + }; + + // Stream for live progress + streamLog( v, stack, logId, debug ).then( () => finish() ).catch( finish ); + + // Poll every 10s in case stream misses the complete event + const pollTimer = setInterval( async () => { + if ( done ) return; + try { + const backup = await findCompletedBackup( v, stack, startTime ); + if ( backup ) finish(); + } catch { + // Ignore poll errors — stream is still the primary signal + } + }, 10000 ); + } ); +} + +// --- Download archive --- + +export async function downloadArchive( url, dest ) { + const spinner = ora( 'Downloading backup…' ).start(); + const resp = await fetch( url ); + if ( ! resp.ok ) { + spinner.fail( 'Download failed.' ); + throw new Error( `Download failed: ${ resp.status } ${ resp.statusText }` ); + } + + const size = parseInt( resp.headers.get( 'content-length' ) || '0', 10 ); + const progress = new progressStream( { length: size, time: 200 } ); + progress.on( 'progress', p => { + spinner.text = `Downloading… ${ p.percentage.toFixed( 1 ) }% (${ bytes( p.speed ) }/s)`; + } ); + + await pipeline( resp.body, progress, fs.createWriteStream( dest ) ); + spinner.succeed( `Downloaded to ${ chalk.underline( dest ) }` ); +} + +// --- Extract archive --- + +export async function extractArchive( archivePath, extractDir ) { + fs.mkdirSync( extractDir, { recursive: true } ); + await runProcess( 'tar', [ '-xf', archivePath, '-C', extractDir ] ); +} + +// --- Search-replace mappings --- + +export function resolveMappings( localPath, key, explicitPairs, skip ) { + if ( skip ) { + return {}; + } + + const composer = JSON.parse( fs.readFileSync( path.join( localPath, 'composer.json' ), 'utf8' ) ); + const configMappings = composer?.extra?.altis?.cloud?.[ 'search-replace' ]?.[ key ] || {}; + const mappings = { ...configMappings }; + + for ( const pair of ( explicitPairs || [] ) ) { + const eqIdx = pair.indexOf( '=' ); + if ( eqIdx < 1 ) { + throw new Error( `Invalid --replace value "${ pair }". Expected format: from=to` ); + } + mappings[ pair.slice( 0, eqIdx ) ] = pair.slice( eqIdx + 1 ); + } + + for ( const [ from, to ] of Object.entries( mappings ) ) { + if ( ! from || ! to ) { + throw new Error( `Invalid search-replace mapping: "${ from }" => "${ to }"` ); + } + } + + return mappings; +} + +// --- Search-replace SQL and import into local-server --- + +export async function searchReplaceAndImport( sqlGzPath, mappings, localPath ) { + const { replace } = require( '@automattic/vip-search-replace' ); + + const syncDir = path.join( localPath, SYNC_DIR ); + const sqlDest = path.join( syncDir, 'database.sql' ); + fs.mkdirSync( syncDir, { recursive: true } ); + + const spinner = ora( 'Running search-replace on SQL…' ).start(); + const readStream = fs.createReadStream( sqlGzPath ).pipe( createGunzip() ); + const replacements = Object.entries( mappings ).flat(); + + const sqlStream = replacements.length > 0 + ? await replace( readStream, replacements ) + : readStream; + + await pipeline( sqlStream, fs.createWriteStream( sqlDest ) ); + spinner.succeed( `Search-replace complete (${ Object.keys( mappings ).length } mapping(s))` ); + + const containerPath = `${ CONTAINER_ROOT }/${ SYNC_DIR }/database.sql`; + console.log( chalk.dim( 'Importing database into local-server (this may take a while)…' ) ); + await runComposerServer( localPath, [ 'cli', '--', 'db', 'import', containerPath ] ); + console.log( chalk.dim( 'Database import complete.' ) ); + + fs.unlinkSync( sqlDest ); + try { + fs.rmdirSync( syncDir ); + } catch { + // Not empty (e.g. other files) — that's fine + } +} + +// --- Copy uploads into content/uploads --- + +export function copyUploads( extractDir, localPath ) { + const src = path.join( extractDir, 'uploads' ); + const dest = path.join( localPath, 'content', 'uploads' ); + fs.mkdirSync( dest, { recursive: true } ); + fs.cpSync( src, dest, { recursive: true } ); +} + +// --- composer server helpers --- + +export function runComposerServer( localPath, args ) { + return runProcess( 'composer', [ 'server', ...args ], { cwd: localPath } ); +} + +export async function runPostSync( localPath ) { + const registered = await new Promise( resolve => { + const proc = spawn( 'composer', [ 'server', 'cli', '--', 'altis', 'post-sync', '--help' ], { + cwd: localPath, + stdio: 'pipe', + } ); + proc.on( 'close', code => resolve( code === 0 ) ); + proc.on( 'error', () => resolve( false ) ); + } ); + + if ( ! registered ) return; + + try { + await runComposerServer( localPath, [ 'cli', '--', 'altis', 'post-sync' ] ); + } catch ( err ) { + console.warn( chalk.yellow( `Warning: wp altis post-sync failed: ${ err.message }` ) ); + } +} + +// --- Common yargs option builders --- + +export function addCommonOptions( cmd ) { + cmd.option( 'path', { + description: 'Local Altis project path. Defaults to current working directory.', + type: 'string', + } ); + cmd.option( 'output-dir', { + description: 'Working directory for downloaded archives. Defaults to a temp directory.', + type: 'string', + } ); + cmd.option( 'keep-archive', { + description: 'Keep downloaded archive and extracted files after restore.', + type: 'boolean', + default: false, + } ); + cmd.option( 'yes', { + description: 'Skip destructive action confirmations.', + type: 'boolean', + default: false, + } ); + cmd.option( 'resume', { + description: 'Resume watching an already-started remote backup task.', + type: 'string', + } ); + cmd.option( 'debug', { + description: 'Enable debug output for stream logging.', + type: 'boolean', + default: false, + } ); + cmd.option( 'json', { + description: 'Print machine-readable JSON summary.', + type: 'boolean', + default: false, + } ); +} + +export function addDatabaseOptions( cmd ) { + cmd.option( 'tables', { + description: 'Comma-separated list of tables to include in the backup.', + type: 'string', + } ); + cmd.option( 'search-replace-key', { + description: 'composer.json key under extra.altis.cloud.search-replace. Defaults to local-server.', + type: 'string', + default: 'local-server', + } ); + cmd.option( 'replace', { + description: 'Explicit search-replace mapping (from=to). Repeatable.', + type: 'array', + } ); + cmd.option( 'skip-search-replace', { + description: 'Skip the search-replace step.', + type: 'boolean', + default: false, + } ); + cmd.option( 'dry-run-search-replace', { + description: 'Print resolved mappings without triggering a backup or import.', + type: 'boolean', + default: false, + } ); + cmd.option( 'skip-post-sync', { + description: 'Skip the wp altis post-sync hook.', + type: 'boolean', + default: false, + } ); +} + +export function addUploadsOptions( cmd ) { + cmd.option( 'uploads-path', { + description: 'Uploads prefix to export from the remote app.', + type: 'string', + } ); +} + +// --- Internal helpers --- + +function runProcess( cmd, args, opts = {} ) { + return new Promise( ( resolve, reject ) => { + const proc = spawn( cmd, args, { stdio: 'inherit', ...opts } ); + proc.on( 'close', code => { + if ( code === 0 ) { + resolve(); + } else { + reject( new Error( `${ cmd } ${ args.join( ' ' ) } exited with code ${ code }` ) ); + } + } ); + proc.on( 'error', reject ); + } ); +} diff --git a/lib/commands/stack/util.js b/lib/commands/stack/util.js index 07d6940..d73acda 100644 --- a/lib/commands/stack/util.js +++ b/lib/commands/stack/util.js @@ -164,72 +164,76 @@ export function streamLog(vantage, stack, log, debug) { log: [], }; - vantage.getLogStream( { id: stack, log } ).then( stream => { - // Render status at 30fps. - let renderLoop = setInterval( () => renderStatus( status ), 1000 / 30 ); - - stream.on( 'open', () => status.log.push( chalk.bold.yellow( 'Connected!' ) ) ); - stream.on( 'close', () => { - // Final render. - clearInterval( renderLoop ); - renderStatus( status ); - }); - - if ( debug ) { - stream.on( 'retry', () => status.log.push( chalk.yellow( 'Reconnecting to stream...' ) ) ); - } + return new Promise( ( resolve, reject ) => { + vantage.getLogStream( { id: stack, log } ).then( stream => { + // Render status at 30fps. + let renderLoop = setInterval( () => renderStatus( status ), 1000 / 30 ); + + stream.on( 'open', () => status.log.push( chalk.bold.yellow( 'Connected!' ) ) ); + stream.on( 'close', () => { + // Final render. + clearInterval( renderLoop ); + renderStatus( status ); + }); - stream.on( 'data', ({ type, data }) => { if ( debug ) { - console.log( `${chalk.red(type)} ${data}` ); + stream.on( 'retry', () => status.log.push( chalk.yellow( 'Reconnecting to stream...' ) ) ); } - switch ( type ) { - case 'fail': { - const parsed = JSON.parse( data ); - const messageLines = parsed.message.split( '\n' ); - Array.prototype.push.apply( status.log, messageLines.map( line => chalk.red( line ) ) ); - - stream.destroy(); - status.spinner.fail( chalk.bold.red( 'Failed.' ) ); - break; - } - case 'percentComplete': - status.progress = parseInt( data, 10 ); - break; - - case 'log': - const parsed = JSON.parse( data ); - const delimPos = parsed.message.indexOf( '::' ); - const isErrorOutput = delimPos > 0 && parsed.message.substring( 0, delimPos ) === 'err'; - - // Strip prefixes: - const output = delimPos > 0 ? parsed.message.substring( delimPos + 2 ) : parsed.message; - const messageLines = output.trim().split( '\n' ); - switch ( parsed.level ) { - case 'info': - status.step = messageLines.slice( -1 ); - Array.prototype.push.apply( status.log, messageLines.map( t => chalk.yellow( t ) ) ); - break; - - case 'debug': - let lines = messageLines.map( line => INDENT + line ); - if ( isErrorOutput ) { - lines = lines.map( line => chalk.red( line ) ); - } - Array.prototype.push.apply( status.log, lines ); - break; + stream.on( 'data', ({ type, data }) => { + if ( debug ) { + console.log( `${chalk.red(type)} ${data}` ); + } + switch ( type ) { + case 'fail': { + const parsed = JSON.parse( data ); + const messageLines = parsed.message.split( '\n' ); + Array.prototype.push.apply( status.log, messageLines.map( line => chalk.red( line ) ) ); + + stream.destroy(); + status.spinner.fail( chalk.bold.red( 'Failed.' ) ); + reject( new Error( parsed.message ) ); + break; } - break; - case 'complete': - status.progress = 100; - stream.destroy(); - status.spinner.succeed( chalk.bold.green( 'Complete!' ) ); - break; - } - }); - }); + case 'percentComplete': + status.progress = parseInt( data, 10 ); + break; + + case 'log': + const parsed = JSON.parse( data ); + const delimPos = parsed.message.indexOf( '::' ); + const isErrorOutput = delimPos > 0 && parsed.message.substring( 0, delimPos ) === 'err'; + + // Strip prefixes: + const output = delimPos > 0 ? parsed.message.substring( delimPos + 2 ) : parsed.message; + const messageLines = output.trim().split( '\n' ); + switch ( parsed.level ) { + case 'info': + status.step = messageLines.slice( -1 ); + Array.prototype.push.apply( status.log, messageLines.map( t => chalk.yellow( t ) ) ); + break; + + case 'debug': + let lines = messageLines.map( line => INDENT + line ); + if ( isErrorOutput ) { + lines = lines.map( line => chalk.red( line ) ); + } + Array.prototype.push.apply( status.log, lines ); + break; + } + break; + + case 'complete': + status.progress = 100; + stream.destroy(); + status.spinner.succeed( chalk.bold.green( 'Complete!' ) ); + resolve(); + break; + } + }); + }).catch( reject ); + } ); } export function renderRepo(repo) { diff --git a/lib/stream.js b/lib/stream.js index bfd533b..878e5d9 100644 --- a/lib/stream.js +++ b/lib/stream.js @@ -45,7 +45,7 @@ export default function(url, opts) { buf = ''; - stream = got.default.stream(url, reqOpts); + stream = got.stream(url, reqOpts); onclose = once(() => { if (destroyed) return; diff --git a/package-lock.json b/package-lock.json index 432dab9..43d5112 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "altis-cli", "version": "1.1.0", "dependencies": { + "@automattic/vip-search-replace": "^2.0.0", "@humanmade/ssm": "^0.0.1", "ansi-escapes": "^2.0.0", "application-config": "^1.0.1", @@ -40,6 +41,24 @@ "altis-cli": "bin/altis-cli.js" } }, + "node_modules/@automattic/vip-search-replace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@automattic/vip-search-replace/-/vip-search-replace-2.0.0.tgz", + "integrity": "sha512-i1d3/KCK0sVgNjsJAV4UlMnC51AypLDfX8iEWTDLBiSR3wEvSWMSftF3f7yK46VdbBXB5zDvnVM48kFvASW9Ig==", + "cpu": [ + "ia32", + "x64", + "arm64" + ], + "os": [ + "darwin", + "linux", + "win32" + ], + "dependencies": { + "debug": "^4.2.0" + } + }, "node_modules/@humanmade/ssm": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/@humanmade/ssm/-/ssm-0.0.1.tgz", @@ -55,7 +74,6 @@ "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -72,17 +90,24 @@ } } }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "engines": { + "node": ">=18" + } + }, "node_modules/@inquirer/checkbox": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.0.tgz", - "integrity": "sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA==", - "license": "MIT", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -96,29 +121,13 @@ } } }, - "node_modules/@inquirer/checkbox/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@inquirer/confirm": { - "version": "5.1.14", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.14.tgz", - "integrity": "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==", - "license": "MIT", + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/type": "^3.0.8" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -133,19 +142,18 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.15", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.15.tgz", - "integrity": "sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==", - "license": "MIT", - "dependencies": { - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -159,26 +167,10 @@ } } }, - "node_modules/@inquirer/core/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@inquirer/core/node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", "engines": { "node": ">=14" }, @@ -187,14 +179,13 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.15", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.15.tgz", - "integrity": "sha512-wst31XT8DnGOSS4nNJDIklGKnf+8shuauVrWzgKegWUe28zfCftcWZ2vktGdzJgcylWSS2SrDnYUb6alZcwnCQ==", - "license": "MIT", + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/type": "^3.0.8", - "external-editor": "^3.1.0" + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -209,14 +200,33 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.17.tgz", - "integrity": "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==", - "license": "MIT", + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/type": "^3.0.8", - "yoctocolors-cjs": "^2.1.2" + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" }, "engines": { "node": ">=18" @@ -231,22 +241,20 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", - "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", - "license": "MIT", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", "engines": { "node": ">=18" } }, "node_modules/@inquirer/input": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.1.tgz", - "integrity": "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==", - "license": "MIT", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/type": "^3.0.8" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -261,13 +269,12 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.17.tgz", - "integrity": "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==", - "license": "MIT", + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/type": "^3.0.8" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -282,14 +289,13 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.17.tgz", - "integrity": "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==", - "license": "MIT", + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2" + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -303,37 +309,21 @@ } } }, - "node_modules/@inquirer/password/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@inquirer/prompts": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.7.1.tgz", - "integrity": "sha512-XDxPrEWeWUBy8scAXzXuFY45r/q49R0g72bUzgQXZ1DY/xEFX+ESDMkTQolcb5jRBzaNJX2W8XQl6krMNDTjaA==", - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^4.2.0", - "@inquirer/confirm": "^5.1.14", - "@inquirer/editor": "^4.2.15", - "@inquirer/expand": "^4.0.17", - "@inquirer/input": "^4.2.1", - "@inquirer/number": "^3.0.17", - "@inquirer/password": "^4.0.17", - "@inquirer/rawlist": "^4.1.5", - "@inquirer/search": "^3.0.17", - "@inquirer/select": "^4.3.1" + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" }, "engines": { "node": ">=18" @@ -348,14 +338,13 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.5.tgz", - "integrity": "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==", - "license": "MIT", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/type": "^3.0.8", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -370,15 +359,14 @@ } }, "node_modules/@inquirer/search": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.17.tgz", - "integrity": "sha512-CuBU4BAGFqRYors4TNCYzy9X3DpKtgIW4Boi0WNkm4Ei1hvY9acxKdBdyqzqBCEe4YxSdaQQsasJlFlUJNgojw==", - "license": "MIT", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -393,16 +381,15 @@ } }, "node_modules/@inquirer/select": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.1.tgz", - "integrity": "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==", - "license": "MIT", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -416,26 +403,10 @@ } } }, - "node_modules/@inquirer/select/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@inquirer/type": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", - "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", - "license": "MIT", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", "engines": { "node": ">=18" }, @@ -448,89 +419,10 @@ } } }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", - "license": "MIT", "engines": { "node": ">=12.22.0" } @@ -539,7 +431,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", - "license": "MIT", "dependencies": { "graceful-fs": "4.2.10" }, @@ -550,14 +441,12 @@ "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "license": "ISC" + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, "node_modules/@pnpm/npm-conf": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", - "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", - "license": "MIT", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-3.0.2.tgz", + "integrity": "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==", "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", @@ -570,14 +459,12 @@ "node_modules/@sec-ant/readable-stream": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "license": "MIT" + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==" }, "node_modules/@sindresorhus/is": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.0.2.tgz", "integrity": "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -589,7 +476,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "license": "MIT", "dependencies": { "defer-to-connect": "^2.0.1" }, @@ -601,7 +487,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "license": "MIT", "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", @@ -612,32 +497,28 @@ "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "license": "MIT" + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/node": { - "version": "24.0.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.12.tgz", - "integrity": "sha512-LtOrbvDf5ndC9Xi+4QZjVL0woFymF/xSTKZKPgrrl7H7XoeDvnD+E2IclKVDyaK9UM756W/3BXqSU+JEHopA9g==", - "license": "MIT", + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "dependencies": { - "undici-types": "~7.8.0" + "undici-types": "~7.19.0" } }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -646,7 +527,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", "dependencies": { "string-width": "^4.1.0" } @@ -655,7 +535,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", "engines": { "node": ">=8" } @@ -663,14 +542,12 @@ "node_modules/ansi-align/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", "engines": { "node": ">=8" } @@ -679,7 +556,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -693,7 +569,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -710,10 +585,9 @@ } }, "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "engines": { "node": ">=12" }, @@ -722,10 +596,9 @@ } }, "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "engines": { "node": ">=12" }, @@ -756,12 +629,20 @@ } }, "node_modules/atomically": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.0.3.tgz", - "integrity": "sha512-kU6FmrwZ3Lx7/7y3hPS5QnbJfaohcIul5fGqf7ok+4KklIEk9tJ0C2IQPdacSbVUWv6zVHXEBWoWd6NrVMT7Cw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-2.1.1.tgz", + "integrity": "sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==", "dependencies": { - "stubborn-fs": "^1.2.5", - "when-exit": "^2.1.1" + "stubborn-fs": "^2.0.0", + "when-exit": "^2.1.4" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/base64-stream": { @@ -773,7 +654,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", - "license": "MIT", "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", @@ -791,23 +671,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/boxen/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/boxen/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "license": "MIT", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", @@ -820,11 +687,21 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "license": "MIT", "dependencies": { "run-applescript": "^7.0.0" }, @@ -839,7 +716,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -848,7 +724,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "license": "MIT", "engines": { "node": ">=14.16" } @@ -857,7 +732,6 @@ "version": "12.0.1", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-12.0.1.tgz", "integrity": "sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==", - "license": "MIT", "dependencies": { "@types/http-cache-semantics": "^4.0.4", "get-stream": "^9.0.1", @@ -875,7 +749,6 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", - "license": "MIT", "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" @@ -891,7 +764,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -903,7 +775,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "license": "MIT", "engines": { "node": ">=16" }, @@ -912,10 +783,9 @@ } }, "node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "license": "MIT", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -924,16 +794,14 @@ } }, "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "license": "MIT" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==" }, "node_modules/cli-boxes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", "engines": { "node": ">=10" }, @@ -945,7 +813,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "license": "MIT", "dependencies": { "restore-cursor": "^5.0.0" }, @@ -960,7 +827,6 @@ "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", "engines": { "node": ">=6" }, @@ -972,7 +838,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "license": "ISC", "engines": { "node": ">= 12" } @@ -981,7 +846,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz", "integrity": "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==", - "license": "MIT", "dependencies": { "execa": "^8.0.1", "is-wsl": "^3.1.0", @@ -998,7 +862,6 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "license": "ISC", "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", @@ -1009,10 +872,9 @@ } }, "node_modules/cliui/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "license": "MIT", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", @@ -1037,7 +899,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" }, @@ -1049,7 +910,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "license": "MIT", "engines": { "node": ">=4" } @@ -1058,7 +918,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -1069,14 +928,12 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/columnify": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.6.0.tgz", "integrity": "sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==", - "license": "MIT", "dependencies": { "strip-ansi": "^6.0.1", "wcwidth": "^1.0.0" @@ -1108,7 +965,6 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "license": "MIT", "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -1117,14 +973,12 @@ "node_modules/config-chain/node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/configstore": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-7.0.0.tgz", - "integrity": "sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ==", - "license": "BSD-2-Clause", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-7.1.0.tgz", + "integrity": "sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==", "dependencies": { "atomically": "^2.0.3", "dot-prop": "^9.0.0", @@ -1135,7 +989,7 @@ "node": ">=18" }, "funding": { - "url": "https://github.com/yeoman/configstore?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/core-util-is": { @@ -1147,7 +1001,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1172,16 +1025,30 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "license": "MIT", "engines": { "node": ">= 12" } }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" }, @@ -1196,7 +1063,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", "engines": { "node": ">=10" }, @@ -1208,16 +1074,14 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/default-browser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", - "license": "MIT", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" @@ -1230,10 +1094,9 @@ } }, "node_modules/default-browser-id": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", - "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", - "license": "MIT", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", "engines": { "node": ">=18" }, @@ -1256,7 +1119,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "license": "MIT", "engines": { "node": ">=10" } @@ -1265,7 +1127,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -1277,7 +1138,6 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", - "license": "MIT", "dependencies": { "type-fest": "^4.18.2" }, @@ -1288,35 +1148,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dot-prop/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, "node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "license": "MIT" + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==" }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", "dependencies": { "once": "^1.4.0" } @@ -1325,7 +1165,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -1334,9 +1173,9 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -1345,7 +1184,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -1357,7 +1195,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", @@ -1380,7 +1217,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", "engines": { "node": ">=14" }, @@ -1388,20 +1224,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", @@ -1416,7 +1238,6 @@ "url": "https://paypal.me/jimmywarting" } ], - "license": "MIT", "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -1425,39 +1246,10 @@ "node": "^12.20 || >= 14.13" } }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data-encoder": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.1.0.tgz", "integrity": "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==", - "license": "MIT", "engines": { "node": ">= 18" } @@ -1466,7 +1258,6 @@ "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" }, @@ -1483,10 +1274,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", - "license": "MIT", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", "engines": { "node": ">=18" }, @@ -1498,7 +1288,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "license": "MIT", "engines": { "node": ">=16" }, @@ -1507,23 +1296,16 @@ } }, "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1533,7 +1315,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", - "license": "MIT", "dependencies": { "ini": "4.1.1" }, @@ -1546,9 +1327,7 @@ }, "node_modules/got": { "version": "14.4.7", - "resolved": "https://registry.npmjs.org/got/-/got-14.4.7.tgz", "integrity": "sha512-DI8zV1231tqiGzOiOzQWDhsBmncFW7oQDH6Zgy6pDPrqJuVZMtoSgPLLsBZQj8Jg4JFfwoOsDA8NGtLQLnIx2g==", - "license": "MIT", "dependencies": { "@sindresorhus/is": "^7.0.1", "@szmarczak/http-timer": "^5.0.1", @@ -1569,35 +1348,20 @@ "url": "https://github.com/sindresorhus/got?sponsor=1" } }, - "node_modules/got/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==" }, "node_modules/http2-wrapper": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.2.0" @@ -1610,21 +1374,23 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "license": "Apache-2.0", "engines": { "node": ">=16.17.0" } }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/indent-string": { @@ -1644,23 +1410,21 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", - "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/inquirer": { - "version": "12.8.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.8.2.tgz", - "integrity": "sha512-oBDL9f4+cDambZVJdfJu2M5JQfvaug9lbo6fKDlFV40i8t3FGA1Db67ov5Hp5DInG4zmXhHWTSnlXBntnJ7GMA==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.1.15", - "@inquirer/prompts": "^7.7.1", - "@inquirer/type": "^3.0.8", - "ansi-escapes": "^4.3.2", + "version": "12.11.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.11.1.tgz", + "integrity": "sha512-9VF7mrY+3OmsAfjH3yKz/pLbJ5z22E23hENKw3/LNSaA/sAt3v49bDRY+Ygct1xwuKT+U+cBfTzjCPySna69Qw==", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/prompts": "^7.10.1", + "@inquirer/type": "^3.0.10", "mute-stream": "^2.0.0", - "run-async": "^4.0.5", + "run-async": "^4.0.6", "rxjs": "^7.8.2" }, "engines": { @@ -1675,25 +1439,10 @@ } } }, - "node_modules/inquirer/node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-docker": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "license": "MIT", "bin": { "is-docker": "cli.js" }, @@ -1705,12 +1454,11 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", - "license": "MIT", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "dependencies": { - "get-east-asian-width": "^1.0.0" + "get-east-asian-width": "^1.3.1" }, "engines": { "node": ">=18" @@ -1723,7 +1471,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-1.0.0.tgz", "integrity": "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==", - "license": "MIT", "bin": { "is-in-ci": "cli.js" }, @@ -1738,7 +1485,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "license": "MIT", "dependencies": { "is-docker": "^3.0.0" }, @@ -1756,7 +1502,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz", "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==", - "license": "MIT", "dependencies": { "global-directory": "^4.0.1", "is-path-inside": "^4.0.0" @@ -1772,7 +1517,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -1781,10 +1525,9 @@ } }, "node_modules/is-npm": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", - "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", - "license": "MIT", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.1.0.tgz", + "integrity": "sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -1796,7 +1539,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -1808,7 +1550,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -1820,7 +1561,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -1829,10 +1569,9 @@ } }, "node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "license": "MIT", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", "dependencies": { "is-inside-container": "^1.0.0" }, @@ -1847,7 +1586,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is64bit/-/is64bit-2.0.0.tgz", "integrity": "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==", - "license": "MIT", "dependencies": { "system-architecture": "^0.1.0" }, @@ -1866,8 +1604,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isomorphic-ws": { "version": "4.0.1", @@ -1877,21 +1614,6 @@ "ws": "*" } }, - "node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/js-sha256": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", @@ -1900,23 +1622,20 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "license": "MIT" + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, "node_modules/ky": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/ky/-/ky-1.8.2.tgz", - "integrity": "sha512-XybQJ3d4Ea1kI27DoelE5ZCT3bSJlibYTtQuMsyzKox3TMyayw1asgQdl54WroAm+fIA3ZCr8zXW2RpR7qWVpA==", - "license": "MIT", + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.3.tgz", + "integrity": "sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==", "engines": { "node": ">=18" }, @@ -1926,9 +1645,7 @@ }, "node_modules/latest-version": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-6.0.0.tgz", "integrity": "sha512-zfTuGx4PwpoSJ1mABs58AkM6qMzu49LZ7LT5JHprKvpGpQ+cYtfSibi3tLLrH4z7UylYU42rfBdwN8YgqbTljA==", - "license": "MIT", "dependencies": { "package-json": "^7.0.0" }, @@ -1943,7 +1660,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "license": "MIT", "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" @@ -1959,7 +1675,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -1971,7 +1686,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", - "license": "MIT", "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", @@ -1987,10 +1701,9 @@ } }, "node_modules/log-update/node_modules/ansi-escapes": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", - "license": "MIT", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", "dependencies": { "environment": "^1.0.0" }, @@ -2002,10 +1715,9 @@ } }, "node_modules/log-update/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "license": "MIT", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", @@ -2034,7 +1746,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -2043,10 +1754,9 @@ } }, "node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", - "license": "ISC", + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", "engines": { "node": "20 || >=22" } @@ -2054,14 +1764,12 @@ "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "license": "MIT" + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, "node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -2073,7 +1781,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -2085,7 +1792,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -2094,15 +1800,14 @@ } }, "node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2117,10 +1822,9 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "engines": { "node": ">=16 || 14 >=14.17" } @@ -2144,11 +1848,15 @@ "node": ">=0.10.0" } }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "node_modules/mute-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", - "license": "ISC", "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -2176,7 +1884,6 @@ "url": "https://paypal.me/jimmywarting" } ], - "license": "MIT", "engines": { "node": ">=10.5.0" } @@ -2185,7 +1892,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", @@ -2200,10 +1906,9 @@ } }, "node_modules/normalize-url": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.2.tgz", - "integrity": "sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==", - "license": "MIT", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", "engines": { "node": ">=14.16" }, @@ -2215,7 +1920,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "license": "MIT", "dependencies": { "path-key": "^4.0.0" }, @@ -2230,7 +1934,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -2242,7 +1945,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", "dependencies": { "wrappy": "1" } @@ -2251,7 +1953,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "license": "MIT", "dependencies": { "mimic-fn": "^4.0.0" }, @@ -2266,7 +1967,6 @@ "version": "10.2.0", "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", - "license": "MIT", "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", @@ -2284,7 +1984,6 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", - "license": "MIT", "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", @@ -2303,20 +2002,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-cancelable": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", - "license": "MIT", "engines": { "node": ">=14.16" } @@ -2325,7 +2014,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/package-json/-/package-json-7.0.0.tgz", "integrity": "sha512-CHJqc94AA8YfSLHGQT3DbvSIuE12NLFekpM4n7LRrAd3dOJtA911+4xe9q6nC3/jcKraq7nNS9VxgtT0KC+diA==", - "license": "MIT", "dependencies": { "got": "^11.8.2", "registry-auth-token": "^4.0.0", @@ -2342,14 +2030,12 @@ "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, "node_modules/package-json/node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "license": "MIT", "engines": { "node": ">=10" }, @@ -2361,7 +2047,6 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "license": "MIT", "dependencies": { "defer-to-connect": "^2.0.0" }, @@ -2373,7 +2058,6 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "license": "MIT", "engines": { "node": ">=10.6.0" } @@ -2382,7 +2066,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "license": "MIT", "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -2400,7 +2083,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -2415,7 +2097,6 @@ "version": "11.8.6", "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "license": "MIT", "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", @@ -2440,7 +2121,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" @@ -2453,7 +2133,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "license": "MIT", "engines": { "node": ">=8" } @@ -2462,7 +2141,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "license": "MIT", "engines": { "node": ">=10" }, @@ -2474,7 +2152,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "license": "MIT", "engines": { "node": ">=8" } @@ -2483,7 +2160,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "license": "MIT", "dependencies": { "lowercase-keys": "^2.0.0" }, @@ -2503,22 +2179,20 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "license": "BlueOak-1.0.0", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2533,7 +2207,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-2.0.0.tgz", "integrity": "sha512-xJwOWR46jcXUq6EH9yYyqp+I52skPySOeHfkxOZ2IY1AiBi/sFJhbhAKHoV3OTw/omQ45KTio9215dRJ2Yxd3Q==", - "license": "BSD-2-Clause", "dependencies": { "speedometer": "~1.0.0", "through2": "~2.0.3" @@ -2542,24 +2215,21 @@ "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "license": "ISC" + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==" }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "node_modules/pupa": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", - "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", - "license": "MIT", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.3.0.tgz", + "integrity": "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==", "dependencies": { "escape-goat": "^4.0.0" }, @@ -2574,7 +2244,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "license": "MIT", "engines": { "node": ">=10" }, @@ -2586,7 +2255,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -2600,8 +2268,7 @@ "node_modules/rc/node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/readable-stream": { "version": "2.3.7", @@ -2626,7 +2293,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", - "license": "MIT", "dependencies": { "rc": "1.2.8" }, @@ -2638,7 +2304,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", - "license": "MIT", "dependencies": { "rc": "^1.2.8" }, @@ -2649,14 +2314,12 @@ "node_modules/resolve-alpn": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "license": "MIT" + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" }, "node_modules/responselike": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "license": "MIT", "dependencies": { "lowercase-keys": "^3.0.0" }, @@ -2671,7 +2334,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "license": "MIT", "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" @@ -2687,7 +2349,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "license": "MIT", "dependencies": { "mimic-function": "^5.0.0" }, @@ -2702,7 +2363,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", "engines": { "node": ">=14" }, @@ -2711,13 +2371,12 @@ } }, "node_modules/rimraf": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", - "license": "ISC", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", "dependencies": { - "glob": "^11.0.0", - "package-json-from-dist": "^1.0.0" + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" @@ -2730,10 +2389,9 @@ } }, "node_modules/run-applescript": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", - "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", - "license": "MIT", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", "engines": { "node": ">=18" }, @@ -2742,10 +2400,9 @@ } }, "node_modules/run-async": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.5.tgz", - "integrity": "sha512-oN9GTgxUNDBumHTTDmQ8dep6VIJbgj9S3dPP+9XylVLIK4xB9XTXtKWROd5pnhdXR9k0EgO1JRcNh0T+Ny2FsA==", - "license": "MIT", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.6.tgz", + "integrity": "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==", "engines": { "node": ">=0.12.0" } @@ -2754,7 +2411,6 @@ "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } @@ -2762,14 +2418,12 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "bin": { "semver": "bin/semver.js" }, @@ -2781,7 +2435,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -2793,7 +2446,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", "engines": { "node": ">=8" } @@ -2804,10 +2456,9 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/slice-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", - "license": "MIT", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" @@ -2828,7 +2479,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", "engines": { "node": ">= 10.x" } @@ -2837,7 +2487,6 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -2862,7 +2511,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -2875,64 +2523,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -2941,33 +2537,10 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -2979,21 +2552,27 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/stubborn-fs": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-1.2.5.tgz", - "integrity": "sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stubborn-fs/-/stubborn-fs-2.0.0.tgz", + "integrity": "sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==", + "dependencies": { + "stubborn-utils": "^1.0.1" + } + }, + "node_modules/stubborn-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stubborn-utils/-/stubborn-utils-1.0.2.tgz", + "integrity": "sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==" }, "node_modules/system-architecture": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz", "integrity": "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==", - "license": "MIT", "engines": { "node": ">=18" }, @@ -3010,40 +2589,26 @@ "xtend": "~4.0.1" } }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "license": "MIT" + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==" }, "node_modules/unused-filename": { "version": "1.0.0", @@ -3061,7 +2626,6 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-7.3.1.tgz", "integrity": "sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==", - "license": "BSD-2-Clause", "dependencies": { "boxen": "^8.0.1", "chalk": "^5.3.0", @@ -3085,7 +2649,6 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-9.0.0.tgz", "integrity": "sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==", - "license": "MIT", "dependencies": { "package-json": "^10.0.0" }, @@ -3100,7 +2663,6 @@ "version": "10.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-10.0.1.tgz", "integrity": "sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==", - "license": "MIT", "dependencies": { "ky": "^1.2.0", "registry-auth-token": "^5.0.2", @@ -3115,12 +2677,11 @@ } }, "node_modules/update-notifier/node_modules/registry-auth-token": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", - "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", - "license": "MIT", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.1.tgz", + "integrity": "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==", "dependencies": { - "@pnpm/npm-conf": "^2.1.0" + "@pnpm/npm-conf": "^3.0.2" }, "engines": { "node": ">=14" @@ -3130,7 +2691,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", - "license": "MIT", "dependencies": { "rc": "1.2.8" }, @@ -3150,6 +2710,7 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", "bin": { "uuid": "dist/bin/uuid" } @@ -3166,22 +2727,19 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/when-exit": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.4.tgz", - "integrity": "sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg==", - "license": "MIT" + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.5.tgz", + "integrity": "sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==" }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -3196,7 +2754,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "license": "MIT", "dependencies": { "string-width": "^7.0.0" }, @@ -3211,7 +2768,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3221,94 +2777,10 @@ "node": ">=8" } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", "engines": { "node": ">=8" } @@ -3317,7 +2789,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3331,14 +2802,12 @@ "node_modules/wrap-ansi/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", "engines": { "node": ">=8" } @@ -3347,7 +2816,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3361,7 +2829,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3375,10 +2842,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "license": "MIT", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", "engines": { "node": ">=10.0.0" }, @@ -3399,7 +2865,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", - "license": "MIT", "dependencies": { "is-wsl": "^3.1.0" }, @@ -3414,7 +2879,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -3442,7 +2906,6 @@ "version": "18.0.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", - "license": "MIT", "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", @@ -3459,16 +2922,14 @@ "version": "22.0.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "license": "ISC", "engines": { "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", - "license": "MIT", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", "engines": { "node": ">=18" }, diff --git a/package.json b/package.json index 95a7f29..5a4f4b5 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "type": "module", "version": "1.1.0", "dependencies": { + "@automattic/vip-search-replace": "^2.0.0", "@humanmade/ssm": "^0.0.1", "ansi-escapes": "^2.0.0", "application-config": "^1.0.1", From b7f11d2ec0baf34e429ea991e03b8194dc7db0b2 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Mon, 4 May 2026 21:12:16 +0800 Subject: [PATCH 02/22] Add --latest flag to skip prompt and auto-confirm --latest uses the most recent existing backup without prompting and implies --yes, making it suitable for scripted or repeated syncs. Errors if no backup exists for the app. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 16 +++++++++++++--- lib/commands/stack/sync/database.js | 16 +++++++++++++--- lib/commands/stack/sync/uploads.js | 16 +++++++++++++--- lib/commands/stack/sync/util.js | 5 +++++ 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index 2cda375..e03de37 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -15,6 +15,7 @@ import { extractArchive, findCompletedBackup, formatAge, + getLatestBackup, promptBackupChoice, resolveMappings, runComposerServer, @@ -34,6 +35,7 @@ const handler = async function ( argv ) { outputDir, keepArchive, yes, + latest, resume, debug, tables, @@ -100,12 +102,20 @@ const handler = async function ( argv ) { let logId = resume; let backup = null; - if ( ! logId ) { + if ( latest ) { + backup = await getLatestBackup( v, app ); + if ( ! backup ) { + console.error( chalk.red( `No existing backup found for ${ chalk.bold( app ) }.` ) ); + process.exit( 1 ); + } + const age = formatAge( new Date( backup.date ) ); + console.log( chalk.dim( `Using latest backup: ${ backup.id } (${ age } old)` ) ); + } else if ( ! logId ) { backup = await promptBackupChoice( v, app ); } - // 4. Confirm with full context - if ( ! yes ) { + // 4. Confirm with full context (--latest implies --yes) + if ( ! yes && ! latest ) { let confirmMsg; if ( backup ) { const age = formatAge( new Date( backup.date ) ); diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js index 6bce9f7..cbf4429 100644 --- a/lib/commands/stack/sync/database.js +++ b/lib/commands/stack/sync/database.js @@ -13,6 +13,7 @@ import { extractArchive, findCompletedBackup, formatAge, + getLatestBackup, promptBackupChoice, resolveMappings, runComposerServer, @@ -31,6 +32,7 @@ const handler = async function ( argv ) { outputDir, keepArchive, yes, + latest, resume, debug, tables, @@ -97,12 +99,20 @@ const handler = async function ( argv ) { let logId = resume; let backup = null; - if ( ! logId ) { + if ( latest ) { + backup = await getLatestBackup( v, app ); + if ( ! backup ) { + console.error( chalk.red( `No existing backup found for ${ chalk.bold( app ) }.` ) ); + process.exit( 1 ); + } + const age = formatAge( new Date( backup.date ) ); + console.log( chalk.dim( `Using latest backup: ${ backup.id } (${ age } old)` ) ); + } else if ( ! logId ) { backup = await promptBackupChoice( v, app ); } - // 4. Confirm with full context - if ( ! yes ) { + // 4. Confirm with full context (--latest implies --yes) + if ( ! yes && ! latest ) { let confirmMsg; if ( backup ) { const age = formatAge( new Date( backup.date ) ); diff --git a/lib/commands/stack/sync/uploads.js b/lib/commands/stack/sync/uploads.js index a56a8e1..1803fc2 100644 --- a/lib/commands/stack/sync/uploads.js +++ b/lib/commands/stack/sync/uploads.js @@ -14,6 +14,7 @@ import { extractArchive, findCompletedBackup, formatAge, + getLatestBackup, promptBackupChoice, runComposerServer, startBackup, @@ -30,6 +31,7 @@ const handler = async function ( argv ) { outputDir, keepArchive, yes, + latest, resume, debug, } = argv; @@ -52,12 +54,20 @@ const handler = async function ( argv ) { let logId = resume; let backup = null; - if ( ! logId ) { + if ( latest ) { + backup = await getLatestBackup( v, app ); + if ( ! backup ) { + console.error( chalk.red( `No existing backup found for ${ chalk.bold( app ) }.` ) ); + process.exit( 1 ); + } + const age = formatAge( new Date( backup.date ) ); + console.log( chalk.dim( `Using latest backup: ${ backup.id } (${ age } old)` ) ); + } else if ( ! logId ) { backup = await promptBackupChoice( v, app ); } - // 3. Confirm with full context - if ( ! yes ) { + // 3. Confirm with full context (--latest implies --yes) + if ( ! yes && ! latest ) { let confirmMsg; if ( backup ) { const age = formatAge( new Date( backup.date ) ); diff --git a/lib/commands/stack/sync/util.js b/lib/commands/stack/sync/util.js index 80c1a51..5ba10e5 100644 --- a/lib/commands/stack/sync/util.js +++ b/lib/commands/stack/sync/util.js @@ -307,6 +307,11 @@ export function addCommonOptions( cmd ) { type: 'boolean', default: false, } ); + cmd.option( 'latest', { + description: 'Use the latest existing backup without prompting. Implies --yes.', + type: 'boolean', + default: false, + } ); cmd.option( 'resume', { description: 'Resume watching an already-started remote backup task.', type: 'string', From 6d86bec7ba67bbf30ab0819247b5dcefa62c7869 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Mon, 4 May 2026 21:26:56 +0800 Subject: [PATCH 03/22] Update user-facing strings to say "Altis Dashboard" instead of "Vantage" Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/config/setup.js | 2 +- lib/commands/stack/sync/all.js | 2 +- lib/commands/stack/sync/database.js | 2 +- lib/commands/stack/sync/index.js | 2 +- lib/commands/stack/sync/uploads.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/commands/config/setup.js b/lib/commands/config/setup.js index cae25e3..3f3769e 100644 --- a/lib/commands/config/setup.js +++ b/lib/commands/config/setup.js @@ -10,7 +10,7 @@ const handler = function (argv) { questions.push({ type: "confirm", name: "resetVantage", - message: `Logged in to Vantage. Reset?`, + message: `Logged in to Altis Dashboard. Reset?`, default: false, }); } diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index e03de37..38ab41a 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -240,7 +240,7 @@ const handler = async function ( argv ) { export default { command: 'all ', - description: 'Sync database and uploads from a remote Vantage app into local-server.', + description: 'Sync database and uploads from a remote Altis Dashboard app into local-server.', builder: cmd => { addCommonOptions( cmd ); addDatabaseOptions( cmd ); diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js index cbf4429..a140de6 100644 --- a/lib/commands/stack/sync/database.js +++ b/lib/commands/stack/sync/database.js @@ -217,7 +217,7 @@ const handler = async function ( argv ) { export default { command: 'database ', - description: 'Sync the database from a remote Vantage app into local-server.', + description: 'Sync the database from a remote Altis Dashboard app into local-server.', builder: cmd => { addCommonOptions( cmd ); addDatabaseOptions( cmd ); diff --git a/lib/commands/stack/sync/index.js b/lib/commands/stack/sync/index.js index 865aed8..f6829d3 100644 --- a/lib/commands/stack/sync/index.js +++ b/lib/commands/stack/sync/index.js @@ -2,7 +2,7 @@ import buildSubcommands from '../../../buildSubcommands.js'; export default { command: 'sync', - description: 'Sync data from a remote Vantage app into local-server.', + description: 'Sync data from a remote Altis Dashboard app into local-server.', builder: async function ( command ) { command.demandCommand( 1 ); const subcommands = await buildSubcommands( new URL( '.', import.meta.url ).pathname ); diff --git a/lib/commands/stack/sync/uploads.js b/lib/commands/stack/sync/uploads.js index 1803fc2..d64286c 100644 --- a/lib/commands/stack/sync/uploads.js +++ b/lib/commands/stack/sync/uploads.js @@ -171,7 +171,7 @@ const handler = async function ( argv ) { export default { command: 'uploads ', - description: 'Sync uploads from a remote Vantage app into local-server.', + description: 'Sync uploads from a remote Altis Dashboard app into local-server.', builder: cmd => { addCommonOptions( cmd ); addUploadsOptions( cmd ); From ff8dd39eb1bd5bff80ee2c013b8fcbd4d414a4e0 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Mon, 4 May 2026 22:35:23 +0800 Subject: [PATCH 04/22] Rename sync to sync-local and update command strings to use app Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 4 ++-- lib/commands/stack/sync/database.js | 4 ++-- lib/commands/stack/sync/index.js | 2 +- lib/commands/stack/sync/uploads.js | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index 38ab41a..dd581f8 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -145,7 +145,7 @@ const handler = async function ( argv ) { try { logId = await startBackup( v, app, opts ); backupSpinner.succeed( `Backup started (log: ${ chalk.dim( logId ) })` ); - console.log( chalk.dim( `Resume later with: altis-cli stack sync all ${ app } --resume ${ logId }` ) ); + console.log( chalk.dim( `Resume later with: altis-cli app sync-local all ${ app } --resume ${ logId }` ) ); } catch ( err ) { backupSpinner.fail( `Failed to start backup: ${ err.message }` ); process.exit( 1 ); @@ -162,7 +162,7 @@ const handler = async function ( argv ) { backup = await findCompletedBackup( v, app, startTime ); if ( ! backup ) { findSpinner.fail( - `Backup completed but no download URL found. Run:\n altis-cli stack backups ${ app }` + `Backup completed but no download URL found. Run:\n altis-cli app backups ${ app }` ); process.exit( 1 ); } diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js index a140de6..8a2a33f 100644 --- a/lib/commands/stack/sync/database.js +++ b/lib/commands/stack/sync/database.js @@ -141,7 +141,7 @@ const handler = async function ( argv ) { try { logId = await startBackup( v, app, opts ); backupSpinner.succeed( `Backup started (log: ${ chalk.dim( logId ) })` ); - console.log( chalk.dim( `Resume later with: altis-cli stack sync database ${ app } --resume ${ logId }` ) ); + console.log( chalk.dim( `Resume later with: altis-cli app sync-local database ${ app } --resume ${ logId }` ) ); } catch ( err ) { backupSpinner.fail( `Failed to start backup: ${ err.message }` ); process.exit( 1 ); @@ -158,7 +158,7 @@ const handler = async function ( argv ) { backup = await findCompletedBackup( v, app, startTime ); if ( ! backup ) { findSpinner.fail( - `Backup completed but no download URL found. Run:\n altis-cli stack backups ${ app }` + `Backup completed but no download URL found. Run:\n altis-cli app backups ${ app }` ); process.exit( 1 ); } diff --git a/lib/commands/stack/sync/index.js b/lib/commands/stack/sync/index.js index f6829d3..7f95daa 100644 --- a/lib/commands/stack/sync/index.js +++ b/lib/commands/stack/sync/index.js @@ -1,7 +1,7 @@ import buildSubcommands from '../../../buildSubcommands.js'; export default { - command: 'sync', + command: 'sync-local', description: 'Sync data from a remote Altis Dashboard app into local-server.', builder: async function ( command ) { command.demandCommand( 1 ); diff --git a/lib/commands/stack/sync/uploads.js b/lib/commands/stack/sync/uploads.js index d64286c..86b807b 100644 --- a/lib/commands/stack/sync/uploads.js +++ b/lib/commands/stack/sync/uploads.js @@ -96,7 +96,7 @@ const handler = async function ( argv ) { try { logId = await startBackup( v, app, opts ); backupSpinner.succeed( `Backup started (log: ${ chalk.dim( logId ) })` ); - console.log( chalk.dim( `Resume later with: altis-cli stack sync uploads ${ app } --resume ${ logId }` ) ); + console.log( chalk.dim( `Resume later with: altis-cli app sync-local uploads ${ app } --resume ${ logId }` ) ); } catch ( err ) { backupSpinner.fail( `Failed to start backup: ${ err.message }` ); process.exit( 1 ); @@ -113,7 +113,7 @@ const handler = async function ( argv ) { backup = await findCompletedBackup( v, app, startTime ); if ( ! backup ) { findSpinner.fail( - `Backup completed but no download URL found. Run:\n altis-cli stack backups ${ app }` + `Backup completed but no download URL found. Run:\n altis-cli app backups ${ app }` ); process.exit( 1 ); } From b4687591215375fcffcfde16473d1bebff533396 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Mon, 4 May 2026 23:23:57 +0800 Subject: [PATCH 05/22] Add partial database sync with --table and --site-id options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace --tables with --table (repeatable array, comma-separated) - Add --site-id to database and all: resolves multisite site IDs to table names via the database-tables endpoint before backup - Add --site-id to uploads: maps to sites/{id}/ uploads path prefix - Error early on multiple --site-id for uploads and all commands - Warn when --site-id is passed with --latest or --resume - Add app sites command to list multisite site ID → domain mapping Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sites.js | 21 ++++++++++++++ lib/commands/stack/sync/all.js | 24 +++++++++++++-- lib/commands/stack/sync/database.js | 16 ++++++++-- lib/commands/stack/sync/uploads.js | 10 ++++++- lib/commands/stack/sync/util.js | 45 +++++++++++++++++++++++++++-- 5 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 lib/commands/stack/sites.js diff --git a/lib/commands/stack/sites.js b/lib/commands/stack/sites.js new file mode 100644 index 0000000..8fcd3d2 --- /dev/null +++ b/lib/commands/stack/sites.js @@ -0,0 +1,21 @@ +import { getApp, fetchJSON, printJSON, printTable } from './util.js'; +import Vantage from '../../vantage.js'; + +const handler = async argv => { + const app = await getApp( argv ); + const v = new Vantage( argv.config ); + const data = await fetchJSON( v, `stack/applications/${ app }/database-tables` ); + const sites = data.sites || []; + if ( argv.json ) { + printJSON( sites ); + return; + } + printTable( sites.map( ( { id, domain, path } ) => ( { id, domain, path } ) ) ); +}; + +export default { + command: 'sites [stack]', + description: 'List multisite sites and their IDs for an application.', + builder: yargs => yargs.option( 'json', { type: 'boolean', description: 'Print JSON output.' } ), + handler, +}; diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index dd581f8..d2f90a2 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -18,6 +18,7 @@ import { getLatestBackup, promptBackupChoice, resolveMappings, + resolveTablesForSiteIds, runComposerServer, runPostSync, searchReplaceAndImport, @@ -38,7 +39,8 @@ const handler = async function ( argv ) { latest, resume, debug, - tables, + table, + siteId, searchReplaceKey, replace: replacePairs, skipSearchReplace, @@ -114,6 +116,15 @@ const handler = async function ( argv ) { backup = await promptBackupChoice( v, app ); } + if ( siteId && siteId.length > 1 ) { + console.error( chalk.red( 'Error: --site-id only accepts a single value for full sync (uploads can only target one path).' ) ); + process.exit( 1 ); + } + + if ( siteId?.length && ( latest || logId ) ) { + console.log( chalk.yellow( 'Warning: --site-id is ignored when using --latest or --resume (backup already created).' ) ); + } + // 4. Confirm with full context (--latest implies --yes) if ( ! yes && ! latest ) { let confirmMsg; @@ -136,11 +147,18 @@ const handler = async function ( argv ) { // 5. Create backup if needed if ( ! backup && ! logId ) { const backupSpinner = ora( `Creating remote backup for ${ chalk.bold( app ) }…` ).start(); + let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; + if ( siteId && siteId.length ) { + const siteIdList = siteId.flatMap( s => String( s ).split( ',' ) ); + const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); + tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; + } + const resolvedUploadsPath = siteId?.length ? `sites/${ siteId[0] }` : uploadsPath; const opts = { database: 1, uploads: 1, - ...( tables ? { tables: tables.split( ',' ) } : {} ), - ...( uploadsPath ? { uploads_path: uploadsPath } : {} ), + ...( tableList.length ? { tables: tableList } : {} ), + ...( resolvedUploadsPath ? { uploads_path: resolvedUploadsPath } : {} ), }; try { logId = await startBackup( v, app, opts ); diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js index 8a2a33f..c035f03 100644 --- a/lib/commands/stack/sync/database.js +++ b/lib/commands/stack/sync/database.js @@ -16,6 +16,7 @@ import { getLatestBackup, promptBackupChoice, resolveMappings, + resolveTablesForSiteIds, runComposerServer, runPostSync, searchReplaceAndImport, @@ -35,7 +36,8 @@ const handler = async function ( argv ) { latest, resume, debug, - tables, + table, + siteId, searchReplaceKey, replace: replacePairs, skipSearchReplace, @@ -111,6 +113,10 @@ const handler = async function ( argv ) { backup = await promptBackupChoice( v, app ); } + if ( siteId?.length && ( latest || logId ) ) { + console.log( chalk.yellow( 'Warning: --site-id is ignored when using --latest or --resume (backup already created).' ) ); + } + // 4. Confirm with full context (--latest implies --yes) if ( ! yes && ! latest ) { let confirmMsg; @@ -133,10 +139,16 @@ const handler = async function ( argv ) { // 5. Create backup if needed if ( ! backup && ! logId ) { const backupSpinner = ora( `Creating remote backup for ${ chalk.bold( app ) }…` ).start(); + let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; + if ( siteId && siteId.length ) { + const siteIdList = siteId.flatMap( s => String( s ).split( ',' ) ); + const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); + tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; + } const opts = { database: 1, uploads: 0, - ...( tables ? { tables: tables.split( ',' ) } : {} ), + ...( tableList.length ? { tables: tableList } : {} ), }; try { logId = await startBackup( v, app, opts ); diff --git a/lib/commands/stack/sync/uploads.js b/lib/commands/stack/sync/uploads.js index 86b807b..3d0f104 100644 --- a/lib/commands/stack/sync/uploads.js +++ b/lib/commands/stack/sync/uploads.js @@ -28,6 +28,7 @@ const handler = async function ( argv ) { config, path: pathOpt, uploadsPath, + siteId, outputDir, keepArchive, yes, @@ -36,6 +37,13 @@ const handler = async function ( argv ) { debug, } = argv; + if ( siteId && siteId.length > 1 ) { + console.error( chalk.red( 'Error: --site-id only accepts a single value for uploads sync.' ) ); + process.exit( 1 ); + } + + const resolvedUploadsPath = siteId?.length ? `sites/${ siteId[0] }` : uploadsPath; + const localPath = pathOpt || process.cwd(); const v = new Vantage( config ); @@ -91,7 +99,7 @@ const handler = async function ( argv ) { const opts = { database: 0, uploads: 1, - ...( uploadsPath ? { uploads_path: uploadsPath } : {} ), + ...( resolvedUploadsPath ? { uploads_path: resolvedUploadsPath } : {} ), }; try { logId = await startBackup( v, app, opts ); diff --git a/lib/commands/stack/sync/util.js b/lib/commands/stack/sync/util.js index 5ba10e5..7f08e1a 100644 --- a/lib/commands/stack/sync/util.js +++ b/lib/commands/stack/sync/util.js @@ -286,6 +286,37 @@ export async function runPostSync( localPath ) { } } +// --- Resolve table names for multisite site IDs --- + +export async function resolveTablesForSiteIds( v, app, siteIds ) { + const resp = await v.fetch( `stack/applications/${ app }/database-tables` ); + if ( ! resp.ok ) { + throw new Error( `Failed to fetch table list: ${ resp.status }` ); + } + const data = await resp.json(); + const tables = Array.isArray( data ) ? data : ( data.tables || [] ); + + // Derive the table prefix from the blogs table (e.g. "wp_blogs" → prefix "wp_") + let prefix = ''; + for ( const name of tables ) { + const m = name.match( /^(.+_)blogs$/ ); + if ( m ) { + prefix = m[1]; + break; + } + } + if ( ! prefix ) { + throw new Error( 'Could not determine table prefix — is this a multisite install?' ); + } + + const result = []; + for ( const id of siteIds ) { + const pattern = `${ prefix }${ id }_`; + result.push( ...tables.filter( t => t.startsWith( pattern ) ) ); + } + return result; +} + // --- Common yargs option builders --- export function addCommonOptions( cmd ) { @@ -329,9 +360,13 @@ export function addCommonOptions( cmd ) { } export function addDatabaseOptions( cmd ) { - cmd.option( 'tables', { - description: 'Comma-separated list of tables to include in the backup.', - type: 'string', + cmd.option( 'table', { + description: 'Table to include in the partial backup. Repeatable; also accepts comma-separated values.', + type: 'array', + } ); + cmd.option( 'site-id', { + description: 'Multisite site ID to include in the partial backup. Repeatable; also accepts comma-separated values.', + type: 'array', } ); cmd.option( 'search-replace-key', { description: 'composer.json key under extra.altis.cloud.search-replace. Defaults to local-server.', @@ -364,6 +399,10 @@ export function addUploadsOptions( cmd ) { description: 'Uploads prefix to export from the remote app.', type: 'string', } ); + cmd.option( 'site-id', { + description: 'Multisite site ID to sync uploads for. Maps to sites/{id}/ prefix.', + type: 'array', + } ); } // --- Internal helpers --- From e470177aa64791b9fc42d14d1988fe7681bbc814 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Tue, 5 May 2026 00:03:16 +0800 Subject: [PATCH 06/22] Handle site ID 1 as main site for partial sync Site ID 1 uses the base table prefix (wp_posts etc.) and stores uploads at the root, not sites/1/. Syncing all data when site ID 1 is given and noting this to the user. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 20 +++++++++++++++++--- lib/commands/stack/sync/database.js | 9 +++++++-- lib/commands/stack/sync/uploads.js | 11 ++++++++++- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index d2f90a2..b1da76e 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -150,10 +150,24 @@ const handler = async function ( argv ) { let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; if ( siteId && siteId.length ) { const siteIdList = siteId.flatMap( s => String( s ).split( ',' ) ); - const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); - tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; + if ( siteIdList.includes( '1' ) ) { + console.log( chalk.yellow( 'Note: site ID 1 is the main site — its tables use the base prefix, syncing all tables.' ) ); + tableList = []; + } else { + const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); + tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; + } + } + // Site ID 1 is the main site — its uploads are at the root, not sites/1/ + let resolvedUploadsPath = uploadsPath; + if ( siteId?.length ) { + if ( Number( siteId[0] ) === 1 ) { + console.log( chalk.yellow( 'Note: site ID 1 is the main site — syncing all uploads.' ) ); + resolvedUploadsPath = null; + } else { + resolvedUploadsPath = `sites/${ siteId[0] }`; + } } - const resolvedUploadsPath = siteId?.length ? `sites/${ siteId[0] }` : uploadsPath; const opts = { database: 1, uploads: 1, diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js index c035f03..10c1b64 100644 --- a/lib/commands/stack/sync/database.js +++ b/lib/commands/stack/sync/database.js @@ -142,8 +142,13 @@ const handler = async function ( argv ) { let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; if ( siteId && siteId.length ) { const siteIdList = siteId.flatMap( s => String( s ).split( ',' ) ); - const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); - tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; + if ( siteIdList.includes( '1' ) ) { + console.log( chalk.yellow( 'Note: site ID 1 is the main site — its tables use the base prefix, syncing all tables.' ) ); + tableList = []; + } else { + const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); + tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; + } } const opts = { database: 1, diff --git a/lib/commands/stack/sync/uploads.js b/lib/commands/stack/sync/uploads.js index 3d0f104..fa220aa 100644 --- a/lib/commands/stack/sync/uploads.js +++ b/lib/commands/stack/sync/uploads.js @@ -42,7 +42,16 @@ const handler = async function ( argv ) { process.exit( 1 ); } - const resolvedUploadsPath = siteId?.length ? `sites/${ siteId[0] }` : uploadsPath; + // Site ID 1 is the main site — its uploads are at the root, not sites/1/ + let resolvedUploadsPath = uploadsPath; + if ( siteId?.length ) { + if ( Number( siteId[0] ) === 1 ) { + console.log( chalk.yellow( 'Note: site ID 1 is the main site — syncing all uploads.' ) ); + resolvedUploadsPath = null; + } else { + resolvedUploadsPath = `sites/${ siteId[0] }`; + } + } const localPath = pathOpt || process.cwd(); const v = new Vantage( config ); From 2914452b2175357f65b75933154319640b3df891 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Tue, 5 May 2026 13:34:25 +0800 Subject: [PATCH 07/22] Revert site ID 1 special case for database sync Only applies to uploads where sites/1/ doesn't exist. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 9 ++------- lib/commands/stack/sync/database.js | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index b1da76e..3dae936 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -150,13 +150,8 @@ const handler = async function ( argv ) { let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; if ( siteId && siteId.length ) { const siteIdList = siteId.flatMap( s => String( s ).split( ',' ) ); - if ( siteIdList.includes( '1' ) ) { - console.log( chalk.yellow( 'Note: site ID 1 is the main site — its tables use the base prefix, syncing all tables.' ) ); - tableList = []; - } else { - const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); - tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; - } + const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); + tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; } // Site ID 1 is the main site — its uploads are at the root, not sites/1/ let resolvedUploadsPath = uploadsPath; diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js index 10c1b64..c035f03 100644 --- a/lib/commands/stack/sync/database.js +++ b/lib/commands/stack/sync/database.js @@ -142,13 +142,8 @@ const handler = async function ( argv ) { let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; if ( siteId && siteId.length ) { const siteIdList = siteId.flatMap( s => String( s ).split( ',' ) ); - if ( siteIdList.includes( '1' ) ) { - console.log( chalk.yellow( 'Note: site ID 1 is the main site — its tables use the base prefix, syncing all tables.' ) ); - tableList = []; - } else { - const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); - tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; - } + const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); + tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; } const opts = { database: 1, From fa086d367dcee710ae4526e0fe905d3069c7abfb Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 22 May 2026 22:33:16 +0800 Subject: [PATCH 08/22] Always create a new partial backup when --site-id or --table is set Existing backups are full backups, so offering "use latest" when a partial sync is requested was misleading and would silently ignore the site/table filter. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/database.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js index c035f03..e571dd9 100644 --- a/lib/commands/stack/sync/database.js +++ b/lib/commands/stack/sync/database.js @@ -101,20 +101,30 @@ const handler = async function ( argv ) { let logId = resume; let backup = null; + const isPartialSync = siteId?.length || table?.length; + if ( latest ) { - backup = await getLatestBackup( v, app ); - if ( ! backup ) { - console.error( chalk.red( `No existing backup found for ${ chalk.bold( app ) }.` ) ); - process.exit( 1 ); + if ( isPartialSync ) { + console.log( chalk.yellow( 'Warning: --latest cannot be used with --site-id or --table (existing backups are full backups). Creating a new partial backup instead.' ) ); + } else { + backup = await getLatestBackup( v, app ); + if ( ! backup ) { + console.error( chalk.red( `No existing backup found for ${ chalk.bold( app ) }.` ) ); + process.exit( 1 ); + } + const age = formatAge( new Date( backup.date ) ); + console.log( chalk.dim( `Using latest backup: ${ backup.id } (${ age } old)` ) ); } - const age = formatAge( new Date( backup.date ) ); - console.log( chalk.dim( `Using latest backup: ${ backup.id } (${ age } old)` ) ); } else if ( ! logId ) { - backup = await promptBackupChoice( v, app ); + if ( isPartialSync ) { + console.log( chalk.dim( 'Partial sync requested — creating a new backup (existing backups are full backups).' ) ); + } else { + backup = await promptBackupChoice( v, app ); + } } - if ( siteId?.length && ( latest || logId ) ) { - console.log( chalk.yellow( 'Warning: --site-id is ignored when using --latest or --resume (backup already created).' ) ); + if ( logId && isPartialSync ) { + console.log( chalk.yellow( 'Warning: --site-id/--table is ignored when using --resume (backup already created).' ) ); } // 4. Confirm with full context (--latest implies --yes) From 5f4f79a3442d88a78bc696420bbb67107bebb6a9 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 22 May 2026 22:56:31 +0800 Subject: [PATCH 09/22] Fix --site-id handling in sync-local all command Move multi-site-id guard before the backup choice prompt, and apply the same isPartialSync logic as database.js so existing full backups are never offered when a partial sync is requested. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 38 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index 3dae936..45fb17e 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -99,30 +99,40 @@ const handler = async function ( argv ) { console.log( chalk.dim( `Search-replace: ${ Object.keys( mappings ).length } mapping(s) from composer.json[${ searchReplaceKey }]` ) ); } + if ( siteId && siteId.length > 1 ) { + console.error( chalk.red( 'Error: --site-id only accepts a single value for full sync (uploads can only target one path).' ) ); + process.exit( 1 ); + } + // 3. Choose backup (before confirm, so the user knows what they're agreeing to) const startTime = new Date(); let logId = resume; let backup = null; + const isPartialSync = siteId?.length || table?.length; + if ( latest ) { - backup = await getLatestBackup( v, app ); - if ( ! backup ) { - console.error( chalk.red( `No existing backup found for ${ chalk.bold( app ) }.` ) ); - process.exit( 1 ); + if ( isPartialSync ) { + console.log( chalk.yellow( 'Warning: --latest cannot be used with --site-id or --table (existing backups are full backups). Creating a new partial backup instead.' ) ); + } else { + backup = await getLatestBackup( v, app ); + if ( ! backup ) { + console.error( chalk.red( `No existing backup found for ${ chalk.bold( app ) }.` ) ); + process.exit( 1 ); + } + const age = formatAge( new Date( backup.date ) ); + console.log( chalk.dim( `Using latest backup: ${ backup.id } (${ age } old)` ) ); } - const age = formatAge( new Date( backup.date ) ); - console.log( chalk.dim( `Using latest backup: ${ backup.id } (${ age } old)` ) ); } else if ( ! logId ) { - backup = await promptBackupChoice( v, app ); - } - - if ( siteId && siteId.length > 1 ) { - console.error( chalk.red( 'Error: --site-id only accepts a single value for full sync (uploads can only target one path).' ) ); - process.exit( 1 ); + if ( isPartialSync ) { + console.log( chalk.dim( 'Partial sync requested — creating a new backup (existing backups are full backups).' ) ); + } else { + backup = await promptBackupChoice( v, app ); + } } - if ( siteId?.length && ( latest || logId ) ) { - console.log( chalk.yellow( 'Warning: --site-id is ignored when using --latest or --resume (backup already created).' ) ); + if ( logId && isPartialSync ) { + console.log( chalk.yellow( 'Warning: --site-id/--table is ignored when using --resume (backup already created).' ) ); } // 4. Confirm with full context (--latest implies --yes) From 4477dc2a3c6003a8039cdaa2f80a0bd474963928 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 22 May 2026 22:59:32 +0800 Subject: [PATCH 10/22] Catch comma-separated --site-id values in single-site guards --site-id 1,2 is parsed by yargs as ['1,2'] (length 1), bypassing the length > 1 check. Flatten comma-separated values before the guard. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 2 +- lib/commands/stack/sync/uploads.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index 45fb17e..fb52b37 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -99,7 +99,7 @@ const handler = async function ( argv ) { console.log( chalk.dim( `Search-replace: ${ Object.keys( mappings ).length } mapping(s) from composer.json[${ searchReplaceKey }]` ) ); } - if ( siteId && siteId.length > 1 ) { + if ( siteId?.flatMap( s => String( s ).split( ',' ) ).length > 1 ) { console.error( chalk.red( 'Error: --site-id only accepts a single value for full sync (uploads can only target one path).' ) ); process.exit( 1 ); } diff --git a/lib/commands/stack/sync/uploads.js b/lib/commands/stack/sync/uploads.js index fa220aa..5b7d02e 100644 --- a/lib/commands/stack/sync/uploads.js +++ b/lib/commands/stack/sync/uploads.js @@ -37,7 +37,7 @@ const handler = async function ( argv ) { debug, } = argv; - if ( siteId && siteId.length > 1 ) { + if ( siteId?.flatMap( s => String( s ).split( ',' ) ).length > 1 ) { console.error( chalk.red( 'Error: --site-id only accepts a single value for uploads sync.' ) ); process.exit( 1 ); } From dbfeeb6e4fa3c6f4c38060a0026a44b35bcf20a5 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Wed, 27 May 2026 21:33:32 +0800 Subject: [PATCH 11/22] Warn when --site-id 1 is used for DB sync Site ID 1 tables have no numeric prefix so resolveTablesForSiteIds returns empty, silently producing a full DB backup. Print a note so the user knows. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 3 +++ lib/commands/stack/sync/database.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index fb52b37..5bfdaff 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -160,6 +160,9 @@ const handler = async function ( argv ) { let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; if ( siteId && siteId.length ) { const siteIdList = siteId.flatMap( s => String( s ).split( ',' ) ); + if ( siteIdList.includes( '1' ) ) { + console.log( chalk.yellow( 'Note: site ID 1 is the main site — its tables have no numeric prefix, so the full DB will be synced.' ) ); + } const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; } diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js index e571dd9..9693774 100644 --- a/lib/commands/stack/sync/database.js +++ b/lib/commands/stack/sync/database.js @@ -152,6 +152,9 @@ const handler = async function ( argv ) { let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; if ( siteId && siteId.length ) { const siteIdList = siteId.flatMap( s => String( s ).split( ',' ) ); + if ( siteIdList.includes( '1' ) ) { + console.log( chalk.yellow( 'Note: site ID 1 is the main site — its tables have no numeric prefix, so the full DB will be synced.' ) ); + } const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; } From 16415de955d24dbfcc0195b643aa0d94f7f12f83 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Wed, 27 May 2026 21:35:24 +0800 Subject: [PATCH 12/22] Probe for s3 subcommand before falling back to import-uploads The broad try/catch masked real import errors. Now probe composer server s3 --help first (same pattern as runPostSync) and only use the fallback if the command isn't registered. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 7 ++----- lib/commands/stack/sync/uploads.js | 7 ++----- lib/commands/stack/sync/util.js | 13 +++++++++++++ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index 5bfdaff..3f82839 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -16,6 +16,7 @@ import { findCompletedBackup, formatAge, getLatestBackup, + importUploads, promptBackupChoice, resolveMappings, resolveTablesForSiteIds, @@ -249,11 +250,7 @@ const handler = async function ( argv ) { copySpinner.succeed( 'Uploads copied.' ); console.log( chalk.dim( 'Syncing uploads to local S3…' ) ); - try { - await runComposerServer( localPath, [ 's3', 'import-uploads' ] ); - } catch { - await runComposerServer( localPath, [ 'import-uploads' ] ); - } + await importUploads( localPath ); console.log( chalk.bold.green( `\n✓ Database and uploads synced from ${ app }` ) ); diff --git a/lib/commands/stack/sync/uploads.js b/lib/commands/stack/sync/uploads.js index 5b7d02e..78bb23f 100644 --- a/lib/commands/stack/sync/uploads.js +++ b/lib/commands/stack/sync/uploads.js @@ -15,6 +15,7 @@ import { findCompletedBackup, formatAge, getLatestBackup, + importUploads, promptBackupChoice, runComposerServer, startBackup, @@ -157,11 +158,7 @@ const handler = async function ( argv ) { // 8. Sync into local-server S3 console.log( chalk.dim( 'Syncing uploads to local S3…' ) ); - try { - await runComposerServer( localPath, [ 's3', 'import-uploads' ] ); - } catch { - await runComposerServer( localPath, [ 'import-uploads' ] ); - } + await importUploads( localPath ); console.log( chalk.bold.green( `\n✓ Uploads synced from ${ app }` ) ); diff --git a/lib/commands/stack/sync/util.js b/lib/commands/stack/sync/util.js index 7f08e1a..fe13c41 100644 --- a/lib/commands/stack/sync/util.js +++ b/lib/commands/stack/sync/util.js @@ -267,6 +267,19 @@ export function runComposerServer( localPath, args ) { return runProcess( 'composer', [ 'server', ...args ], { cwd: localPath } ); } +export async function importUploads( localPath ) { + const hasS3 = await new Promise( resolve => { + const proc = spawn( 'composer', [ 'server', 's3', '--help' ], { + cwd: localPath, + stdio: 'pipe', + } ); + proc.on( 'close', code => resolve( code === 0 ) ); + proc.on( 'error', () => resolve( false ) ); + } ); + + await runComposerServer( localPath, hasS3 ? [ 's3', 'import-uploads' ] : [ 'import-uploads' ] ); +} + export async function runPostSync( localPath ) { const registered = await new Promise( resolve => { const proc = spawn( 'composer', [ 'server', 'cli', '--', 'altis', 'post-sync', '--help' ], { From b284db03fc6de9970bde7cd49f0418dcb656538e Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Wed, 27 May 2026 21:36:33 +0800 Subject: [PATCH 13/22] Normalise siteId once at the top of the all handler Flattening comma-separated values inline at the guard meant the rest of the body still worked on the raw yargs array. Normalise to siteIds up front so all downstream code uses the same flattened value. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index 3f82839..01bf077 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -100,7 +100,9 @@ const handler = async function ( argv ) { console.log( chalk.dim( `Search-replace: ${ Object.keys( mappings ).length } mapping(s) from composer.json[${ searchReplaceKey }]` ) ); } - if ( siteId?.flatMap( s => String( s ).split( ',' ) ).length > 1 ) { + const siteIds = siteId?.flatMap( s => String( s ).split( ',' ) ) ?? []; + + if ( siteIds.length > 1 ) { console.error( chalk.red( 'Error: --site-id only accepts a single value for full sync (uploads can only target one path).' ) ); process.exit( 1 ); } @@ -110,7 +112,7 @@ const handler = async function ( argv ) { let logId = resume; let backup = null; - const isPartialSync = siteId?.length || table?.length; + const isPartialSync = siteIds.length || table?.length; if ( latest ) { if ( isPartialSync ) { @@ -159,22 +161,21 @@ const handler = async function ( argv ) { if ( ! backup && ! logId ) { const backupSpinner = ora( `Creating remote backup for ${ chalk.bold( app ) }…` ).start(); let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; - if ( siteId && siteId.length ) { - const siteIdList = siteId.flatMap( s => String( s ).split( ',' ) ); - if ( siteIdList.includes( '1' ) ) { + if ( siteIds.length ) { + if ( siteIds.includes( '1' ) ) { console.log( chalk.yellow( 'Note: site ID 1 is the main site — its tables have no numeric prefix, so the full DB will be synced.' ) ); } - const siteTables = await resolveTablesForSiteIds( v, app, siteIdList ); + const siteTables = await resolveTablesForSiteIds( v, app, siteIds ); tableList = [ ...new Set( [ ...tableList, ...siteTables ] ) ]; } // Site ID 1 is the main site — its uploads are at the root, not sites/1/ let resolvedUploadsPath = uploadsPath; - if ( siteId?.length ) { - if ( Number( siteId[0] ) === 1 ) { + if ( siteIds.length ) { + if ( Number( siteIds[0] ) === 1 ) { console.log( chalk.yellow( 'Note: site ID 1 is the main site — syncing all uploads.' ) ); resolvedUploadsPath = null; } else { - resolvedUploadsPath = `sites/${ siteId[0] }`; + resolvedUploadsPath = `sites/${ siteIds[0] }`; } } const opts = { From 2a89bebe65e3d0dc42d825444bc06ef1dacc370e Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Wed, 27 May 2026 21:38:47 +0800 Subject: [PATCH 14/22] Fix misnumbered step comments in database.js and all.js Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 14 +++++++------- lib/commands/stack/sync/database.js | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index 01bf077..907831e 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -211,11 +211,11 @@ const handler = async function ( argv ) { findSpinner.succeed( `Backup: ${ chalk.dim( backup.id ) }` ); } - // 6. Download archive + // 8. Download archive fs.mkdirSync( workDir, { recursive: true } ); await downloadArchive( backup.url, archivePath ); - // 7. Extract + // 9. Extract const extractSpinner = ora( 'Extracting archive…' ).start(); await extractArchive( archivePath, extractDir ); const sqlGzPath = path.join( extractDir, 'database.sql.gz' ); @@ -230,21 +230,21 @@ const handler = async function ( argv ) { } extractSpinner.succeed( 'Extracted.' ); - // 8. Search-replace + import database + // 10. Search-replace + import database console.log( chalk.bold( 'Importing database…' ) ); await searchReplaceAndImport( sqlGzPath, mappings, localPath ); - // 9. Cache flush + // 11. Cache flush console.log( chalk.dim( 'Flushing object cache…' ) ); await runComposerServer( localPath, [ 'cli', '--', 'cache', 'flush' ] ); - // 10. Post-sync hook + // 12. Post-sync hook if ( ! skipPostSync ) { console.log( chalk.dim( 'Running wp altis post-sync…' ) ); await runPostSync( localPath ); } - // 11. Copy uploads + import into S3 + // 13. Copy uploads + import into S3 console.log( chalk.bold( 'Syncing uploads…' ) ); const copySpinner = ora( 'Copying uploads to content/uploads…' ).start(); copyUploads( extractDir, localPath ); @@ -265,7 +265,7 @@ const handler = async function ( argv ) { process.exit( 1 ); } - // 12. Cleanup + // 14. Cleanup if ( ! keepArchive ) { try { fs.rmSync( archivePath, { force: true } ); diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js index 9693774..91c363a 100644 --- a/lib/commands/stack/sync/database.js +++ b/lib/commands/stack/sync/database.js @@ -204,14 +204,14 @@ const handler = async function ( argv ) { } extractSpinner.succeed( 'Extracted.' ); - // 8. Search-replace + import + // 10. Search-replace + import await searchReplaceAndImport( sqlGzPath, mappings, localPath ); - // 9. Cache flush + // 11. Cache flush console.log( chalk.dim( 'Flushing object cache…' ) ); await runComposerServer( localPath, [ 'cli', '--', 'cache', 'flush' ] ); - // 10. Post-sync hook + // 12. Post-sync hook if ( ! skipPostSync ) { console.log( chalk.dim( 'Running wp altis post-sync…' ) ); await runPostSync( localPath ); @@ -229,7 +229,7 @@ const handler = async function ( argv ) { process.exit( 1 ); } - // 11. Cleanup + // 13. Cleanup if ( ! keepArchive ) { try { fs.rmSync( archivePath, { force: true } ); From 784083f4dd93dd761c0d297f196ffd911328f691 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 29 May 2026 16:13:07 +0800 Subject: [PATCH 15/22] Remove step numbers from sync handler comments Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync/all.js | 28 ++++++++++++++-------------- lib/commands/stack/sync/database.js | 26 +++++++++++++------------- lib/commands/stack/sync/uploads.js | 20 ++++++++++---------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync/all.js index 907831e..c0f3b18 100644 --- a/lib/commands/stack/sync/all.js +++ b/lib/commands/stack/sync/all.js @@ -72,7 +72,7 @@ const handler = async function ( argv ) { return; } - // 1. Validate local project + // Validate local project const spinner = ora( 'Validating local project…' ).start(); try { validateLocalProject( localPath ); @@ -82,7 +82,7 @@ const handler = async function ( argv ) { process.exit( 1 ); } - // 2. Resolve search-replace mappings (fail fast before any remote calls) + // Resolve search-replace mappings (fail fast before any remote calls) let mappings; try { mappings = resolveMappings( localPath, searchReplaceKey, replacePairs, skipSearchReplace ); @@ -107,7 +107,7 @@ const handler = async function ( argv ) { process.exit( 1 ); } - // 3. Choose backup (before confirm, so the user knows what they're agreeing to) + // Choose backup (before confirm, so the user knows what they're agreeing to) const startTime = new Date(); let logId = resume; let backup = null; @@ -138,7 +138,7 @@ const handler = async function ( argv ) { console.log( chalk.yellow( 'Warning: --site-id/--table is ignored when using --resume (backup already created).' ) ); } - // 4. Confirm with full context (--latest implies --yes) + // Confirm with full context (--latest implies --yes) if ( ! yes && ! latest ) { let confirmMsg; if ( backup ) { @@ -157,7 +157,7 @@ const handler = async function ( argv ) { const extractDir = path.join( workDir, `${ app }-extracted` ); try { - // 5. Create backup if needed + // Create backup if needed if ( ! backup && ! logId ) { const backupSpinner = ora( `Creating remote backup for ${ chalk.bold( app ) }…` ).start(); let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; @@ -194,12 +194,12 @@ const handler = async function ( argv ) { } } - // 6. Wait for backup to complete (if creating new or resuming) + // Wait for backup to complete (if creating new or resuming) if ( ! backup ) { console.log( chalk.bold( 'Streaming backup progress…' ) ); await waitForBackup( v, app, logId, startTime, debug ); - // 7. Find completed backup + // Find completed backup const findSpinner = ora( 'Finding completed backup…' ).start(); backup = await findCompletedBackup( v, app, startTime ); if ( ! backup ) { @@ -211,11 +211,11 @@ const handler = async function ( argv ) { findSpinner.succeed( `Backup: ${ chalk.dim( backup.id ) }` ); } - // 8. Download archive + // Download archive fs.mkdirSync( workDir, { recursive: true } ); await downloadArchive( backup.url, archivePath ); - // 9. Extract + // Extract const extractSpinner = ora( 'Extracting archive…' ).start(); await extractArchive( archivePath, extractDir ); const sqlGzPath = path.join( extractDir, 'database.sql.gz' ); @@ -230,21 +230,21 @@ const handler = async function ( argv ) { } extractSpinner.succeed( 'Extracted.' ); - // 10. Search-replace + import database + // Search-replace + import database console.log( chalk.bold( 'Importing database…' ) ); await searchReplaceAndImport( sqlGzPath, mappings, localPath ); - // 11. Cache flush + // Cache flush console.log( chalk.dim( 'Flushing object cache…' ) ); await runComposerServer( localPath, [ 'cli', '--', 'cache', 'flush' ] ); - // 12. Post-sync hook + // Post-sync hook if ( ! skipPostSync ) { console.log( chalk.dim( 'Running wp altis post-sync…' ) ); await runPostSync( localPath ); } - // 13. Copy uploads + import into S3 + // Copy uploads + import into S3 console.log( chalk.bold( 'Syncing uploads…' ) ); const copySpinner = ora( 'Copying uploads to content/uploads…' ).start(); copyUploads( extractDir, localPath ); @@ -265,7 +265,7 @@ const handler = async function ( argv ) { process.exit( 1 ); } - // 14. Cleanup + // Cleanup if ( ! keepArchive ) { try { fs.rmSync( archivePath, { force: true } ); diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync/database.js index 91c363a..a8cc222 100644 --- a/lib/commands/stack/sync/database.js +++ b/lib/commands/stack/sync/database.js @@ -68,7 +68,7 @@ const handler = async function ( argv ) { return; } - // 1. Validate local project + // Validate local project const spinner = ora( 'Validating local project…' ).start(); try { validateLocalProject( localPath ); @@ -78,7 +78,7 @@ const handler = async function ( argv ) { process.exit( 1 ); } - // 2. Resolve search-replace mappings (fail fast before any remote calls) + // Resolve search-replace mappings (fail fast before any remote calls) let mappings; try { mappings = resolveMappings( localPath, searchReplaceKey, replacePairs, skipSearchReplace ); @@ -96,7 +96,7 @@ const handler = async function ( argv ) { console.log( chalk.dim( `Search-replace: ${ Object.keys( mappings ).length } mapping(s) from composer.json[${ searchReplaceKey }]` ) ); } - // 3. Choose backup (before confirm, so the user knows what they're agreeing to) + // Choose backup (before confirm, so the user knows what they're agreeing to) const startTime = new Date(); let logId = resume; let backup = null; @@ -127,7 +127,7 @@ const handler = async function ( argv ) { console.log( chalk.yellow( 'Warning: --site-id/--table is ignored when using --resume (backup already created).' ) ); } - // 4. Confirm with full context (--latest implies --yes) + // Confirm with full context (--latest implies --yes) if ( ! yes && ! latest ) { let confirmMsg; if ( backup ) { @@ -146,7 +146,7 @@ const handler = async function ( argv ) { const extractDir = path.join( workDir, `${ app }-extracted` ); try { - // 5. Create backup if needed + // Create backup if needed if ( ! backup && ! logId ) { const backupSpinner = ora( `Creating remote backup for ${ chalk.bold( app ) }…` ).start(); let tableList = table ? table.flatMap( t => String( t ).split( ',' ) ) : []; @@ -173,12 +173,12 @@ const handler = async function ( argv ) { } } - // 6. Wait for backup to complete (if creating new or resuming) + // Wait for backup to complete (if creating new or resuming) if ( ! backup ) { console.log( chalk.bold( 'Streaming backup progress…' ) ); await waitForBackup( v, app, logId, startTime, debug ); - // 7. Find completed backup + // Find completed backup const findSpinner = ora( 'Finding completed backup…' ).start(); backup = await findCompletedBackup( v, app, startTime ); if ( ! backup ) { @@ -190,11 +190,11 @@ const handler = async function ( argv ) { findSpinner.succeed( `Backup: ${ chalk.dim( backup.id ) }` ); } - // 8. Download archive + // Download archive fs.mkdirSync( workDir, { recursive: true } ); await downloadArchive( backup.url, archivePath ); - // 9. Extract + // Extract const extractSpinner = ora( 'Extracting archive…' ).start(); await extractArchive( archivePath, extractDir ); const sqlGzPath = path.join( extractDir, 'database.sql.gz' ); @@ -204,14 +204,14 @@ const handler = async function ( argv ) { } extractSpinner.succeed( 'Extracted.' ); - // 10. Search-replace + import + // Search-replace + import await searchReplaceAndImport( sqlGzPath, mappings, localPath ); - // 11. Cache flush + // Cache flush console.log( chalk.dim( 'Flushing object cache…' ) ); await runComposerServer( localPath, [ 'cli', '--', 'cache', 'flush' ] ); - // 12. Post-sync hook + // Post-sync hook if ( ! skipPostSync ) { console.log( chalk.dim( 'Running wp altis post-sync…' ) ); await runPostSync( localPath ); @@ -229,7 +229,7 @@ const handler = async function ( argv ) { process.exit( 1 ); } - // 13. Cleanup + // Cleanup if ( ! keepArchive ) { try { fs.rmSync( archivePath, { force: true } ); diff --git a/lib/commands/stack/sync/uploads.js b/lib/commands/stack/sync/uploads.js index 78bb23f..01125d4 100644 --- a/lib/commands/stack/sync/uploads.js +++ b/lib/commands/stack/sync/uploads.js @@ -57,7 +57,7 @@ const handler = async function ( argv ) { const localPath = pathOpt || process.cwd(); const v = new Vantage( config ); - // 1. Validate local project + // Validate local project const spinner = ora( 'Validating local project…' ).start(); try { validateLocalProject( localPath ); @@ -67,7 +67,7 @@ const handler = async function ( argv ) { process.exit( 1 ); } - // 2. Choose backup (before confirm, so the user knows what they're agreeing to) + // Choose backup (before confirm, so the user knows what they're agreeing to) const startTime = new Date(); let logId = resume; let backup = null; @@ -84,7 +84,7 @@ const handler = async function ( argv ) { backup = await promptBackupChoice( v, app ); } - // 3. Confirm with full context (--latest implies --yes) + // Confirm with full context (--latest implies --yes) if ( ! yes && ! latest ) { let confirmMsg; if ( backup ) { @@ -103,7 +103,7 @@ const handler = async function ( argv ) { const extractDir = path.join( workDir, `${ app }-extracted` ); try { - // 4. Create backup if needed + // Create backup if needed if ( ! backup && ! logId ) { const backupSpinner = ora( `Creating remote uploads backup for ${ chalk.bold( app ) }…` ).start(); const opts = { @@ -121,12 +121,12 @@ const handler = async function ( argv ) { } } - // 5. Wait for backup to complete (if creating new or resuming) + // Wait for backup to complete (if creating new or resuming) if ( ! backup ) { console.log( chalk.bold( 'Streaming backup progress…' ) ); await waitForBackup( v, app, logId, startTime, debug ); - // 6. Find completed backup + // Find completed backup const findSpinner = ora( 'Finding completed backup…' ).start(); backup = await findCompletedBackup( v, app, startTime ); if ( ! backup ) { @@ -138,11 +138,11 @@ const handler = async function ( argv ) { findSpinner.succeed( `Backup: ${ chalk.dim( backup.id ) }` ); } - // 6. Download archive + // Download archive fs.mkdirSync( workDir, { recursive: true } ); await downloadArchive( backup.url, archivePath ); - // 7. Extract and copy uploads + // Extract and copy uploads const extractSpinner = ora( 'Extracting archive…' ).start(); await extractArchive( archivePath, extractDir ); const uploadsDir = path.join( extractDir, 'uploads' ); @@ -156,7 +156,7 @@ const handler = async function ( argv ) { copyUploads( extractDir, localPath ); copySpinner.succeed( 'Uploads copied.' ); - // 8. Sync into local-server S3 + // Sync into local-server S3 console.log( chalk.dim( 'Syncing uploads to local S3…' ) ); await importUploads( localPath ); @@ -172,7 +172,7 @@ const handler = async function ( argv ) { process.exit( 1 ); } - // 9. Cleanup + // Cleanup if ( ! keepArchive ) { try { fs.rmSync( archivePath, { force: true } ); From c4d456f03a7c14266765bf1ef0e0b12374d05a1c Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 29 May 2026 16:16:58 +0800 Subject: [PATCH 16/22] Rename sync/ directory to sync-local/ to match the command name Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/{sync => sync-local}/all.js | 0 lib/commands/stack/{sync => sync-local}/database.js | 0 lib/commands/stack/{sync => sync-local}/index.js | 0 lib/commands/stack/{sync => sync-local}/uploads.js | 0 lib/commands/stack/{sync => sync-local}/util.js | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename lib/commands/stack/{sync => sync-local}/all.js (100%) rename lib/commands/stack/{sync => sync-local}/database.js (100%) rename lib/commands/stack/{sync => sync-local}/index.js (100%) rename lib/commands/stack/{sync => sync-local}/uploads.js (100%) rename lib/commands/stack/{sync => sync-local}/util.js (100%) diff --git a/lib/commands/stack/sync/all.js b/lib/commands/stack/sync-local/all.js similarity index 100% rename from lib/commands/stack/sync/all.js rename to lib/commands/stack/sync-local/all.js diff --git a/lib/commands/stack/sync/database.js b/lib/commands/stack/sync-local/database.js similarity index 100% rename from lib/commands/stack/sync/database.js rename to lib/commands/stack/sync-local/database.js diff --git a/lib/commands/stack/sync/index.js b/lib/commands/stack/sync-local/index.js similarity index 100% rename from lib/commands/stack/sync/index.js rename to lib/commands/stack/sync-local/index.js diff --git a/lib/commands/stack/sync/uploads.js b/lib/commands/stack/sync-local/uploads.js similarity index 100% rename from lib/commands/stack/sync/uploads.js rename to lib/commands/stack/sync-local/uploads.js diff --git a/lib/commands/stack/sync/util.js b/lib/commands/stack/sync-local/util.js similarity index 100% rename from lib/commands/stack/sync/util.js rename to lib/commands/stack/sync-local/util.js From d699d98d92566d91cb10625acc4969281d85f3c7 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 29 May 2026 16:18:48 +0800 Subject: [PATCH 17/22] Drop unimplemented --json option from sync-local commands Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync-local/util.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/commands/stack/sync-local/util.js b/lib/commands/stack/sync-local/util.js index fe13c41..2ffa806 100644 --- a/lib/commands/stack/sync-local/util.js +++ b/lib/commands/stack/sync-local/util.js @@ -365,11 +365,6 @@ export function addCommonOptions( cmd ) { type: 'boolean', default: false, } ); - cmd.option( 'json', { - description: 'Print machine-readable JSON summary.', - type: 'boolean', - default: false, - } ); } export function addDatabaseOptions( cmd ) { From b6d3f23abd370c58c41017ba22976aa54a29a219 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 29 May 2026 16:19:35 +0800 Subject: [PATCH 18/22] =?UTF-8?q?Drop=20new=20from=20progressStream=20call?= =?UTF-8?q?=20=E2=80=94=20it's=20a=20factory,=20not=20a=20class?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync-local/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands/stack/sync-local/util.js b/lib/commands/stack/sync-local/util.js index 2ffa806..8807cae 100644 --- a/lib/commands/stack/sync-local/util.js +++ b/lib/commands/stack/sync-local/util.js @@ -175,7 +175,7 @@ export async function downloadArchive( url, dest ) { } const size = parseInt( resp.headers.get( 'content-length' ) || '0', 10 ); - const progress = new progressStream( { length: size, time: 200 } ); + const progress = progressStream( { length: size, time: 200 } ); progress.on( 'progress', p => { spinner.text = `Downloading… ${ p.percentage.toFixed( 1 ) }% (${ bytes( p.speed ) }/s)`; } ); From 05f7f575b2357e9badd2160d7ac9a951d136d813 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 29 May 2026 16:20:14 +0800 Subject: [PATCH 19/22] Use wp cli has-command to probe for altis post-sync registration --help spawns composer and prints output unnecessarily; cli has-command is the canonical WP-CLI idiom and returns exit 0/non-zero cleanly. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync-local/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commands/stack/sync-local/util.js b/lib/commands/stack/sync-local/util.js index 8807cae..873e10a 100644 --- a/lib/commands/stack/sync-local/util.js +++ b/lib/commands/stack/sync-local/util.js @@ -282,7 +282,7 @@ export async function importUploads( localPath ) { export async function runPostSync( localPath ) { const registered = await new Promise( resolve => { - const proc = spawn( 'composer', [ 'server', 'cli', '--', 'altis', 'post-sync', '--help' ], { + const proc = spawn( 'composer', [ 'server', 'cli', '--', 'cli', 'has-command', 'altis post-sync' ], { cwd: localPath, stdio: 'pipe', } ); From 69360ea6de8a73df0afb574f2658ddb81d131ade Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 29 May 2026 16:21:13 +0800 Subject: [PATCH 20/22] Comment why downloadArchive uses plain fetch instead of Vantage client Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync-local/util.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/commands/stack/sync-local/util.js b/lib/commands/stack/sync-local/util.js index 873e10a..2768595 100644 --- a/lib/commands/stack/sync-local/util.js +++ b/lib/commands/stack/sync-local/util.js @@ -168,6 +168,7 @@ export function waitForBackup( v, stack, logId, startTime, debug ) { export async function downloadArchive( url, dest ) { const spinner = ora( 'Downloading backup…' ).start(); + // Backup URLs are pre-signed S3 URLs — plain fetch, no auth headers. const resp = await fetch( url ); if ( ! resp.ok ) { spinner.fail( 'Download failed.' ); From 3d8dc9ca6b3f371de90d02f63f2fdcb7937ce364 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 29 May 2026 16:22:13 +0800 Subject: [PATCH 21/22] Rename partial archive to .partial on failure to distinguish from complete downloads Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync-local/all.js | 10 +++++++--- lib/commands/stack/sync-local/database.js | 10 +++++++--- lib/commands/stack/sync-local/uploads.js | 10 +++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/commands/stack/sync-local/all.js b/lib/commands/stack/sync-local/all.js index c0f3b18..e4941a0 100644 --- a/lib/commands/stack/sync-local/all.js +++ b/lib/commands/stack/sync-local/all.js @@ -257,10 +257,14 @@ const handler = async function ( argv ) { } catch ( err ) { console.error( chalk.red( `\nSync failed: ${ err.message }` ) ); - if ( fs.existsSync( archivePath ) || fs.existsSync( extractDir ) ) { + if ( fs.existsSync( archivePath ) ) { + const partial = archivePath + '.partial'; + fs.renameSync( archivePath, partial ); console.log( chalk.dim( 'Kept files for debugging:' ) ); - if ( fs.existsSync( archivePath ) ) console.log( ` ${ archivePath }` ); - if ( fs.existsSync( extractDir ) ) console.log( ` ${ extractDir }` ); + console.log( ` ${ partial }` ); + } + if ( fs.existsSync( extractDir ) ) { + console.log( ` ${ extractDir }` ); } process.exit( 1 ); } diff --git a/lib/commands/stack/sync-local/database.js b/lib/commands/stack/sync-local/database.js index a8cc222..be4d6a2 100644 --- a/lib/commands/stack/sync-local/database.js +++ b/lib/commands/stack/sync-local/database.js @@ -221,10 +221,14 @@ const handler = async function ( argv ) { } catch ( err ) { console.error( chalk.red( `\nSync failed: ${ err.message }` ) ); - if ( fs.existsSync( archivePath ) || fs.existsSync( extractDir ) ) { + if ( fs.existsSync( archivePath ) ) { + const partial = archivePath + '.partial'; + fs.renameSync( archivePath, partial ); console.log( chalk.dim( `Kept files for debugging:` ) ); - if ( fs.existsSync( archivePath ) ) console.log( ` ${ archivePath }` ); - if ( fs.existsSync( extractDir ) ) console.log( ` ${ extractDir }` ); + console.log( ` ${ partial }` ); + } + if ( fs.existsSync( extractDir ) ) { + console.log( ` ${ extractDir }` ); } process.exit( 1 ); } diff --git a/lib/commands/stack/sync-local/uploads.js b/lib/commands/stack/sync-local/uploads.js index 01125d4..ef088ad 100644 --- a/lib/commands/stack/sync-local/uploads.js +++ b/lib/commands/stack/sync-local/uploads.js @@ -164,10 +164,14 @@ const handler = async function ( argv ) { } catch ( err ) { console.error( chalk.red( `\nSync failed: ${ err.message }` ) ); - if ( fs.existsSync( archivePath ) || fs.existsSync( extractDir ) ) { + if ( fs.existsSync( archivePath ) ) { + const partial = archivePath + '.partial'; + fs.renameSync( archivePath, partial ); console.log( chalk.dim( 'Kept files for debugging:' ) ); - if ( fs.existsSync( archivePath ) ) console.log( ` ${ archivePath }` ); - if ( fs.existsSync( extractDir ) ) console.log( ` ${ extractDir }` ); + console.log( ` ${ partial }` ); + } + if ( fs.existsSync( extractDir ) ) { + console.log( ` ${ extractDir }` ); } process.exit( 1 ); } From 4aea52a744ec0abedb08361e459a027224eb9a93 Mon Sep 17 00:00:00 2001 From: Jerico Aragon Date: Fri, 29 May 2026 16:25:39 +0800 Subject: [PATCH 22/22] Replace process.exit(0) in confirm() with a typed UserCancelled error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decouples the util from process lifetime — handlers now catch UserCancelled at the top of each command's main try block. Co-Authored-By: Claude Sonnet 4.6 --- lib/commands/stack/sync-local/all.js | 28 +++++++++++++---------- lib/commands/stack/sync-local/database.js | 28 +++++++++++++---------- lib/commands/stack/sync-local/uploads.js | 28 +++++++++++++---------- lib/commands/stack/sync-local/util.js | 5 ++-- 4 files changed, 51 insertions(+), 38 deletions(-) diff --git a/lib/commands/stack/sync-local/all.js b/lib/commands/stack/sync-local/all.js index e4941a0..a7be190 100644 --- a/lib/commands/stack/sync-local/all.js +++ b/lib/commands/stack/sync-local/all.js @@ -24,6 +24,7 @@ import { runPostSync, searchReplaceAndImport, startBackup, + UserCancelled, validateLocalProject, waitForBackup, } from './util.js'; @@ -138,18 +139,14 @@ const handler = async function ( argv ) { console.log( chalk.yellow( 'Warning: --site-id/--table is ignored when using --resume (backup already created).' ) ); } - // Confirm with full context (--latest implies --yes) - if ( ! yes && ! latest ) { - let confirmMsg; - if ( backup ) { - const age = formatAge( new Date( backup.date ) ); - confirmMsg = `Import backup ${ chalk.bold( backup.id ) } (${ chalk.dim( age + ' old' ) }) — replace local DB and merge uploads?`; - } else if ( logId ) { - confirmMsg = `Resume backup ${ chalk.bold( logId ) } and import DB + uploads into local project?`; - } else { - confirmMsg = `Create a new backup of ${ chalk.bold( app ) } and replace local DB and uploads?`; - } - await confirm( confirmMsg ); + let confirmMsg; + if ( backup ) { + const age = formatAge( new Date( backup.date ) ); + confirmMsg = `Import backup ${ chalk.bold( backup.id ) } (${ chalk.dim( age + ' old' ) }) — replace local DB and merge uploads?`; + } else if ( logId ) { + confirmMsg = `Resume backup ${ chalk.bold( logId ) } and import DB + uploads into local project?`; + } else { + confirmMsg = `Create a new backup of ${ chalk.bold( app ) } and replace local DB and uploads?`; } const workDir = outputDir || fs.mkdtempSync( path.join( tmpdir(), 'altis-sync-' ) ); @@ -157,6 +154,9 @@ const handler = async function ( argv ) { const extractDir = path.join( workDir, `${ app }-extracted` ); try { + if ( ! yes && ! latest ) { + await confirm( confirmMsg ); + } // Create backup if needed if ( ! backup && ! logId ) { const backupSpinner = ora( `Creating remote backup for ${ chalk.bold( app ) }…` ).start(); @@ -256,6 +256,10 @@ const handler = async function ( argv ) { console.log( chalk.bold.green( `\n✓ Database and uploads synced from ${ app }` ) ); } catch ( err ) { + if ( err instanceof UserCancelled ) { + console.log( chalk.yellow( 'Cancelled.' ) ); + process.exit( 0 ); + } console.error( chalk.red( `\nSync failed: ${ err.message }` ) ); if ( fs.existsSync( archivePath ) ) { const partial = archivePath + '.partial'; diff --git a/lib/commands/stack/sync-local/database.js b/lib/commands/stack/sync-local/database.js index be4d6a2..81a7513 100644 --- a/lib/commands/stack/sync-local/database.js +++ b/lib/commands/stack/sync-local/database.js @@ -21,6 +21,7 @@ import { runPostSync, searchReplaceAndImport, startBackup, + UserCancelled, validateLocalProject, waitForBackup, } from './util.js'; @@ -127,18 +128,14 @@ const handler = async function ( argv ) { console.log( chalk.yellow( 'Warning: --site-id/--table is ignored when using --resume (backup already created).' ) ); } - // Confirm with full context (--latest implies --yes) - if ( ! yes && ! latest ) { - let confirmMsg; - if ( backup ) { - const age = formatAge( new Date( backup.date ) ); - confirmMsg = `Import backup ${ chalk.bold( backup.id ) } (${ chalk.dim( age + ' old' ) }) and replace local DB? This cannot be undone.`; - } else if ( logId ) { - confirmMsg = `Resume backup ${ chalk.bold( logId ) } and import into local DB? This cannot be undone.`; - } else { - confirmMsg = `Create a new backup of ${ chalk.bold( app ) } and replace local DB? This cannot be undone.`; - } - await confirm( confirmMsg ); + let confirmMsg; + if ( backup ) { + const age = formatAge( new Date( backup.date ) ); + confirmMsg = `Import backup ${ chalk.bold( backup.id ) } (${ chalk.dim( age + ' old' ) }) and replace local DB? This cannot be undone.`; + } else if ( logId ) { + confirmMsg = `Resume backup ${ chalk.bold( logId ) } and import into local DB? This cannot be undone.`; + } else { + confirmMsg = `Create a new backup of ${ chalk.bold( app ) } and replace local DB? This cannot be undone.`; } const workDir = outputDir || fs.mkdtempSync( path.join( tmpdir(), 'altis-sync-' ) ); @@ -146,6 +143,9 @@ const handler = async function ( argv ) { const extractDir = path.join( workDir, `${ app }-extracted` ); try { + if ( ! yes && ! latest ) { + await confirm( confirmMsg ); + } // Create backup if needed if ( ! backup && ! logId ) { const backupSpinner = ora( `Creating remote backup for ${ chalk.bold( app ) }…` ).start(); @@ -220,6 +220,10 @@ const handler = async function ( argv ) { console.log( chalk.bold.green( `\n✓ Database synced from ${ app }` ) ); } catch ( err ) { + if ( err instanceof UserCancelled ) { + console.log( chalk.yellow( 'Cancelled.' ) ); + process.exit( 0 ); + } console.error( chalk.red( `\nSync failed: ${ err.message }` ) ); if ( fs.existsSync( archivePath ) ) { const partial = archivePath + '.partial'; diff --git a/lib/commands/stack/sync-local/uploads.js b/lib/commands/stack/sync-local/uploads.js index ef088ad..85eb2f0 100644 --- a/lib/commands/stack/sync-local/uploads.js +++ b/lib/commands/stack/sync-local/uploads.js @@ -19,6 +19,7 @@ import { promptBackupChoice, runComposerServer, startBackup, + UserCancelled, validateLocalProject, waitForBackup, } from './util.js'; @@ -84,18 +85,14 @@ const handler = async function ( argv ) { backup = await promptBackupChoice( v, app ); } - // Confirm with full context (--latest implies --yes) - if ( ! yes && ! latest ) { - let confirmMsg; - if ( backup ) { - const age = formatAge( new Date( backup.date ) ); - confirmMsg = `Merge uploads from backup ${ chalk.bold( backup.id ) } (${ chalk.dim( age + ' old' ) }) into ./content/uploads? Existing files may be overwritten.`; - } else if ( logId ) { - confirmMsg = `Resume backup ${ chalk.bold( logId ) } and sync uploads into ./content/uploads? Existing files may be overwritten.`; - } else { - confirmMsg = `Create a new uploads backup of ${ chalk.bold( app ) } and sync into ./content/uploads? Existing files may be overwritten.`; - } - await confirm( confirmMsg ); + let confirmMsg; + if ( backup ) { + const age = formatAge( new Date( backup.date ) ); + confirmMsg = `Merge uploads from backup ${ chalk.bold( backup.id ) } (${ chalk.dim( age + ' old' ) }) into ./content/uploads? Existing files may be overwritten.`; + } else if ( logId ) { + confirmMsg = `Resume backup ${ chalk.bold( logId ) } and sync uploads into ./content/uploads? Existing files may be overwritten.`; + } else { + confirmMsg = `Create a new uploads backup of ${ chalk.bold( app ) } and sync into ./content/uploads? Existing files may be overwritten.`; } const workDir = outputDir || fs.mkdtempSync( path.join( tmpdir(), 'altis-sync-' ) ); @@ -103,6 +100,9 @@ const handler = async function ( argv ) { const extractDir = path.join( workDir, `${ app }-extracted` ); try { + if ( ! yes && ! latest ) { + await confirm( confirmMsg ); + } // Create backup if needed if ( ! backup && ! logId ) { const backupSpinner = ora( `Creating remote uploads backup for ${ chalk.bold( app ) }…` ).start(); @@ -163,6 +163,10 @@ const handler = async function ( argv ) { console.log( chalk.bold.green( `\n✓ Uploads synced from ${ app }` ) ); } catch ( err ) { + if ( err instanceof UserCancelled ) { + console.log( chalk.yellow( 'Cancelled.' ) ); + process.exit( 0 ); + } console.error( chalk.red( `\nSync failed: ${ err.message }` ) ); if ( fs.existsSync( archivePath ) ) { const partial = archivePath + '.partial'; diff --git a/lib/commands/stack/sync-local/util.js b/lib/commands/stack/sync-local/util.js index 2768595..bb630a6 100644 --- a/lib/commands/stack/sync-local/util.js +++ b/lib/commands/stack/sync-local/util.js @@ -51,6 +51,8 @@ export function validateLocalProject( localPath ) { // --- Destructive action confirmation --- +export class UserCancelled extends Error {} + export async function confirm( message ) { const { ok } = await inquirer.prompt( { type: 'confirm', @@ -59,8 +61,7 @@ export async function confirm( message ) { default: false, } ); if ( ! ok ) { - console.log( chalk.yellow( 'Cancelled.' ) ); - process.exit( 0 ); + throw new UserCancelled( 'Cancelled.' ); } }