@angular/service-worker 21.0.0-next.8 → 21.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v21.0.0-next.8
2
+ * @license Angular v21.0.0-rc.0
3
3
  * (c) 2010-2025 Google LLC. https://angular.dev/
4
4
  * License: MIT
5
5
  */
@@ -10,682 +10,425 @@ import { Observable, Subject, NEVER } from 'rxjs';
10
10
  import { switchMap, take, filter, map } from 'rxjs/operators';
11
11
 
12
12
  const ERR_SW_NOT_SUPPORTED = 'Service workers are disabled or not supported by this browser';
13
- /**
14
- * @publicApi
15
- */
16
13
  class NgswCommChannel {
17
- serviceWorker;
18
- worker;
19
- registration;
20
- events;
21
- constructor(serviceWorker, injector) {
22
- this.serviceWorker = serviceWorker;
23
- if (!serviceWorker) {
24
- this.worker =
25
- this.events =
26
- this.registration =
27
- new Observable((subscriber) => subscriber.error(new _RuntimeError(5601 /* RuntimeErrorCode.SERVICE_WORKER_DISABLED_OR_NOT_SUPPORTED_BY_THIS_BROWSER */, (typeof ngDevMode === 'undefined' || ngDevMode) && ERR_SW_NOT_SUPPORTED)));
14
+ serviceWorker;
15
+ worker;
16
+ registration;
17
+ events;
18
+ constructor(serviceWorker, injector) {
19
+ this.serviceWorker = serviceWorker;
20
+ if (!serviceWorker) {
21
+ this.worker = this.events = this.registration = new Observable(subscriber => subscriber.error(new _RuntimeError(5601, (typeof ngDevMode === 'undefined' || ngDevMode) && ERR_SW_NOT_SUPPORTED)));
22
+ } else {
23
+ let currentWorker = null;
24
+ const workerSubject = new Subject();
25
+ this.worker = new Observable(subscriber => {
26
+ if (currentWorker !== null) {
27
+ subscriber.next(currentWorker);
28
28
  }
29
- else {
30
- let currentWorker = null;
31
- const workerSubject = new Subject();
32
- this.worker = new Observable((subscriber) => {
33
- if (currentWorker !== null) {
34
- subscriber.next(currentWorker);
35
- }
36
- return workerSubject.subscribe((v) => subscriber.next(v));
37
- });
38
- const updateController = () => {
39
- const { controller } = serviceWorker;
40
- if (controller === null) {
41
- return;
42
- }
43
- currentWorker = controller;
44
- workerSubject.next(currentWorker);
45
- };
46
- serviceWorker.addEventListener('controllerchange', updateController);
47
- updateController();
48
- this.registration = this.worker.pipe(switchMap(() => serviceWorker.getRegistration().then((registration) => {
49
- // The `getRegistration()` method may return undefined in
50
- // non-secure contexts or incognito mode, where service worker
51
- // registration might not be allowed.
52
- if (!registration) {
53
- throw new _RuntimeError(5601 /* RuntimeErrorCode.SERVICE_WORKER_DISABLED_OR_NOT_SUPPORTED_BY_THIS_BROWSER */, (typeof ngDevMode === 'undefined' || ngDevMode) && ERR_SW_NOT_SUPPORTED);
54
- }
55
- return registration;
56
- })));
57
- const _events = new Subject();
58
- this.events = _events.asObservable();
59
- const messageListener = (event) => {
60
- const { data } = event;
61
- if (data?.type) {
62
- _events.next(data);
63
- }
64
- };
65
- serviceWorker.addEventListener('message', messageListener);
66
- // The injector is optional to avoid breaking changes.
67
- const appRef = injector?.get(ApplicationRef, null, { optional: true });
68
- appRef?.onDestroy(() => {
69
- serviceWorker.removeEventListener('controllerchange', updateController);
70
- serviceWorker.removeEventListener('message', messageListener);
71
- });
29
+ return workerSubject.subscribe(v => subscriber.next(v));
30
+ });
31
+ const updateController = () => {
32
+ const {
33
+ controller
34
+ } = serviceWorker;
35
+ if (controller === null) {
36
+ return;
72
37
  }
73
- }
74
- postMessage(action, payload) {
75
- return new Promise((resolve) => {
76
- this.worker.pipe(take(1)).subscribe((sw) => {
77
- sw.postMessage({
78
- action,
79
- ...payload,
80
- });
81
- resolve();
82
- });
83
- });
84
- }
85
- postMessageWithOperation(type, payload, operationNonce) {
86
- const waitForOperationCompleted = this.waitForOperationCompleted(operationNonce);
87
- const postMessage = this.postMessage(type, payload);
88
- return Promise.all([postMessage, waitForOperationCompleted]).then(([, result]) => result);
89
- }
90
- generateNonce() {
91
- return Math.round(Math.random() * 10000000);
92
- }
93
- eventsOfType(type) {
94
- let filterFn;
95
- if (typeof type === 'string') {
96
- filterFn = (event) => event.type === type;
38
+ currentWorker = controller;
39
+ workerSubject.next(currentWorker);
40
+ };
41
+ serviceWorker.addEventListener('controllerchange', updateController);
42
+ updateController();
43
+ this.registration = this.worker.pipe(switchMap(() => serviceWorker.getRegistration().then(registration => {
44
+ if (!registration) {
45
+ throw new _RuntimeError(5601, (typeof ngDevMode === 'undefined' || ngDevMode) && ERR_SW_NOT_SUPPORTED);
97
46
  }
98
- else {
99
- filterFn = (event) => type.includes(event.type);
47
+ return registration;
48
+ })));
49
+ const _events = new Subject();
50
+ this.events = _events.asObservable();
51
+ const messageListener = event => {
52
+ const {
53
+ data
54
+ } = event;
55
+ if (data?.type) {
56
+ _events.next(data);
100
57
  }
101
- return this.events.pipe(filter(filterFn));
58
+ };
59
+ serviceWorker.addEventListener('message', messageListener);
60
+ const appRef = injector?.get(ApplicationRef, null, {
61
+ optional: true
62
+ });
63
+ appRef?.onDestroy(() => {
64
+ serviceWorker.removeEventListener('controllerchange', updateController);
65
+ serviceWorker.removeEventListener('message', messageListener);
66
+ });
102
67
  }
103
- nextEventOfType(type) {
104
- return this.eventsOfType(type).pipe(take(1));
105
- }
106
- waitForOperationCompleted(nonce) {
107
- return new Promise((resolve, reject) => {
108
- this.eventsOfType('OPERATION_COMPLETED')
109
- .pipe(filter((event) => event.nonce === nonce), take(1), map((event) => {
110
- if (event.result !== undefined) {
111
- return event.result;
112
- }
113
- throw new Error(event.error);
114
- }))
115
- .subscribe({
116
- next: resolve,
117
- error: reject,
118
- });
68
+ }
69
+ postMessage(action, payload) {
70
+ return new Promise(resolve => {
71
+ this.worker.pipe(take(1)).subscribe(sw => {
72
+ sw.postMessage({
73
+ action,
74
+ ...payload
119
75
  });
76
+ resolve();
77
+ });
78
+ });
79
+ }
80
+ postMessageWithOperation(type, payload, operationNonce) {
81
+ const waitForOperationCompleted = this.waitForOperationCompleted(operationNonce);
82
+ const postMessage = this.postMessage(type, payload);
83
+ return Promise.all([postMessage, waitForOperationCompleted]).then(([, result]) => result);
84
+ }
85
+ generateNonce() {
86
+ return Math.round(Math.random() * 10000000);
87
+ }
88
+ eventsOfType(type) {
89
+ let filterFn;
90
+ if (typeof type === 'string') {
91
+ filterFn = event => event.type === type;
92
+ } else {
93
+ filterFn = event => type.includes(event.type);
120
94
  }
121
- get isEnabled() {
122
- return !!this.serviceWorker;
123
- }
95
+ return this.events.pipe(filter(filterFn));
96
+ }
97
+ nextEventOfType(type) {
98
+ return this.eventsOfType(type).pipe(take(1));
99
+ }
100
+ waitForOperationCompleted(nonce) {
101
+ return new Promise((resolve, reject) => {
102
+ this.eventsOfType('OPERATION_COMPLETED').pipe(filter(event => event.nonce === nonce), take(1), map(event => {
103
+ if (event.result !== undefined) {
104
+ return event.result;
105
+ }
106
+ throw new Error(event.error);
107
+ })).subscribe({
108
+ next: resolve,
109
+ error: reject
110
+ });
111
+ });
112
+ }
113
+ get isEnabled() {
114
+ return !!this.serviceWorker;
115
+ }
124
116
  }
125
117
 
126
- /**
127
- * Subscribe and listen to
128
- * [Web Push
129
- * Notifications](https://developer.mozilla.org/en-US/docs/Web/API/Push_API/Best_Practices) through
130
- * Angular Service Worker.
131
- *
132
- * @usageNotes
133
- *
134
- * You can inject a `SwPush` instance into any component or service
135
- * as a dependency.
136
- *
137
- * <code-example path="service-worker/push/module.ts" region="inject-sw-push"
138
- * header="app.component.ts"></code-example>
139
- *
140
- * To subscribe, call `SwPush.requestSubscription()`, which asks the user for permission.
141
- * The call returns a `Promise` with a new
142
- * [`PushSubscription`](https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription)
143
- * instance.
144
- *
145
- * <code-example path="service-worker/push/module.ts" region="subscribe-to-push"
146
- * header="app.component.ts"></code-example>
147
- *
148
- * A request is rejected if the user denies permission, or if the browser
149
- * blocks or does not support the Push API or ServiceWorkers.
150
- * Check `SwPush.isEnabled` to confirm status.
151
- *
152
- * Invoke Push Notifications by pushing a message with the following payload.
153
- *
154
- * ```ts
155
- * {
156
- * "notification": {
157
- * "actions": NotificationAction[],
158
- * "badge": USVString,
159
- * "body": DOMString,
160
- * "data": any,
161
- * "dir": "auto"|"ltr"|"rtl",
162
- * "icon": USVString,
163
- * "image": USVString,
164
- * "lang": DOMString,
165
- * "renotify": boolean,
166
- * "requireInteraction": boolean,
167
- * "silent": boolean,
168
- * "tag": DOMString,
169
- * "timestamp": DOMTimeStamp,
170
- * "title": DOMString,
171
- * "vibrate": number[]
172
- * }
173
- * }
174
- * ```
175
- *
176
- * Only `title` is required. See `Notification`
177
- * [instance
178
- * properties](https://developer.mozilla.org/en-US/docs/Web/API/Notification#Instance_properties).
179
- *
180
- * While the subscription is active, Service Worker listens for
181
- * [PushEvent](https://developer.mozilla.org/en-US/docs/Web/API/PushEvent)
182
- * occurrences and creates
183
- * [Notification](https://developer.mozilla.org/en-US/docs/Web/API/Notification)
184
- * instances in response.
185
- *
186
- * Unsubscribe using `SwPush.unsubscribe()`.
187
- *
188
- * An application can subscribe to `SwPush.notificationClicks` observable to be notified when a user
189
- * clicks on a notification. For example:
190
- *
191
- * <code-example path="service-worker/push/module.ts" region="subscribe-to-notification-clicks"
192
- * header="app.component.ts"></code-example>
193
- *
194
- * You can read more on handling notification clicks in the [Service worker notifications
195
- * guide](ecosystem/service-workers/push-notifications).
196
- *
197
- * @see [Push Notifications](https://developers.google.com/web/fundamentals/codelabs/push-notifications/)
198
- * @see [Angular Push Notifications](https://blog.angular-university.io/angular-push-notifications/)
199
- * @see [MDN: Push API](https://developer.mozilla.org/en-US/docs/Web/API/Push_API)
200
- * @see [MDN: Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API)
201
- * @see [MDN: Web Push API Notifications best practices](https://developer.mozilla.org/en-US/docs/Web/API/Push_API/Best_Practices)
202
- *
203
- * @publicApi
204
- */
205
118
  class SwPush {
206
- sw;
207
- /**
208
- * Emits the payloads of the received push notification messages.
209
- */
210
- messages;
211
- /**
212
- * Emits the payloads of the received push notification messages as well as the action the user
213
- * interacted with. If no action was used the `action` property contains an empty string `''`.
214
- *
215
- * Note that the `notification` property does **not** contain a
216
- * [Notification][Mozilla Notification] object but rather a
217
- * [NotificationOptions](https://notifications.spec.whatwg.org/#dictdef-notificationoptions)
218
- * object that also includes the `title` of the [Notification][Mozilla Notification] object.
219
- *
220
- * [Mozilla Notification]: https://developer.mozilla.org/en-US/docs/Web/API/Notification
221
- */
222
- notificationClicks;
223
- /**
224
- * Emits the payloads of notifications that were closed, along with the action (if any)
225
- * associated with the close event. If no action was used, the `action` property contains
226
- * an empty string `''`.
227
- *
228
- * Note that the `notification` property does **not** contain a
229
- * [Notification][Mozilla Notification] object but rather a
230
- * [NotificationOptions](https://notifications.spec.whatwg.org/#dictdef-notificationoptions)
231
- * object that also includes the `title` of the [Notification][Mozilla Notification] object.
232
- *
233
- * [Mozilla Notification]: https://developer.mozilla.org/en-US/docs/Web/API/Notification
234
- */
235
- notificationCloses;
236
- /**
237
- * Emits updates to the push subscription, including both the previous (`oldSubscription`)
238
- * and current (`newSubscription`) values. Either subscription may be `null`, depending on
239
- * the context:
240
- *
241
- * - `oldSubscription` is `null` if no previous subscription existed.
242
- * - `newSubscription` is `null` if the subscription was invalidated and not replaced.
243
- *
244
- * This stream allows clients to react to automatic changes in push subscriptions,
245
- * such as those triggered by browser expiration or key rotation.
246
- *
247
- * [Push API]: https://w3c.github.io/push-api
248
- */
249
- pushSubscriptionChanges;
250
- /**
251
- * Emits the currently active
252
- * [PushSubscription](https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription)
253
- * associated to the Service Worker registration or `null` if there is no subscription.
254
- */
255
- subscription;
256
- /**
257
- * True if the Service Worker is enabled (supported by the browser and enabled via
258
- * `ServiceWorkerModule`).
259
- */
260
- get isEnabled() {
261
- return this.sw.isEnabled;
119
+ sw;
120
+ messages;
121
+ notificationClicks;
122
+ notificationCloses;
123
+ pushSubscriptionChanges;
124
+ subscription;
125
+ get isEnabled() {
126
+ return this.sw.isEnabled;
127
+ }
128
+ pushManager = null;
129
+ subscriptionChanges = new Subject();
130
+ constructor(sw) {
131
+ this.sw = sw;
132
+ if (!sw.isEnabled) {
133
+ this.messages = NEVER;
134
+ this.notificationClicks = NEVER;
135
+ this.notificationCloses = NEVER;
136
+ this.pushSubscriptionChanges = NEVER;
137
+ this.subscription = NEVER;
138
+ return;
262
139
  }
263
- pushManager = null;
264
- subscriptionChanges = new Subject();
265
- constructor(sw) {
266
- this.sw = sw;
267
- if (!sw.isEnabled) {
268
- this.messages = NEVER;
269
- this.notificationClicks = NEVER;
270
- this.notificationCloses = NEVER;
271
- this.pushSubscriptionChanges = NEVER;
272
- this.subscription = NEVER;
273
- return;
274
- }
275
- this.messages = this.sw.eventsOfType('PUSH').pipe(map((message) => message.data));
276
- this.notificationClicks = this.sw
277
- .eventsOfType('NOTIFICATION_CLICK')
278
- .pipe(map((message) => message.data));
279
- this.notificationCloses = this.sw
280
- .eventsOfType('NOTIFICATION_CLOSE')
281
- .pipe(map((message) => message.data));
282
- this.pushSubscriptionChanges = this.sw
283
- .eventsOfType('PUSH_SUBSCRIPTION_CHANGE')
284
- .pipe(map((message) => message.data));
285
- this.pushManager = this.sw.registration.pipe(map((registration) => registration.pushManager));
286
- const workerDrivenSubscriptions = this.pushManager.pipe(switchMap((pm) => pm.getSubscription()));
287
- this.subscription = new Observable((subscriber) => {
288
- const workerDrivenSubscription = workerDrivenSubscriptions.subscribe(subscriber);
289
- const subscriptionChanges = this.subscriptionChanges.subscribe(subscriber);
290
- return () => {
291
- workerDrivenSubscription.unsubscribe();
292
- subscriptionChanges.unsubscribe();
293
- };
294
- });
140
+ this.messages = this.sw.eventsOfType('PUSH').pipe(map(message => message.data));
141
+ this.notificationClicks = this.sw.eventsOfType('NOTIFICATION_CLICK').pipe(map(message => message.data));
142
+ this.notificationCloses = this.sw.eventsOfType('NOTIFICATION_CLOSE').pipe(map(message => message.data));
143
+ this.pushSubscriptionChanges = this.sw.eventsOfType('PUSH_SUBSCRIPTION_CHANGE').pipe(map(message => message.data));
144
+ this.pushManager = this.sw.registration.pipe(map(registration => registration.pushManager));
145
+ const workerDrivenSubscriptions = this.pushManager.pipe(switchMap(pm => pm.getSubscription()));
146
+ this.subscription = new Observable(subscriber => {
147
+ const workerDrivenSubscription = workerDrivenSubscriptions.subscribe(subscriber);
148
+ const subscriptionChanges = this.subscriptionChanges.subscribe(subscriber);
149
+ return () => {
150
+ workerDrivenSubscription.unsubscribe();
151
+ subscriptionChanges.unsubscribe();
152
+ };
153
+ });
154
+ }
155
+ requestSubscription(options) {
156
+ if (!this.sw.isEnabled || this.pushManager === null) {
157
+ return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED));
295
158
  }
296
- /**
297
- * Subscribes to Web Push Notifications,
298
- * after requesting and receiving user permission.
299
- *
300
- * @param options An object containing the `serverPublicKey` string.
301
- * @returns A Promise that resolves to the new subscription object.
302
- */
303
- requestSubscription(options) {
304
- if (!this.sw.isEnabled || this.pushManager === null) {
305
- return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED));
306
- }
307
- const pushOptions = { userVisibleOnly: true };
308
- let key = this.decodeBase64(options.serverPublicKey.replace(/_/g, '/').replace(/-/g, '+'));
309
- let applicationServerKey = new Uint8Array(new ArrayBuffer(key.length));
310
- for (let i = 0; i < key.length; i++) {
311
- applicationServerKey[i] = key.charCodeAt(i);
312
- }
313
- pushOptions.applicationServerKey = applicationServerKey;
314
- return new Promise((resolve, reject) => {
315
- this.pushManager.pipe(switchMap((pm) => pm.subscribe(pushOptions)), take(1)).subscribe({
316
- next: (sub) => {
317
- this.subscriptionChanges.next(sub);
318
- resolve(sub);
319
- },
320
- error: reject,
321
- });
322
- });
159
+ const pushOptions = {
160
+ userVisibleOnly: true
161
+ };
162
+ let key = this.decodeBase64(options.serverPublicKey.replace(/_/g, '/').replace(/-/g, '+'));
163
+ let applicationServerKey = new Uint8Array(new ArrayBuffer(key.length));
164
+ for (let i = 0; i < key.length; i++) {
165
+ applicationServerKey[i] = key.charCodeAt(i);
323
166
  }
324
- /**
325
- * Unsubscribes from Service Worker push notifications.
326
- *
327
- * @returns A Promise that is resolved when the operation succeeds, or is rejected if there is no
328
- * active subscription or the unsubscribe operation fails.
329
- */
330
- unsubscribe() {
331
- if (!this.sw.isEnabled) {
332
- return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED));
333
- }
334
- const doUnsubscribe = (sub) => {
335
- if (sub === null) {
336
- throw new _RuntimeError(5602 /* RuntimeErrorCode.NOT_SUBSCRIBED_TO_PUSH_NOTIFICATIONS */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
337
- 'Not subscribed to push notifications.');
338
- }
339
- return sub.unsubscribe().then((success) => {
340
- if (!success) {
341
- throw new _RuntimeError(5603 /* RuntimeErrorCode.PUSH_SUBSCRIPTION_UNSUBSCRIBE_FAILED */, (typeof ngDevMode === 'undefined' || ngDevMode) && 'Unsubscribe failed!');
342
- }
343
- this.subscriptionChanges.next(null);
344
- });
345
- };
346
- return new Promise((resolve, reject) => {
347
- this.subscription
348
- .pipe(take(1), switchMap(doUnsubscribe))
349
- .subscribe({ next: resolve, error: reject });
350
- });
351
- }
352
- decodeBase64(input) {
353
- return atob(input);
167
+ pushOptions.applicationServerKey = applicationServerKey;
168
+ return new Promise((resolve, reject) => {
169
+ this.pushManager.pipe(switchMap(pm => pm.subscribe(pushOptions)), take(1)).subscribe({
170
+ next: sub => {
171
+ this.subscriptionChanges.next(sub);
172
+ resolve(sub);
173
+ },
174
+ error: reject
175
+ });
176
+ });
177
+ }
178
+ unsubscribe() {
179
+ if (!this.sw.isEnabled) {
180
+ return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED));
354
181
  }
355
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0-next.8", ngImport: i0, type: SwPush, deps: [{ token: NgswCommChannel }], target: i0.ɵɵFactoryTarget.Injectable });
356
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0-next.8", ngImport: i0, type: SwPush });
182
+ const doUnsubscribe = sub => {
183
+ if (sub === null) {
184
+ throw new _RuntimeError(5602, (typeof ngDevMode === 'undefined' || ngDevMode) && 'Not subscribed to push notifications.');
185
+ }
186
+ return sub.unsubscribe().then(success => {
187
+ if (!success) {
188
+ throw new _RuntimeError(5603, (typeof ngDevMode === 'undefined' || ngDevMode) && 'Unsubscribe failed!');
189
+ }
190
+ this.subscriptionChanges.next(null);
191
+ });
192
+ };
193
+ return new Promise((resolve, reject) => {
194
+ this.subscription.pipe(take(1), switchMap(doUnsubscribe)).subscribe({
195
+ next: resolve,
196
+ error: reject
197
+ });
198
+ });
199
+ }
200
+ decodeBase64(input) {
201
+ return atob(input);
202
+ }
203
+ static ɵfac = i0.ɵɵngDeclareFactory({
204
+ minVersion: "12.0.0",
205
+ version: "21.0.0-rc.0",
206
+ ngImport: i0,
207
+ type: SwPush,
208
+ deps: [{
209
+ token: NgswCommChannel
210
+ }],
211
+ target: i0.ɵɵFactoryTarget.Injectable
212
+ });
213
+ static ɵprov = i0.ɵɵngDeclareInjectable({
214
+ minVersion: "12.0.0",
215
+ version: "21.0.0-rc.0",
216
+ ngImport: i0,
217
+ type: SwPush
218
+ });
357
219
  }
358
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0-next.8", ngImport: i0, type: SwPush, decorators: [{
359
- type: Injectable
360
- }], ctorParameters: () => [{ type: NgswCommChannel }] });
220
+ i0.ɵɵngDeclareClassMetadata({
221
+ minVersion: "12.0.0",
222
+ version: "21.0.0-rc.0",
223
+ ngImport: i0,
224
+ type: SwPush,
225
+ decorators: [{
226
+ type: Injectable
227
+ }],
228
+ ctorParameters: () => [{
229
+ type: NgswCommChannel
230
+ }]
231
+ });
361
232
 
