Angular service worker library for Progressive Web Apps with caching, push notifications, and update management
npx @tessl/cli install tessl/npm-angular--service-worker@19.2.0Angular's service worker library provides comprehensive PWA support including caching strategies, push notifications, and app update management. It enables offline-first applications with automatic resource caching and background sync capabilities.
npm install @angular/service-workerimport { ServiceWorkerModule, SwPush, SwUpdate } from '@angular/service-worker';For standalone/functional approach:
import { provideServiceWorker } from '@angular/service-worker';For low-level communication:
import { NgswCommChannel } from '@angular/service-worker';For configuration and generation:
import { Config, AssetGroup, DataGroup, Generator } from '@angular/service-worker/config';For testing:
import {
MockServiceWorkerContainer,
MockServiceWorker,
patchDecodeBase64
} from '@angular/service-worker/testing';import { NgModule } from '@angular/core';
import { ServiceWorkerModule } from '@angular/service-worker';
@NgModule({
imports: [
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: environment.production,
registrationStrategy: 'registerWhenStable:30000'
})
]
})
export class AppModule {}import { bootstrapApplication } from '@angular/platform-browser';
import { provideServiceWorker } from '@angular/service-worker';
bootstrapApplication(AppComponent, {
providers: [
provideServiceWorker('ngsw-worker.js', {
enabled: environment.production,
registrationStrategy: 'registerWhenStable:30000'
})
]
});import { SwPush } from '@angular/service-worker';
constructor(private swPush: SwPush) {}
subscribeToNotifications() {
if (this.swPush.isEnabled) {
this.swPush.requestSubscription({
serverPublicKey: 'YOUR_VAPID_PUBLIC_KEY'
})
.then(sub => console.log('Subscription object:', sub))
.catch(err => console.error('Could not subscribe', err));
}
}import { SwUpdate } from '@angular/service-worker';
constructor(private swUpdate: SwUpdate) {}
checkForUpdates() {
if (this.swUpdate.isEnabled) {
this.swUpdate.versionUpdates.subscribe(event => {
switch (event.type) {
case 'VERSION_READY':
if (confirm('New version available. Load now?')) {
document.location.reload();
}
break;
case 'VERSION_DETECTED':
console.log('Downloading new app version...');
break;
}
});
}
}Angular Service Worker is built around several key components:
Angular module and standalone provider setup for service worker integration with configurable registration strategies.
class ServiceWorkerModule {
static register(
script: string,
options?: SwRegistrationOptions
): ModuleWithProviders<ServiceWorkerModule>;
}
function provideServiceWorker(
script: string,
options?: SwRegistrationOptions
): EnvironmentProviders;
abstract class SwRegistrationOptions {
enabled?: boolean;
scope?: string;
registrationStrategy?: string | (() => Observable<unknown>);
}Complete push notification system with subscription management, message handling, and notification click events.
class SwPush {
readonly messages: Observable<object>;
readonly notificationClicks: Observable<{
action: string;
notification: NotificationOptions & { title: string };
}>;
readonly subscription: Observable<PushSubscription | null>;
get isEnabled(): boolean;
requestSubscription(options: {serverPublicKey: string}): Promise<PushSubscription>;
unsubscribe(): Promise<void>;
}Service for detecting, downloading, and activating app updates with comprehensive version event handling.
class SwUpdate {
readonly versionUpdates: Observable<VersionEvent>;
readonly unrecoverable: Observable<UnrecoverableStateEvent>;
get isEnabled(): boolean;
checkForUpdate(): Promise<boolean>;
activateUpdate(): Promise<boolean>;
}Direct service worker communication channel for advanced message passing and event handling.
class NgswCommChannel {
readonly worker: Observable<ServiceWorker>;
readonly registration: Observable<ServiceWorkerRegistration>;
readonly events: Observable<TypedEvent>;
get isEnabled(): boolean;
postMessage(action: string, payload: Object): Promise<void>;
postMessageWithOperation(type: string, payload: Object, operationNonce: number): Promise<boolean>;
generateNonce(): number;
eventsOfType<T extends TypedEvent>(type: T['type'] | T['type'][]): Observable<T>;
nextEventOfType<T extends TypedEvent>(type: T['type']): Observable<T>;
waitForOperationCompleted(nonce: number): Promise<boolean>;
}Configuration types and utilities for service worker behavior and caching strategies.
interface Config {
appData?: {};
index: string;
assetGroups?: AssetGroup[];
dataGroups?: DataGroup[];
navigationUrls?: string[];
navigationRequestStrategy?: 'freshness' | 'performance';
applicationMaxAge?: Duration;
}
interface AssetGroup {
name: string;
installMode?: 'prefetch' | 'lazy';
updateMode?: 'prefetch' | 'lazy';
resources: {files?: Glob[]; urls?: Glob[]};
cacheQueryOptions?: Pick<CacheQueryOptions, 'ignoreSearch'>;
}
interface DataGroup {
name: string;
urls: Glob[];
version?: number;
cacheConfig: {
maxSize: number;
maxAge: Duration;
timeout?: Duration;
refreshAhead?: Duration;
strategy?: 'freshness' | 'performance';
cacheOpaqueResponses?: boolean;
};
cacheQueryOptions?: Pick<CacheQueryOptions, 'ignoreSearch'>;
}Mock implementations for unit testing service worker functionality in Angular applications.
class MockServiceWorkerContainer {
controller: MockServiceWorker | null;
messages: Subject<any>;
notificationClicks: Subject<{}>;
addEventListener(event: 'controllerchange' | 'message', handler: Function): void;
removeEventListener(event: 'controllerchange', handler: Function): void;
register(url: string): Promise<void>;
getRegistration(): Promise<ServiceWorkerRegistration>;
setupSw(url?: string): void;
sendMessage(value: Object): void;
}Command-line tools and programmatic API for service worker configuration and manifest generation.
class Generator {
constructor(fs: Filesystem, baseHref: string);
process(config: Config): Promise<any>;
}
interface Filesystem {
list(dir: string, pattern: string): Promise<string[]>;
read(path: string): Promise<string>;
hash(path: string): Promise<string>;
write(path: string, contents: string): Promise<void>;
}interface VersionDetectedEvent {
type: 'VERSION_DETECTED';
version: {hash: string; appData?: object};
}
interface VersionReadyEvent {
type: 'VERSION_READY';
currentVersion: {hash: string; appData?: object};
latestVersion: {hash: string; appData?: object};
}
interface VersionInstallationFailedEvent {
type: 'VERSION_INSTALLATION_FAILED';
version: {hash: string; appData?: object};
error: string;
}
interface NoNewVersionDetectedEvent {
type: 'NO_NEW_VERSION_DETECTED';
version: {hash: string; appData?: Object};
}
type VersionEvent =
| VersionDetectedEvent
| VersionInstallationFailedEvent
| VersionReadyEvent
| NoNewVersionDetectedEvent;interface UnrecoverableStateEvent {
type: 'UNRECOVERABLE_STATE';
reason: string;
}interface TypedEvent {
type: string;
}
interface PushEvent {
type: 'PUSH';
data: any;
}
type IncomingEvent = UnrecoverableStateEvent | VersionEvent;type Glob = string;
type Duration = string;enum RuntimeErrorCode {
UNKNOWN_REGISTRATION_STRATEGY = 5600,
SERVICE_WORKER_DISABLED_OR_NOT_SUPPORTED_BY_THIS_BROWSER = 5601,
NOT_SUBSCRIBED_TO_PUSH_NOTIFICATIONS = 5602,
PUSH_SUBSCRIPTION_UNSUBSCRIBE_FAILED = 5603,
SERVICE_WORKER_REGISTRATION_FAILED = 5604
}