Interview question
Build a safe Angular service-worker update prompt
Coordinates version-ready events, unsaved-work checks, deferred reload, failure reporting, and one prompt per version without unsafe in-place activation.
TL;DR
Coordinates version-ready events, unsaved-work checks, deferred reload, failure reporting, and one prompt per version without unsafe in-place activation.
Angular service worker, SwUpdate, versionUpdates, reload UX, unsaved work, duplicate-event suppression, errors, and lifecycle ownership.
Practice the problem like a real interview: restate, reason, implement, and test.
Implement an application-scoped update coordinator. When VERSION_READY arrives, show one prompt for that version. Reload only after the user accepts and no unsaved work remains; otherwise remember that an update is waiting. Report installation failures without exposing sensitive data. Do not call activateUpdate in the live tab.
version v2 ready, no dirty work, user accepts -> document reload
version v2 ready, editor dirty -> update waits
editor becomes clean, user accepts -> document reload
duplicate v2 event -> no second prompt
import {Injectable, inject, signal} from '@angular/core';
import {SwUpdate, VersionReadyEvent} from '@angular/service-worker';
@Injectable({providedIn: 'root'})
export class UpdateCoordinator {
private readonly updates = inject(SwUpdate);
private readonly dirty = inject(UnsavedWorkRegistry);
private readonly reporter = inject(UpdateReporter);
readonly pendingVersion = signal<string | null>(null);
private promptedVersion: string | null = null;
private started = false;
start(): void {
if (this.started || !this.updates.isEnabled) return;
this.started = true;
this.updates.versionUpdates.subscribe(event => {
if (event.type === 'VERSION_READY') this.onReady(event);
if (event.type === 'VERSION_INSTALLATION_FAILED') {
this.reporter.failed(event.version.hash);
}
});
}
private onReady(event: VersionReadyEvent): void {
const version = event.latestVersion.hash;
this.pendingVersion.set(version);
if (this.promptedVersion === version || this.dirty.hasUnsavedWork()) return;
this.promptedVersion = version;
}
reloadAccepted(): void {
if (!this.pendingVersion() || this.dirty.hasUnsavedWork()) return;
document.location.reload();
}
}
Each update event is handled in O(1) time and the coordinator retains only one pending and one prompted version identifier.
activateUpdate can mix an old runtime with new lazy chunks.Move to the linked follow-up, next path step, prerequisite, or deeper variant.
Practice the next layer of the same subject.