362
- /**
363
- * Subscribe to update notifications from the Service Worker, trigger update
364
- * checks, and forcibly activate updates.
365
- *
366
- * @see {@link /ecosystem/service-workers/communications Service Worker Communication Guide}
367
- *
368
- * @publicApi
369
- */
370
233
  class SwUpdate {
371
- sw;
372
- /**
373
- * Emits a `VersionDetectedEvent` event whenever a new version is detected on the server.
374
- *
375
- * Emits a `VersionInstallationFailedEvent` event whenever checking for or downloading a new
376
- * version fails.
377
- *
378
- * Emits a `VersionReadyEvent` event whenever a new version has been downloaded and is ready for
379
- * activation.
380
- */
381
- versionUpdates;
382
- /**
383
- * Emits an `UnrecoverableStateEvent` event whenever the version of the app used by the service
384
- * worker to serve this client is in a broken state that cannot be recovered from without a full
385
- * page reload.
386
- */
387
- unrecoverable;
388
- /**
389
- * True if the Service Worker is enabled (supported by the browser and enabled via
390
- * `ServiceWorkerModule`).
391
- */
392
- get isEnabled() {
393
- return this.sw.isEnabled;
234
+ sw;
235
+ versionUpdates;
236
+ unrecoverable;
237
+ get isEnabled() {
238
+ return this.sw.isEnabled;
239
+ }
240
+ ongoingCheckForUpdate = null;
241
+ constructor(sw) {
242
+ this.sw = sw;
243
+ if (!sw.isEnabled) {
244
+ this.versionUpdates = NEVER;
245
+ this.unrecoverable = NEVER;
246
+ return;
394
247
  }
395
- ongoingCheckForUpdate = null;
396
- constructor(sw) {
397
- this.sw = sw;
398
- if (!sw.isEnabled) {
399
- this.versionUpdates = NEVER;
400
- this.unrecoverable = NEVER;
401
- return;
402
- }
403
- this.versionUpdates = this.sw.eventsOfType([
404
- 'VERSION_DETECTED',
405
- 'VERSION_INSTALLATION_FAILED',
406
- 'VERSION_READY',
407
- 'NO_NEW_VERSION_DETECTED',
408
- 'VERSION_FAILED',
409
- ]);
410
- this.unrecoverable = this.sw.eventsOfType('UNRECOVERABLE_STATE');
248
+ this.versionUpdates = this.sw.eventsOfType(['VERSION_DETECTED', 'VERSION_INSTALLATION_FAILED', 'VERSION_READY', 'NO_NEW_VERSION_DETECTED', 'VERSION_FAILED']);
249
+ this.unrecoverable = this.sw.eventsOfType('UNRECOVERABLE_STATE');
250
+ }
251
+ checkForUpdate() {
252
+ if (!this.sw.isEnabled) {
253
+ return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED));
411
254
  }
412
- /**
413
- * Checks for an update and waits until the new version is downloaded from the server and ready
414
- * for activation.
415
- *
416
- * @returns a promise that
417
- * - resolves to `true` if a new version was found and is ready to be activated.
418
- * - resolves to `false` if no new version was found
419
- * - rejects if any error occurs
420
- */
421
- checkForUpdate() {
422
- if (!this.sw.isEnabled) {
423
- return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED));
424
- }
425
- if (this.ongoingCheckForUpdate) {
426
- return this.ongoingCheckForUpdate;
427
- }
428
- const nonce = this.sw.generateNonce();
429
- this.ongoingCheckForUpdate = this.sw
430
- .postMessageWithOperation('CHECK_FOR_UPDATES', { nonce }, nonce)
431
- .finally(() => {
432
- this.ongoingCheckForUpdate = null;
433
- });
434
- return this.ongoingCheckForUpdate;
255
+ if (this.ongoingCheckForUpdate) {
256
+ return this.ongoingCheckForUpdate;
435
257
  }
436
- /**
437
- * Updates the current client (i.e. browser tab) to the latest version that is ready for
438
- * activation.
439
- *
440
- * In most cases, you should not use this method and instead should update a client by reloading
441
- * the page.
442
- *
443
- * <div class="docs-alert docs-alert-important">
444
- *
445
- * Updating a client without reloading can easily result in a broken application due to a version
446
- * mismatch between the application shell and other page resources,
447
- * such as lazy-loaded chunks, whose filenames may change between
448
- * versions.
449
- *
450
- * Only use this method, if you are certain it is safe for your specific use case.
451
- *
452
- * </div>
453
- *
454
- * @returns a promise that
455
- * - resolves to `true` if an update was activated successfully
456
- * - resolves to `false` if no update was available (for example, the client was already on the
457
- * latest version).
458
- * - rejects if any error occurs
459
- */
460
- activateUpdate() {
461
- if (!this.sw.isEnabled) {
462
- return Promise.reject(new _RuntimeError(5601 /* RuntimeErrorCode.SERVICE_WORKER_DISABLED_OR_NOT_SUPPORTED_BY_THIS_BROWSER */, (typeof ngDevMode === 'undefined' || ngDevMode) && ERR_SW_NOT_SUPPORTED));
463
- }
464
- const nonce = this.sw.generateNonce();
465
- return this.sw.postMessageWithOperation('ACTIVATE_UPDATE', { nonce }, nonce);
258
+ const nonce = this.sw.generateNonce();
259
+ this.ongoingCheckForUpdate = this.sw.postMessageWithOperation('CHECK_FOR_UPDATES', {
260
+ nonce
261
+ }, nonce).finally(() => {
262
+ this.ongoingCheckForUpdate = null;
263
+ });
264
+ return this.ongoingCheckForUpdate;
265
+ }
266
+ activateUpdate() {
267
+ if (!this.sw.isEnabled) {
268
+ return Promise.reject(new _RuntimeError(5601, (typeof ngDevMode === 'undefined' || ngDevMode) && ERR_SW_NOT_SUPPORTED));
466
269
  }
467
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0-next.8", ngImport: i0, type: SwUpdate, deps: [{ token: NgswCommChannel }], target: i0.ɵɵFactoryTarget.Injectable });
468
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0-next.8", ngImport: i0, type: SwUpdate });
270
+ const nonce = this.sw.generateNonce();
271
+ return this.sw.postMessageWithOperation('ACTIVATE_UPDATE', {
272
+ nonce
273
+ }, nonce);
274
+ }
275
+ static ɵfac = i0.ɵɵngDeclareFactory({
276
+ minVersion: "12.0.0",
277
+ version: "21.0.0-rc.0",
278
+ ngImport: i0,
279
+ type: SwUpdate,
280
+ deps: [{
281
+ token: NgswCommChannel
282
+ }],
283
+ target: i0.ɵɵFactoryTarget.Injectable
284
+ });
285
+ static ɵprov = i0.ɵɵngDeclareInjectable({
286
+ minVersion: "12.0.0",
287
+ version: "21.0.0-rc.0",
288
+ ngImport: i0,
289
+ type: SwUpdate
290
+ });
469
291
  }
