Repository: angular
Issue: Spawning Web Worker from previous AppVersion results in network fallback
Labels: area: service-worker

### Which @angular/* package(s) are the source of the bug?

service-worker

### Is this a regression?

Yes

### Description

Consider the following scenario:

- There is an existing Angular app with Service Worker
- An angular app has an on-demand web worker, that is cached via Service Worker
- A web worker is updated and a new version of an Angular app is deployed
- Existing client received a new version of an Angular app, but did not refresh an already opened tab

In that scenario, when an existing client, that already received a new version of an Angular app, but did not refresh an already opened tab, triggers a code path, where a new Web Worker is created, a Web Worker script is fetched from the network, instead of from a cache. Because of the nature of deployment, scripts from a previous version are not stored on a server, meaning a request for an old Web Worker script results in a 404 error, and eventually in a Web Worker initialization failure.

I'd expect a Service Worker to serve a Web Worker script for an old version if a fetch is initiated from an old Angular app version.

Please use a minimal reproduction repository and follow the steps in the video to simulate the issue.

[ngsw-web-worker.webm](https://github.com/user-attachments/assets/2da4f484-747a-42c4-ac5b-fd56cc412e01)


### Please provide a link to a minimal reproduction of the bug

https://github.com/rozpuszczalny/ngsw-web-worker

### Please provide the exception or error you saw

```true
Worker emitted an Event on 'error' callback; service worker called network, where it should use cache.
```


### Please provide the environment you discovered this bug in (run `ng version`)

```true
Angular CLI: 18.2.6
Node: 20.12.0
Package Manager: npm 10.5.0
OS: linux x64

Angular: 18.2.6
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
... service-worker

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1802.6
@angular-devkit/build-angular   18.2.6
@angular-devkit/core            18.2.6
@angular-devkit/schematics      18.2.6
@schematics/angular             18.2.6
rxjs                            7.8.1
typescript                      5.5.4
zone.js                         0.14.10
```


### Anything else?

We are experiencing such an issue in our production setup, which makes an update process quite painful for the users. Weirdly, for some users, simply reloading a tab does not solve the issue - we have to instruct the users to manually delete a Service Worker via `chrome://serviceworker-internals`. We still didn't identify nor reproduce that scenario where reload is not working.

---

Probably introduced by https://github.com/angular/angular/pull/42607, which was first released with Angular v12.

---

A possible solution is to check if `clientId` and `resultingClientId` are both set. If that's true, `clientId` should be used over `resultingClientId`. My crude testing showed (at least in Chrome browser) that `resultingClientId` is the client ID of a newly created web worker, and `clientId` is the client ID of a tab that spawned a web worker.

Since `resultingClientId` is relevant when opening a new tab, I've tested and noticed that `clientId` is set to an empty string when opening a website in a separate tab. However, opening `window.open('/', '_top')` sets both `clientId` and `resultingClientId` - similar to a web worker scenario. Unfortunately, `location.reload()` also sets both `clientId` and `resultingClientId`, which further complicates a potential solution.

https://github.com/angular/angular/blob/1549afe10eddc92845cd1fde862aaa010c2395af/packages/service-worker/worker/src/driver.ts#L713-L713