UNPKG

@angular/service-worker

Version:

Angular - service worker tooling!

334 lines (323 loc) 13.4 kB
/** * @license Angular v7.0.2 * (c) 2010-2018 Google, Inc. https://angular.io/ * License: MIT */ import { __assign, __decorate, __metadata } from 'tslib'; import { concat, defer, fromEvent, of, throwError, NEVER, Subject, merge } from 'rxjs'; import { filter, map, publish, switchMap, take, tap } from 'rxjs/operators'; import { Injectable, APP_INITIALIZER, ApplicationRef, InjectionToken, Injector, NgModule, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ var ERR_SW_NOT_SUPPORTED = 'Service workers are disabled or not supported by this browser'; function errorObservable(message) { return defer(function () { return throwError(new Error(message)); }); } /** * @publicApi */ var NgswCommChannel = /** @class */ (function () { function NgswCommChannel(serviceWorker) { this.serviceWorker = serviceWorker; if (!serviceWorker) { this.worker = this.events = this.registration = errorObservable(ERR_SW_NOT_SUPPORTED); } else { var controllerChangeEvents = fromEvent(serviceWorker, 'controllerchange'); var controllerChanges = controllerChangeEvents.pipe(map(function () { return serviceWorker.controller; })); var currentController = defer(function () { return of(serviceWorker.controller); }); var controllerWithChanges = concat(currentController, controllerChanges); this.worker = controllerWithChanges.pipe(filter(function (c) { return !!c; })); this.registration = (this.worker.pipe(switchMap(function () { return serviceWorker.getRegistration(); }))); var rawEvents = fromEvent(serviceWorker, 'message'); var rawEventPayload = rawEvents.pipe(map(function (event) { return event.data; })); var eventsUnconnected = rawEventPayload.pipe(filter(function (event) { return event && event.type; })); var events = eventsUnconnected.pipe(publish()); events.connect(); this.events = events; } } NgswCommChannel.prototype.postMessage = function (action, payload) { return this.worker .pipe(take(1), tap(function (sw) { sw.postMessage(__assign({ action: action }, payload)); })) .toPromise() .then(function () { return undefined; }); }; NgswCommChannel.prototype.postMessageWithStatus = function (type, payload, nonce) { var waitForStatus = this.waitForStatus(nonce); var postMessage = this.postMessage(type, payload); return Promise.all([waitForStatus, postMessage]).then(function () { return undefined; }); }; NgswCommChannel.prototype.generateNonce = function () { return Math.round(Math.random() * 10000000); }; NgswCommChannel.prototype.eventsOfType = function (type) { var filterFn = function (event) { return event.type === type; }; return this.events.pipe(filter(filterFn)); }; NgswCommChannel.prototype.nextEventOfType = function (type) { return this.eventsOfType(type).pipe(take(1)); }; NgswCommChannel.prototype.waitForStatus = function (nonce) { return this.eventsOfType('STATUS') .pipe(filter(function (event) { return event.nonce === nonce; }), take(1), map(function (event) { if (event.status) { return undefined; } throw new Error(event.error); })) .toPromise(); }; Object.defineProperty(NgswCommChannel.prototype, "isEnabled", { get: function () { return !!this.serviceWorker; }, enumerable: true, configurable: true }); return NgswCommChannel; }()); /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * Subscribe and listen to push notifications from the Service Worker. * * @publicApi */ var SwPush = /** @class */ (function () { function SwPush(sw) { this.sw = sw; this.subscriptionChanges = new Subject(); if (!sw.isEnabled) { this.messages = NEVER; this.subscription = NEVER; return; } this.messages = this.sw.eventsOfType('PUSH').pipe(map(function (message) { return message.data; })); this.pushManager = this.sw.registration.pipe(map(function (registration) { return registration.pushManager; })); var workerDrivenSubscriptions = this.pushManager.pipe(switchMap(function (pm) { return pm.getSubscription(); })); this.subscription = merge(workerDrivenSubscriptions, this.subscriptionChanges); } Object.defineProperty(SwPush.prototype, "isEnabled", { /** * True if the Service Worker is enabled (supported by the browser and enabled via * `ServiceWorkerModule`). */ get: function () { return this.sw.isEnabled; }, enumerable: true, configurable: true }); SwPush.prototype.requestSubscription = function (options) { var _this = this; if (!this.sw.isEnabled) { return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED)); } var pushOptions = { userVisibleOnly: true }; var key = this.decodeBase64(options.serverPublicKey.replace(/_/g, '/').replace(/-/g, '+')); var applicationServerKey = new Uint8Array(new ArrayBuffer(key.length)); for (var i = 0; i < key.length; i++) { applicationServerKey[i] = key.charCodeAt(i); } pushOptions.applicationServerKey = applicationServerKey; return this.pushManager.pipe(switchMap(function (pm) { return pm.subscribe(pushOptions); }), take(1)) .toPromise() .then(function (sub) { _this.subscriptionChanges.next(sub); return sub; }); }; SwPush.prototype.unsubscribe = function () { var _this = this; if (!this.sw.isEnabled) { return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED)); } var doUnsubscribe = function (sub) { if (sub === null) { throw new Error('Not subscribed to push notifications.'); } return sub.unsubscribe().then(function (success) { if (!success) { throw new Error('Unsubscribe failed!'); } _this.subscriptionChanges.next(null); }); }; return this.subscription.pipe(take(1), switchMap(doUnsubscribe)).toPromise(); }; SwPush.prototype.decodeBase64 = function (input) { return atob(input); }; SwPush = __decorate([ Injectable(), __metadata("design:paramtypes", [NgswCommChannel]) ], SwPush); return SwPush; }()); /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * Subscribe to update notifications from the Service Worker, trigger update * checks, and forcibly activate updates. * * @publicApi */ var SwUpdate = /** @class */ (function () { function SwUpdate(sw) { this.sw = sw; if (!sw.isEnabled) { this.available = NEVER; this.activated = NEVER; return; } this.available = this.sw.eventsOfType('UPDATE_AVAILABLE'); this.activated = this.sw.eventsOfType('UPDATE_ACTIVATED'); } Object.defineProperty(SwUpdate.prototype, "isEnabled", { /** * True if the Service Worker is enabled (supported by the browser and enabled via * `ServiceWorkerModule`). */ get: function () { return this.sw.isEnabled; }, enumerable: true, configurable: true }); SwUpdate.prototype.checkForUpdate = function () { if (!this.sw.isEnabled) { return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED)); } var statusNonce = this.sw.generateNonce(); return this.sw.postMessageWithStatus('CHECK_FOR_UPDATES', { statusNonce: statusNonce }, statusNonce); }; SwUpdate.prototype.activateUpdate = function () { if (!this.sw.isEnabled) { return Promise.reject(new Error(ERR_SW_NOT_SUPPORTED)); } var statusNonce = this.sw.generateNonce(); return this.sw.postMessageWithStatus('ACTIVATE_UPDATE', { statusNonce: statusNonce }, statusNonce); }; SwUpdate = __decorate([ Injectable(), __metadata("design:paramtypes", [NgswCommChannel]) ], SwUpdate); return SwUpdate; }()); /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ var RegistrationOptions = /** @class */ (function () { function RegistrationOptions() { } return RegistrationOptions; }()); var SCRIPT = new InjectionToken('NGSW_REGISTER_SCRIPT'); function ngswAppInitializer(injector, script, options, platformId) { var initializer = function () { var app = injector.get(ApplicationRef); if (!(isPlatformBrowser(platformId) && ('serviceWorker' in navigator) && options.enabled !== false)) { return; } var whenStable = app.isStable.pipe(filter(function (stable) { return !!stable; }), take(1)).toPromise(); // Wait for service worker controller changes, and fire an INITIALIZE action when a new SW // becomes active. This allows the SW to initialize itself even if there is no application // traffic. navigator.serviceWorker.addEventListener('controllerchange', function () { if (navigator.serviceWorker.controller !== null) { navigator.serviceWorker.controller.postMessage({ action: 'INITIALIZE' }); } }); // Don't return the Promise, as that will block the application until the SW is registered, and // cause a crash if the SW registration fails. whenStable.then(function () { return navigator.serviceWorker.register(script, { scope: options.scope }); }); }; return initializer; } function ngswCommChannelFactory(opts, platformId) { return new NgswCommChannel(isPlatformBrowser(platformId) && opts.enabled !== false ? navigator.serviceWorker : undefined); } /** * @publicApi */ var ServiceWorkerModule = /** @class */ (function () { function ServiceWorkerModule() { } ServiceWorkerModule_1 = ServiceWorkerModule; /** * Register the given Angular Service Worker script. * * If `enabled` is set to `false` in the given options, the module will behave as if service * workers are not supported by the browser, and the service worker will not be registered. */ ServiceWorkerModule.register = function (script, opts) { if (opts === void 0) { opts = {}; } return { ngModule: ServiceWorkerModule_1, providers: [ { provide: SCRIPT, useValue: script }, { provide: RegistrationOptions, useValue: opts }, { provide: NgswCommChannel, useFactory: ngswCommChannelFactory, deps: [RegistrationOptions, PLATFORM_ID] }, { provide: APP_INITIALIZER, useFactory: ngswAppInitializer, deps: [Injector, SCRIPT, RegistrationOptions, PLATFORM_ID], multi: true, }, ], }; }; var ServiceWorkerModule_1; ServiceWorkerModule = ServiceWorkerModule_1 = __decorate([ NgModule({ providers: [SwPush, SwUpdate], }) ], ServiceWorkerModule); return ServiceWorkerModule; }()); /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ // This file only reexports content of the `src` folder. Keep it that way. /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * Generated bundle index. Do not edit. */ export { NgswCommChannel as ɵangular_packages_service_worker_service_worker_a, RegistrationOptions as ɵangular_packages_service_worker_service_worker_b, SCRIPT as ɵangular_packages_service_worker_service_worker_c, ngswAppInitializer as ɵangular_packages_service_worker_service_worker_d, ngswCommChannelFactory as ɵangular_packages_service_worker_service_worker_e, ServiceWorkerModule, SwPush, SwUpdate }; //# sourceMappingURL=service-worker.js.map