diff --git a/packages/service-worker/worker/main.ts b/packages/service-worker/worker/main.ts index 11f86ccb5e..dab1ec368e 100644 --- a/packages/service-worker/worker/main.ts +++ b/packages/service-worker/worker/main.ts @@ -12,5 +12,5 @@ import {Driver} from './src/driver'; const scope = self as any as ServiceWorkerGlobalScope; -const adapter = new Adapter(); +const adapter = new Adapter(scope); const driver = new Driver(scope, adapter, new CacheDatabase(scope, adapter)); diff --git a/packages/service-worker/worker/src/adapter.ts b/packages/service-worker/worker/src/adapter.ts index 2e2847d62e..5955cdd56b 100644 --- a/packages/service-worker/worker/src/adapter.ts +++ b/packages/service-worker/worker/src/adapter.ts @@ -13,6 +13,15 @@ * from the global scope. */ export class Adapter { + readonly cacheNamePrefix: string; + + constructor(scope: ServiceWorkerGlobalScope) { + // Suffixing `ngsw` with the baseHref to avoid clash of cache names + // for SWs with different scopes on the same domain. + const baseHref = new URL(scope.registration.scope).pathname; + this.cacheNamePrefix = 'ngsw:' + baseHref; + } + /** * Wrapper around the `Request` constructor. */ diff --git a/packages/service-worker/worker/src/app-version.ts b/packages/service-worker/worker/src/app-version.ts index dafdcabfe0..5db2d6d3a4 100644 --- a/packages/service-worker/worker/src/app-version.ts +++ b/packages/service-worker/worker/src/app-version.ts @@ -72,7 +72,7 @@ export class AppVersion implements UpdateSource { this.assetGroups = (manifest.assetGroups || []).map(config => { // Every asset group has a cache that's prefixed by the manifest hash and the name of the // group. - const prefix = `ngsw:${this.manifestHash}:assets`; + const prefix = `${adapter.cacheNamePrefix}:${this.manifestHash}:assets`; // Check the caching mode, which determines when resources will be fetched/updated. switch (config.installMode) { case 'prefetch': @@ -89,7 +89,7 @@ export class AppVersion implements UpdateSource { .map( config => new DataGroup( this.scope, this.adapter, config, this.database, - `ngsw:${config.version}:data`)); + `${adapter.cacheNamePrefix}:${config.version}:data`)); // This keeps backwards compatibility with app versions without navigation urls. // Fix: https://github.com/angular/angular/issues/27209 diff --git a/packages/service-worker/worker/src/db-cache.ts b/packages/service-worker/worker/src/db-cache.ts index 581aadf2fb..2a4b112821 100644 --- a/packages/service-worker/worker/src/db-cache.ts +++ b/packages/service-worker/worker/src/db-cache.ts @@ -23,16 +23,17 @@ export class CacheDatabase implements Database { if (this.tables.has(name)) { this.tables.delete(name); } - return this.scope.caches.delete(`ngsw:db:${name}`); + return this.scope.caches.delete(`${this.adapter.cacheNamePrefix}:db:${name}`); } list(): Promise { - return this.scope.caches.keys().then(keys => keys.filter(key => key.startsWith('ngsw:db:'))); + return this.scope.caches.keys().then( + keys => keys.filter(key => key.startsWith(`${this.adapter.cacheNamePrefix}:db:`))); } open(name: string): Promise { if (!this.tables.has(name)) { - const table = this.scope.caches.open(`ngsw:db:${name}`) + const table = this.scope.caches.open(`${this.adapter.cacheNamePrefix}:db:${name}`) .then(cache => new CacheTable(name, cache, this.adapter)); this.tables.set(name, table); } diff --git a/packages/service-worker/worker/src/driver.ts b/packages/service-worker/worker/src/driver.ts index 186f21ed80..d7b69ba57f 100644 --- a/packages/service-worker/worker/src/driver.ts +++ b/packages/service-worker/worker/src/driver.ts @@ -705,7 +705,7 @@ export class Driver implements Debuggable, UpdateSource { private async deleteAllCaches(): Promise { await(await this.scope.caches.keys()) - .filter(key => key.startsWith('ngsw:')) + .filter(key => key.startsWith(`${this.adapter.cacheNamePrefix}:`)) .reduce(async(previous, key) => { await Promise.all([ previous, @@ -924,9 +924,7 @@ export class Driver implements Debuggable, UpdateSource { */ async cleanupOldSwCaches(): Promise { const cacheNames = await this.scope.caches.keys(); - const oldSwCacheNames = - cacheNames.filter(name => /^ngsw:(?:active|staged|manifest:.+)$/.test(name)); - + const oldSwCacheNames = cacheNames.filter(name => /^ngsw:(?!\/)/.test(name)); await Promise.all(oldSwCacheNames.map(name => this.scope.caches.delete(name))); } diff --git a/packages/service-worker/worker/test/happy_spec.ts b/packages/service-worker/worker/test/happy_spec.ts index 59ce6d6d1f..95bf43eec3 100644 --- a/packages/service-worker/worker/test/happy_spec.ts +++ b/packages/service-worker/worker/test/happy_spec.ts @@ -587,7 +587,7 @@ import {async_beforeEach, async_fit, async_it} from './async'; serverUpdate.assertNoOtherRequests(); let keys = await scope.caches.keys(); - let hasOriginalCaches = keys.some(name => name.startsWith(`ngsw:${manifestHash}:`)); + let hasOriginalCaches = keys.some(name => name.startsWith(`ngsw:/:${manifestHash}:`)); expect(hasOriginalCaches).toEqual(true); scope.clients.remove('default'); @@ -600,7 +600,7 @@ import {async_beforeEach, async_fit, async_it} from './async'; expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo v2'); keys = await scope.caches.keys(); - hasOriginalCaches = keys.some(name => name.startsWith(`ngsw:${manifestHash}:`)); + hasOriginalCaches = keys.some(name => name.startsWith(`ngsw:/:${manifestHash}:`)); expect(hasOriginalCaches).toEqual(false); }); @@ -938,13 +938,21 @@ import {async_beforeEach, async_fit, async_it} from './async'; describe('cleanupOldSwCaches()', () => { async_it('should delete the correct caches', async() => { - const oldSwCacheNames = ['ngsw:active', 'ngsw:staged', 'ngsw:manifest:a1b2c3:super:duper']; + const oldSwCacheNames = [ + // Example cache names from the beta versions of `@angular/service-worker`. + 'ngsw:active', + 'ngsw:staged', + 'ngsw:manifest:a1b2c3:super:duper', + // Example cache names from the beta versions of `@angular/service-worker`. + 'ngsw:a1b2c3:assets:foo', + 'ngsw:db:a1b2c3:assets:bar', + ]; const otherCacheNames = [ 'ngsuu:active', 'not:ngsw:active', - 'ngsw:staged:not', 'NgSw:StAgEd', - 'ngsw:manifest', + 'ngsw:/:active', + 'ngsw:/foo/:staged', ]; const allCacheNames = oldSwCacheNames.concat(otherCacheNames); diff --git a/packages/service-worker/worker/testing/scope.ts b/packages/service-worker/worker/testing/scope.ts index f2c0d1f66e..1ce31cde4b 100644 --- a/packages/service-worker/worker/testing/scope.ts +++ b/packages/service-worker/worker/testing/scope.ts @@ -74,6 +74,7 @@ export class MockClients implements Clients { } export class SwTestHarness implements ServiceWorkerGlobalScope, Adapter, Context { + readonly cacheNamePrefix: string; readonly clients = new MockClients(); private eventHandlers = new Map(); private skippedWaiting = true; @@ -115,6 +116,8 @@ export class SwTestHarness implements ServiceWorkerGlobalScope, Adapter, Context constructor(private server: MockServerState, readonly caches: MockCacheStorage) { this.time = Date.now(); + const baseHref = new URL(this.registration.scope).pathname; + this.cacheNamePrefix = 'ngsw:' + baseHref; } async resolveSelfMessages(): Promise {