Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 93 additions & 19 deletions packages/common/src/packageActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,18 +253,45 @@ export async function applyPackageChanges(
return false;
}

let previewAllNotification: string | undefined;

try {
if (!skipConfirmation) {
const previewJobs: IPreviewJob[] = [];

// Emit notification about the preview jobs
previewAllNotification = Notification.emit(
'Previewing package changes',
'in-progress',
{
autoClose: false
}
);

const removePreview = await pkgModel.dry_run_preview(
toRemove,
'remove',
theEnvironment
);
const updatePreview = await pkgModel.dry_run_preview(
toUpdate,
'update',
theEnvironment
);
const installPreview = await pkgModel.dry_run_preview(
toInstall,
'install',
theEnvironment
);
Comment on lines +271 to +285

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three dry_run_preview calls are awaited sequentially, which increases latency compared to the previous behavior where the dialog loaded previews concurrently via Promise.all. Consider running these previews in parallel (e.g., await Promise.all([...])) and then using the results for has_side_effects checks / job data.

Suggested change
const removePreview = await pkgModel.dry_run_preview(
toRemove,
'remove',
theEnvironment
);
const updatePreview = await pkgModel.dry_run_preview(
toUpdate,
'update',
theEnvironment
);
const installPreview = await pkgModel.dry_run_preview(
toInstall,
'install',
theEnvironment
);
const [removePreview, updatePreview, installPreview] = await Promise.all([
pkgModel.dry_run_preview(toRemove, 'remove', theEnvironment),
pkgModel.dry_run_preview(toUpdate, 'update', theEnvironment),
pkgModel.dry_run_preview(toInstall, 'install', theEnvironment)
]);

Copilot uses AI. Check for mistakes.

if (toRemove.length > 0) {
previewJobs.push({
section: {
id: 'remove',
title: 'Remove packages',
requestedPackages: toRemove.map(specBaseName)
},
promise: pkgModel.dry_run_preview(toRemove, 'remove', theEnvironment)
promise: Promise.resolve(removePreview)
});
}
if (toUpdate.length > 0) {
Expand All @@ -274,7 +301,7 @@ export async function applyPackageChanges(
title: 'Update packages',
requestedPackages: toUpdate.map(specBaseName)
},
promise: pkgModel.dry_run_preview(toUpdate, 'update', theEnvironment)
promise: Promise.resolve(updatePreview)
});
}
if (toInstall.length > 0) {
Expand All @@ -284,22 +311,37 @@ export async function applyPackageChanges(
title: 'Install packages',
requestedPackages: toInstall.map(specBaseName)
},
promise: pkgModel.dry_run_preview(
toInstall,
'install',
theEnvironment
)
promise: Promise.resolve(installPreview)
});
}

const confirmed = await openPackagePreviewDialog({
title: 'Preview package changes',
jobs: previewJobs,
acceptLabel: 'Apply'
});
if (
!removePreview.has_side_effects &&
!updatePreview.has_side_effects &&
!installPreview.has_side_effects
) {
Notification.update({
id: previewAllNotification,
message: 'No additional changes needed, applying changes...',
type: 'success',
autoClose: 2000
});
}

if (!confirmed) {
return false;
if (
removePreview.has_side_effects ||
updatePreview.has_side_effects ||
installPreview.has_side_effects
) {
const confirmed = await openPackagePreviewDialog({
title: 'Preview package changes',
jobs: previewJobs,
acceptLabel: 'Apply'
});

if (!confirmed) {
Comment thread
RRosio marked this conversation as resolved.
return false;
}
}
}

Expand All @@ -313,6 +355,9 @@ export async function applyPackageChanges(
}
});

if (previewAllNotification) {
Notification.dismiss(previewAllNotification);
}
toastId = Notification.emit('Starting packages actions', 'in-progress');

if (toRemove.length > 0) {
Expand Down Expand Up @@ -361,17 +406,43 @@ export async function applyPackageChanges(

return true;
} catch (error) {
console.error('Error when applying package changes: ', error);
const fullError = formatPreviewErrorForDialog(error);
const firstNewLine = fullError.indexOf('\n');
const firstLine =
firstNewLine === -1 ? fullError : fullError.slice(0, firstNewLine);

if (error !== 'cancelled') {
console.error(error);
if (toastId) {
if (previewAllNotification) {
Notification.dismiss(previewAllNotification);
}
Notification.update({
id: toastId,
message: (error as any).message,
message: firstLine,
type: 'error',
autoClose: 0
autoClose: false,
actions: [
{
label: 'Show details',
callback: () => {
openPreviewErrorDialog(fullError);
}
}
]
});
} else {
Notification.error((error as any).message);
Notification.emit(firstLine, 'error', {
autoClose: false,
actions: [
{
Comment on lines 437 to +441

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the error path where toastId is still empty, the code emits an error toast but does not dismiss previewAllNotification, which can leave the in-progress preview toast lingering alongside the error. Dismiss previewAllNotification in this branch as well (ideally via a shared cleanup/finally).

Copilot uses AI. Check for mistakes.
label: 'Show details',
callback: () => {
openPreviewErrorDialog(fullError);
}
}
]
});
}

// Emit failed signal
Expand All @@ -381,13 +452,16 @@ export async function applyPackageChanges(
status: 'failed',
details: {
packagesAffected: selectedPackages.length,
error: (error as any).message
error: firstLine
}
});
} else {
if (toastId) {
Notification.dismiss(toastId);
}
if (previewAllNotification) {
Notification.dismiss(previewAllNotification);
}
}

return false;
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15478,21 +15478,21 @@ __metadata:

"typescript@patch:typescript@>=3 < 6#~builtin<compat/typescript>":
version: 5.9.3
resolution: "typescript@patch:typescript@npm%3A5.9.3#~builtin<compat/typescript>::version=5.9.3&hash=5786d5"
resolution: "typescript@patch:typescript@npm%3A5.9.3#~builtin<compat/typescript>::version=5.9.3&hash=85af82"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: a5a6dc399d3761ded54192031f11d3ad5df8001c7febe3fbbc8098efcb552cdf8f2f402b3618c56dafcd04fef63dee005f4900f608e185404caedc46480539ed
checksum: 8bb8d86819ac86a498eada254cad7fb69c5f74778506c700c2a712daeaff21d3a6f51fd0d534fe16903cb010d1b74f89437a3d02d4d0ff5ca2ba9a4660de8497
languageName: node
linkType: hard

"typescript@patch:typescript@~5.0.2#~builtin<compat/typescript>":
version: 5.0.4
resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin<compat/typescript>::version=5.0.4&hash=b5f058"
resolution: "typescript@patch:typescript@npm%3A5.0.4#~builtin<compat/typescript>::version=5.0.4&hash=85af82"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: d26b6ba97b6d163c55dbdffd9bbb4c211667ebebc743accfeb2c8c0154aace7afd097b51165a72a5bad2cf65a4612259344ff60f8e642362aa1695c760d303ac
checksum: bb309d320c59a26565fb3793dba550576ab861018ff3fd1b7fccabbe46ae4a35546bc45f342c0a0b6f265c801ccdf64ffd68f548f117ceb7f0eac4b805cd52a9
languageName: node
linkType: hard

Expand Down
Loading