@angular/service-worker
Version:
Angular - service worker tooling!
334 lines (323 loc) • 13.4 kB
JavaScript
/**
* @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