470
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0-next.8", ngImport: i0, type: SwUpdate, decorators: [{
471
- type: Injectable
472
- }], ctorParameters: () => [{ type: NgswCommChannel }] });
292
+ i0.ɵɵngDeclareClassMetadata({
293
+ minVersion: "12.0.0",
294
+ version: "21.0.0-rc.0",
295
+ ngImport: i0,
296
+ type: SwUpdate,
297
+ decorators: [{
298
+ type: Injectable
299
+ }],
300
+ ctorParameters: () => [{
301
+ type: NgswCommChannel
302
+ }]
303
+ });
473
304
 
474
- /*!
475
- * @license
476
- * Copyright Google LLC All Rights Reserved.
477
- *
478
- * Use of this source code is governed by an MIT-style license that can be
479
- * found in the LICENSE file at https://angular.dev/license
480
- */
481
305
  const SCRIPT = new InjectionToken(typeof ngDevMode !== undefined && ngDevMode ? 'NGSW_REGISTER_SCRIPT' : '');
482
306
  function ngswAppInitializer() {
483
- if (typeof ngServerMode !== 'undefined' && ngServerMode) {
484
- return;
307
+ if (typeof ngServerMode !== 'undefined' && ngServerMode) {
308
+ return;
309
+ }
310
+ const options = inject(SwRegistrationOptions);
311
+ if (!('serviceWorker' in navigator && options.enabled !== false)) {
312
+ return;
313
+ }
314
+ const script = inject(SCRIPT);
315
+ const ngZone = inject(NgZone);
316
+ const appRef = inject(ApplicationRef);
317
+ ngZone.runOutsideAngular(() => {
318
+ const sw = navigator.serviceWorker;
319
+ const onControllerChange = () => sw.controller?.postMessage({
320
+ action: 'INITIALIZE'
321
+ });
322
+ sw.addEventListener('controllerchange', onControllerChange);
323
+ appRef.onDestroy(() => {
324
+ sw.removeEventListener('controllerchange', onControllerChange);
325
+ });
326
+ });
327
+ ngZone.runOutsideAngular(() => {
328
+ let readyToRegister;
329
+ const {
330
+ registrationStrategy
331
+ } = options;
332
+ if (typeof registrationStrategy === 'function') {
333
+ readyToRegister = new Promise(resolve => registrationStrategy().subscribe(() => resolve()));
334
+ } else {
335
+ const [strategy, ...args] = (registrationStrategy || 'registerWhenStable:30000').split(':');
336
+ switch (strategy) {
337
+ case 'registerImmediately':
338
+ readyToRegister = Promise.resolve();
339
+ break;
340
+ case 'registerWithDelay':
341
+ readyToRegister = delayWithTimeout(+args[0] || 0);
342
+ break;
343
+ case 'registerWhenStable':
344
+ readyToRegister = Promise.race([appRef.whenStable(), delayWithTimeout(+args[0])]);
345
+ break;
346
+ default:
347
+ throw new _RuntimeError(5600, (typeof ngDevMode === 'undefined' || ngDevMode) && `Unknown ServiceWorker registration strategy: ${options.registrationStrategy}`);
348
+ }
485
349
  }
486
- const options = inject(SwRegistrationOptions);
487
- if (!('serviceWorker' in navigator && options.enabled !== false)) {
350
+ readyToRegister.then(() => {
351
+ if (appRef.destroyed) {
488
352
  return;
489
- }
490
- const script = inject(SCRIPT);
491
- const ngZone = inject(NgZone);
492
- const appRef = inject(ApplicationRef);
493
- // Set up the `controllerchange` event listener outside of
494
- // the Angular zone to avoid unnecessary change detections,
495
- // as this event has no impact on view updates.
496
- ngZone.runOutsideAngular(() => {
497
- // Wait for service worker controller changes, and fire an INITIALIZE action when a new SW
498
- // becomes active. This allows the SW to initialize itself even if there is no application
499
- // traffic.
500
- const sw = navigator.serviceWorker;
501
- const onControllerChange = () => sw.controller?.postMessage({ action: 'INITIALIZE' });
502
- sw.addEventListener('controllerchange', onControllerChange);
503
- appRef.onDestroy(() => {
504
- sw.removeEventListener('controllerchange', onControllerChange);
505
- });
506
- });
507
- // Run outside the Angular zone to avoid preventing the app from stabilizing (especially
508
- // given that some registration strategies wait for the app to stabilize).
509
- ngZone.runOutsideAngular(() => {
510
- let readyToRegister;
511
- const { registrationStrategy } = options;
512
- if (typeof registrationStrategy === 'function') {
513
- readyToRegister = new Promise((resolve) => registrationStrategy().subscribe(() => resolve()));
514
- }
515
- else {
516
- const [strategy, ...args] = (registrationStrategy || 'registerWhenStable:30000').split(':');
517
- switch (strategy) {
518
- case 'registerImmediately':
519
- readyToRegister = Promise.resolve();
520
- break;
521
- case 'registerWithDelay':
522
- readyToRegister = delayWithTimeout(+args[0] || 0);
523
- break;
524
- case 'registerWhenStable':
525
- readyToRegister = Promise.race([appRef.whenStable(), delayWithTimeout(+args[0])]);
526
- break;
527
- default:
528
- // Unknown strategy.
529
- throw new _RuntimeError(5600 /* RuntimeErrorCode.UNKNOWN_REGISTRATION_STRATEGY */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
530
- `Unknown ServiceWorker registration strategy: ${options.registrationStrategy}`);
531
- }
532
- }
533
- // Don't return anything to avoid blocking the application until the SW is registered.
534
- // Catch and log the error if SW registration fails to avoid uncaught rejection warning.
535
- readyToRegister.then(() => {
536
- // If the registration strategy has resolved after the application has
537
- // been explicitly destroyed by the user (e.g., by navigating away to
538
- // another application), we simply should not register the worker.
539
- if (appRef.destroyed) {
540
- return;
541
- }
542
- navigator.serviceWorker
543
- .register(script, {
544
- scope: options.scope,
545
- updateViaCache: options.updateViaCache,
546
- type: options.type,
547
- })
548
- .catch((err) => console.error(_formatRuntimeError(5604 /* RuntimeErrorCode.SERVICE_WORKER_REGISTRATION_FAILED */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
549
- 'Service worker registration failed with: ' + err)));
550
- });
353
+ }
354
+ navigator.serviceWorker.register(script, {
355
+ scope: options.scope,
356
+ updateViaCache: options.updateViaCache,
357
+ type: options.type
358
+ }).catch(err => console.error(_formatRuntimeError(5604, (typeof ngDevMode === 'undefined' || ngDevMode) && 'Service worker registration failed with: ' + err)));
551
359
  });
360
+ });
552
361
  }
553
362
  function delayWithTimeout(timeout) {
554
- return new Promise((resolve) => setTimeout(resolve, timeout));
363
+ return new Promise(resolve => setTimeout(resolve, timeout));
555
364
  }
556
365
  function ngswCommChannelFactory() {
557
- const opts = inject(SwRegistrationOptions);
558
- const injector = inject(Injector);
559
- const isBrowser = !(typeof ngServerMode !== 'undefined' && ngServerMode);
560
- return new NgswCommChannel(isBrowser && opts.enabled !== false ? navigator.serviceWorker : undefined, injector);
366
+ const opts = inject(SwRegistrationOptions);
367
+ const injector = inject(Injector);
368
+ const isBrowser = !(typeof ngServerMode !== 'undefined' && ngServerMode);
369
+ return new NgswCommChannel(isBrowser && opts.enabled !== false ? navigator.serviceWorker : undefined, injector);
561
370
  }
562
- /**
563
- * Token that can be used to provide options for `ServiceWorkerModule` outside of
564
- * `ServiceWorkerModule.register()`.
565
- *
566
- * You can use this token to define a provider that generates the registration options at runtime,
567
- * for example via a function call:
568
- *
569
- * {@example service-worker/registration-options/module.ts region="registration-options"
570
- * header="app.module.ts"}
571
- *
572
- * @publicApi
573
- */
574
371
  class SwRegistrationOptions {
575
- /**
576
- * Whether the ServiceWorker will be registered and the related services (such as `SwPush` and
577
- * `SwUpdate`) will attempt to communicate and interact with it.
578
- *
579
- * Default: true
580
- */
581
- enabled;
582
- /**
583
- * The value of the setting used to determine the circumstances in which the browser
584
- * will consult the HTTP cache when it tries to update the service worker or any scripts that are imported via importScripts().
585
- * [ServiceWorkerRegistration.updateViaCache](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/updateViaCache)
586
- */
587
- updateViaCache;
588
- /**
589
- * The type of the ServiceWorker script to register.
590
- * [ServiceWorkerRegistration#type](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register#type)
591
- * - `classic`: Registers the script as a classic worker. ES module features such as `import` and `export` are NOT allowed in the script.
592
- * - `module`: Registers the script as an ES module. Allows use of `import`/`export` syntax and module features.
593
- *
594
- * @default 'classic'
595
- */
596
- type;
597
- /**
598
- * A URL that defines the ServiceWorker's registration scope; that is, what range of URLs it can
599
- * control. It will be used when calling
600
- * [ServiceWorkerContainer#register()](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register).
601
- */
602
- scope;
603
- /**
604
- * Defines the ServiceWorker registration strategy, which determines when it will be registered
605
- * with the browser.
606
- *
607
- * The default behavior of registering once the application stabilizes (i.e. as soon as there are
608
- * no pending micro- and macro-tasks) is designed to register the ServiceWorker as soon as
609
- * possible but without affecting the application's first time load.
610
- *
611
- * Still, there might be cases where you want more control over when the ServiceWorker is
612
- * registered (for example, there might be a long-running timeout or polling interval, preventing
613
- * the app from stabilizing). The available option are:
614
- *
615
- * - `registerWhenStable:<timeout>`: Register as soon as the application stabilizes (no pending
616
- * micro-/macro-tasks) but no later than `<timeout>` milliseconds. If the app hasn't
617
- * stabilized after `<timeout>` milliseconds (for example, due to a recurrent asynchronous
618
- * task), the ServiceWorker will be registered anyway.
619
- * If `<timeout>` is omitted, the ServiceWorker will only be registered once the app
620
- * stabilizes.
621
- * - `registerImmediately`: Register immediately.
622
- * - `registerWithDelay:<timeout>`: Register with a delay of `<timeout>` milliseconds. For
623
- * example, use `registerWithDelay:5000` to register the ServiceWorker after 5 seconds. If
624
- * `<timeout>` is omitted, is defaults to `0`, which will register the ServiceWorker as soon
625
- * as possible but still asynchronously, once all pending micro-tasks are completed.
626
- * - An Observable factory function: A function that returns an `Observable`.
627
- * The function will be used at runtime to obtain and subscribe to the `Observable` and the
628
- * ServiceWorker will be registered as soon as the first value is emitted.
629
- *
630
- * Default: 'registerWhenStable:30000'
631
- */
632
- registrationStrategy;
372
+ enabled;
373
+ updateViaCache;
374
+ type;
375
+ scope;
376
+ registrationStrategy;
633
377
  }
634
- /**
635
- * @publicApi
636
- *
637
- * Sets up providers to register the given Angular Service Worker script.
638
- *
639
- * If `enabled` is set to `false` in the given options, the module will behave as if service
640
- * workers are not supported by the browser, and the service worker will not be registered.
641
- *
642
- * Example usage:
643
- * ```ts
644
- * bootstrapApplication(AppComponent, {
645
- * providers: [
646
- * provideServiceWorker('ngsw-worker.js')
647
- * ],
648
- * });
649
- * ```
650
- */
651
378
  function provideServiceWorker(script, options = {}) {
652
- return makeEnvironmentProviders([
653
- SwPush,
654
- SwUpdate,
655
- { provide: SCRIPT, useValue: script },
656
- { provide: SwRegistrationOptions, useValue: options },
657
- {
658
- provide: NgswCommChannel,
659
- useFactory: ngswCommChannelFactory,
660
- },
661
- provideAppInitializer(ngswAppInitializer),
662
- ]);
379
+ return makeEnvironmentProviders([SwPush, SwUpdate, {
380
+ provide: SCRIPT,
381
+ useValue: script
382
+ }, {
383
+ provide: SwRegistrationOptions,
384
+ useValue: options
385
+ }, {
386
+ provide: NgswCommChannel,
387
+ useFactory: ngswCommChannelFactory
388
+ }, provideAppInitializer(ngswAppInitializer)]);
663
389
  }
664
390
 
665
- /**
666
- * @publicApi
667
- */
668
391
  class ServiceWorkerModule {
669
- /**
670
- * Register the given Angular Service Worker script.
671
- *
672
- * If `enabled` is set to `false` in the given options, the module will behave as if service
673
- * workers are not supported by the browser, and the service worker will not be registered.
674
- */
675
- static register(script, options = {}) {
676
- return {
677
- ngModule: ServiceWorkerModule,
678
- providers: [provideServiceWorker(script, options)],
679
- };
680
- }
681
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0-next.8", ngImport: i0, type: ServiceWorkerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
682
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.0-next.8", ngImport: i0, type: ServiceWorkerModule });
683
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.0-next.8", ngImport: i0, type: ServiceWorkerModule, providers: [SwPush, SwUpdate] });
392
+ static register(script, options = {}) {
393
+ return {
394
+ ngModule: ServiceWorkerModule,
395
+ providers: [provideServiceWorker(script, options)]
396
+ };
397
+ }
398
+ static ɵfac = i0.ɵɵngDeclareFactory({
399
+ minVersion: "12.0.0",
400
+ version: "21.0.0-rc.0",
401
+ ngImport: i0,
402
+ type: ServiceWorkerModule,
403
+ deps: [],
404
+ target: i0.ɵɵFactoryTarget.NgModule
405
+ });
406
+ static ɵmod = i0.ɵɵngDeclareNgModule({
407
+ minVersion: "14.0.0",
408
+ version: "21.0.0-rc.0",
409
+ ngImport: i0,
410
+ type: ServiceWorkerModule
411
+ });
412
+ static ɵinj = i0.ɵɵngDeclareInjector({
413
+ minVersion: "12.0.0",
414
+ version: "21.0.0-rc.0",
415
+ ngImport: i0,
416
+ type: ServiceWorkerModule,
417
+ providers: [SwPush, SwUpdate]
418
+ });
684
419
  }
685
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0-next.8", ngImport: i0, type: ServiceWorkerModule, decorators: [{
686
- type: NgModule,
687
- args: [{ providers: [SwPush, SwUpdate] }]
688
- }] });
420
+ i0.ɵɵngDeclareClassMetadata({
421
+ minVersion: "12.0.0",
422
+ version: "21.0.0-rc.0",
423
+ ngImport: i0,
424
+ type: ServiceWorkerModule,
425
+ decorators: [{
426
+ type: NgModule,
427
+ args: [{
428
+ providers: [SwPush, SwUpdate]
429
+ }]
430
+ }]
431
+ });
689
432
 
690
433
  export { ServiceWorkerModule, SwPush, SwRegistrationOptions, SwUpdate, provideServiceWorker };
691
434
  //# sourceMappingURL=service-worker.mjs.map