When an app version is detected as broken, the SW calls the
`Driver#versionFailed()` method. Previously, this method would in turn
call the `Driver#sync()` method, which writes some metadata about the
SW's state to the `ngsw:/:db:contol` cache. More specifically,
`Driver#sync()` persists info about all known app versions, which one is
the latest and also what version each client is assigned to.
However, no relevant info is changed inside `Driver#versionFailed()`, so
the call to `Driver#sync()` is redundant (since there are no changes
that need to be synced with the cache). This is a left-over from
before #43518, when `Driver#versionFailed()` used to update version
assignments and thus did require synchronization with the cache.
This commit removes the redundant `Driver#sync()` call to avoid an
unnecessary write to the cache.
PR Close#43680
This commit updates the `node` engines range for all Angular
framework packages to:
* No longer support NodeJS v12 `< 12.20`. This is done because APF v13
uses package export patterns which are only supported as of v12.20.
https://nodejs.org/api/packages.html#packages_subpath_patterns.
* Allows for the latest v16 NodeJS versions. This matches with the CLI
which added NodeJS v16 support with https://github.com/angular/angular-cli/pull/21854.
We already limit this to `>= v16.10.0` in preparation to only
supporting the LTS minors of Node v16.
BREAKING CHANGE: NodeJS versions older than `v12.20.0` are no longer
supported due to the Angular packages using the NodeJS package exports
feature with subpath patterns.
PR Close#43740
Previously, all asset-groups from `ngsw-config.json` were processed in
parallel. For each asset-group, we retrieved all files for the current
build, filtered out files that were already matched by other
asset-groups, determined which of the remaining files belonged to the
current asset-group and generated entries for the `ngsw.json` manifest.
This process was susceptible to race conditions when there were files
that would be matched by multiple asset-groups. This made the generation
of the `ngsw.json` manifest non-deterministic and violated the rule that
each file would belong to the first asset-group that matched it (based
on the asset-groups' order of appearance in `ngsw-config.json`), thus
leading to broken ServiceWorker behavior.
This commit fixes it by ensuring that the generation process is
deterministic and that asset-groups are processed in the proper order.
NOTE 1:
The generation process has been broken since the beginning, but we have
only noticed this recently. This is possibly related to the CLI's
switching from a virtual file system host (which has more consistent
timing characteristics) to the Node.js built-in `fs.promises` in
angular/angular-cli@d3bc530c10.
NOTE 2:
This commit also ensures that files in the `ngsw.json` hash-table are in
alphabetic order. Previously, the files were added to the hash-table in
blocks corresponding to each asset-group.
This change is not necessary (i.e. the order of keys in the hash-table
makes no difference in behavior), but it makes it easier to scan for a
file (for example, for debugging purposes).
PR Close#43679
This commit introduces a new `SwUpdate#versionUpdates` observalbe, which provides more granular
information about Service Worker version updates than the already existing `SwUpdate#available`
observable.
The new events emitted by `SwUpdate#versionUpdates` basically add the possibility for application to
get notified if a new version is available on the server (not yet downloaded) and if an installation
of a new version failed.
Closes#39840
DEPRECATED: The `SwUpdate#availalbe` observable is deprecated.
The new `SwUpdate#versionUpdates` observable provides the same information and more. Therefore, it
is possible to rebuild the same behavior as `SwUpdate#availalbe` using the events emitted by
`SwUpdate#versionUpdates` and filtering for `VersionReadyEvent` events.
As a result, the `SwUpdate#availalbe` observable is now redundant.
PR Close#43668
Previously `activateUpdate` and `checkForUpdate` returned `Promise<void>`, which did not provide any
information of the actual outcome of the operation. Developers needed to subscribe to a
corresponding observable to obtain the outcome.
With this commit `SwUpdate#activateUpdate` will return `Promise<boolean>` which resolves to `true` if
an update was activated, to `false` if no new version is available and rejects in case of any error.
`SwUpdate#checkForUpdate` also returns a `Promise<boolean>` which resolves to `true` if an update is available, to `false` if no update was found and rejects in case of any error.
Closes#39840
BREAKING CHANGE: The return type of `SwUpdate#activateUpdate` and `SwUpdate#checkForUpdate` changed to `Promise<boolean>`.
Although unlikely, it is possible that this change will cause TypeScript type-checking to fail in
some cases. If necessary, update your types to account for the new
return type.
DEPRECATED: The `SwUpdate#activated` observable is deprecated.
The `SwUpdate#activated` observable only emits values as a direct response to calling
`SwUpdate#activateUpdate()` and was only useful for determining whether the call resulted in an
update or not. Now, the return value of `SwUpdate#activateUpdate()` can be used to determine the
outcome of the operation and therefore using `SwUpdate#activated` does not offer any benefit.
PR Close#43668
This commits sets the JS target for all command line tools to
NodeJS v12. ESbuild will automatically downlevel the ES2020 features
we currently use to make them compatible with NodeJS v12 <-> ES2019.
ES2020 is the prodmode output, but we still support Node v12 so
there needs to be some downleveling for now.
Note: This is a separate commit because initially the target was
set to Node v14 to match up with the prodmode Bazel output.
PR Close#43431
Updates the service-worker package to export the worker script and
schema, so that these files can be resolved and included by consumers.
Additionally, the CLI bundle is updated to be using ESM as it
transitively relies on the `core` entry-point of the package which
already uses ESM. Also this is required because the script is part
of the the service-worker package which has `type: module` applied.
PR Close#43431
With the changes to support APF v13 in the `ng_package` rule, we have
removed the ambiguous `entry_point` attribute. The attribute suggested
that it would be used for determining the primary entry-point input
file. This was not the case as the flat module output file is consulted
for bundling et at. The attribute has been renamed to match its
purposed (renamed to `primary_bundle_name`).
We no longer need to set that attribute because the primary bundle
name is (1) not of relevance for consumers and (2) the rule already
infers the bundle name properly from the Bazel package.
PR Close#43431
Previously, when a version was found to be broken, any clients assigned
to that version were unassigned (and either assigned to the latest
version or to none if the latest version was the broken one). A version
could be considered broken for several reasons, but most often it is a
response for a hashed asset that eiher does not exist or contains
different content than the SW expects. See
https://github.com/angular/angular/issues/28114#issuecomment-923122967
for more details.
However, assigning a client to a different version (or the network) in
the middle of a session, turned out to be more risky than keeping it on
the same version. For angular.io, for example, it has led to #28114.
This commit avoids making things worse when identifying a broken version
by keeping existing clients to their assigned version (but ensuring that
no new clients are assigned to the broken version).
NOTE:
Reloading the page generates a new client ID, so it is like a new client
for the SW, even if the tab and URL are the same.
PR Close#43518
Previously, a ServiceWorker test was using the
`MockServerState#sawRequestFor()` method. This method, however, only
returns a boolean indicating whether a request had been seen, but would
not throw an error in either case.
Since the intention was to assert that a specific request was made, this
commit switches to the correct `MockServerState#assertSawRequestFor()`
method.
PR Close#43518
Adds support for TypeScript 4.4. High-level overview of the changes made in this PR:
* Bumps the various packages to `typescript@4.4.2` and `tslib@2.3.0`.
* The `useUnknownInCatchVariables` compiler option has been disabled so that we don't have to cast error objects explicitly everywhere.
* TS now passes in a third argument to the `__spreadArray` call inside child class constructors. I had to update a couple of places in the runtime and ngcc to be able to pick up the calls correctly.
* TS now generates code like `(0, foo)(arg1, arg2)` for imported function calls. I had to update a few of our tests to account for it. See https://github.com/microsoft/TypeScript/pull/44624.
* Our `ngtsc` test setup calls the private `matchFiles` function from TS. I had to update our usage, because a new parameter was added.
* There was one place where we were setting the readonly `hasTrailingComma` property. I updated the usage to pass in the value when constructing the object instead.
* Some browser types were updated which meant that I had to resolve some trivial type errors.
* The downlevel decorators tranform was running into an issue where the Closure synthetic comments were being emitted twice. I've worked around it by recreating the class declaration node instead of cloning it.
PR Close#43281
In combination with the TS `noImplicitOverride` compatibility changes,
we also want to follow the best-practice of adding `override` to
members which are implemented as part of abstract classes. This
commit fixes all instances which will be flagged as part of the
custom `no-implicit-override-abstract` TSLint rule.
PR Close#42512
Previously, the ServiceWorker assumed that a client found in
`clientVersionMap` would exist (i.e. it could be retrieved via
`clients.get()`). However, if a browser tab had been closed, the
corresponding client (while present in `clientVersionMap`, which is only
updated on ServiceWorker initialization) would not be retrievable via
`clients.get()`.
This commit fixes it by checking whether the client exists before trying
to notify it about an unrecoverable state.
PR Close#42736
Previously, we used custom typings for the ServiceWorker environment.
This was necessary back when the ServiceWorker package was introduced,
since there were no official typings.
Since there are now official typings for Web Workers (including
ServiceWorkers) offered by TypeScript as [lib.webworker.d.ts][1], this
commit gets rid of our custom typings in favor of using the official
ones.
[1]: https://github.com/microsoft/TypeScript/blob/v4.3.4/lib/lib.webworker.d.ts
PR Close#42736
This commit better aligns the mock `ServiceWorkerGlobalScope`
implementation used in ServiceWorker tests (and the associated typings)
with the actual implementation (and the official TypeScript typings).
This allows verifying the ServiceWorker behavior in a slightly more
realistic environment.
This is in preparation of switching from our custom typings to the
official TypeScript typings (`lib.webworker.d.ts`).
PR Close#42736
This commit better aligns the mock client implementations used in
ServiceWorker tests (and the associated typings) with the actual
implementations (and the official TypeScript typings). This allows
verifying the ServiceWorker behavior in a slightly more realistic
environment.
This is in preparation of switching from our custom typings to the
official TypeScript typings (`lib.webworker.d.ts`).
PR Close#42736
In the ServiceWorker tests, we use mock implementations of the various
client APIs that the ServiceWorker interacts with. Previously, these
mock implementations were defined in the `testing/scope.ts` file. This
added several extra classes to a file that already contains a few,
making it harder to maintain.
Therefore, this commit moves these mock client classes to a separate
`testing/clients.ts` file.
PR Close#42736
This commit better aligns the mock event implementations used in
ServiceWorker tests (and the associated typings) with the actual
implementations (and the official TypeScript typings). This allows
verifying the ServiceWorker behavior in a slightly more realistic
environment.
This is in preparation of switching from our custom typings to the
official TypeScript typings (`lib.webworker.d.ts`).
PR Close#42736
In the ServiceWorker tests, we use mock implementations of the various
events emitted during the ServiceWorker lifecycle. Previously, these
mock implementations were defined in the `testing/scope.ts` file. This
added several extra classes to a file that already contains a few,
making it harder to maintain.
Therefore, this commit moves these mock event classes to a separate
`testing/events.ts` file.
PR Close#42736
This commit makes the `SwTestHarness.envIsSupported()` static method a
standalone function. This function is used to determine whether the
current environment provides the necessary APIs to run the SW tests and
is independent of `SwTestHarness`, so is no need for it to be a static
method of `SwTestHarness`.
This is in preparation of switching from our custom typings to the
official TypeScript typings (`lib.webworker.d.ts`).
PR Close#42736
This commit removes the duplicate `Context` interface and uses the
`ExtendableEvent` interface instead.
This is in preparation of switching from our custom typings to the
official TypeScript typings (`lib.webworker.d.ts`).
PR Close#42736
The ServiceWorker needs to keep track of some metadata for unhashed
asset resources to know if/when they might need to be revalidated. This
applies to resources that do not exist on the filesystem at build-time
(and thus cannot be hashed), such as fonts or images loaded from
external sources. For hashed resources, this metadata is irrelevant,
because the hash is enough to verify that the content hasn't changed and
no revalidation is necessary.
Previously, the ServiceWorker would store such metadata for hashed
resources as well, even though it would never be used (thus taking up
space unnecessarily).
This commit fixes it by not storing metadata for hashed resources, i.e.
those that are included in an asset-group's `hashes` array.
PR Close#42606
Previously, the SW was only able to clean up caches for app-versions
found in the `Driver`'s `versions` map. If (for some reason) the
`Driver` failed to load a valid stored state (including app-versions)
and ended up with an [empty `versions` map][1], any obsolete versions
would remain in the cache storage. This case was rare but possible.
This commit makes the cache clean-up logic more robust by ensuring that
all app-version caches are removed unless they are currently used by the
SW to serve active clients (with the exception of the latest
app-version, which is always retained).
Fixes#41728
[1]: https://github.com/angular/angular/blob/9de65dbdceac3077881fbc49717f33d0f379e21d/packages/service-worker/worker/src/driver.ts#L515-L529
PR Close#42622
Previously, obsolete caches were only cleaned up when successfully
loading the stored state. When the state failed to be loaded, cleaning
up the caches would be skipped until the next SW initialization.
This commit changes this, ensuring that the caches are cleaned up
regardless if the stored state was loaded successfully or not.
PR Close#42622
This commit simplifies/systemizes accessing the `CacheStorage` through a
wrapper, with the following benefits:
- Ensuring a consistent cache name prefix is used for all caches
(without having to repeat the prefix in different places).
- Allowing referring to caches using their name without the common
cache name prefix.
- Exposing the cache name on cache instances, which for example makes it
easier to delete caches without having to keep track of the name used
to create them.
PR Close#42622
This commit improves the cache names generated by the ServiceWorker by
making them shorter and non-repetitive. In particular, the following
changes are made:
- Data-group cache names no longer include the `dynamic` infix, since it
does not add any value.
Before: `ngsw:<...>:data:dynamic:<...>`
After: `ngsw:<...>:data:<...>`
- `CacheDatabase` table names no longer include the `ngsw:<path>` prefix
twice.
Before: `ngsw:<path>:db:ngsw:<path>:<...>`
After: `ngsw:<path>:db:<...>`
NOTE 1:
This change will result in different cache names being generated for the
same app-versions with the new SericeWorker script. This means that some
of the previously cached data will need to be re-downloaded (because the
ServiceWorker will not be able to re-use the old caches), but that
should be transparent for the end user.
While possible, adding logic to allow the ServiceWorker to retrieve data
from the old caches is not worth the extra complecity and maintenance
cost.
NOTE 2:
Generating different cache names for some of the caches means that the
ServiceWorker will not be able to clean-up some of the old caches. This
will be taken care of in a subsequent commit that will rework the
clean-up logic to be more robust (covering changes such as this one and
other edgecases).
PR Close#42622
`CacheDatabase` uses the un-prefixed table names to interact with
database tables. However, the `list()` method returns the raw, prefixed
table names (which are not useful, since they cannot be used to
open/delete a table).
This commit fixes this by removing the prefix from the cache names
returned by the `list()` method.
NOTE:
This method is currently not used anywhere, so this change does not
affect the ServiceWorker behavior.
PR Close#42622
This commit includes the ServiceWorker version in the debug info shown
at `/ngsw/state` to make it easier to know what version of the
ServiceWorker script is controlling the page.
PR Close#42622
This commit makes some minor refactorings to improve the code
readability and maintainability, including:
- Avoiding code duplication.
- Using more descriptive variable names.
- Using `async/await` instead of `Promise#then()`.
- Accessing variables directly instead of via `this` when possible.
PR Close#42622
Add `openWindow`, `focusLastFocusedOrOpen` and `navigateLastFocusedOrOpen` abilty to the notificationclick handler such that when either a notification or notification action is clicked the service-worker can act accordinly without the need for the app to be open
PR Close#26907
PR Close#42520
The ServiceWorker assigns an app-version to a each client to ensure that
all subsequent requests for a client are served using the same
app-version. The assignment is done based on the client ID.
Previously, the ServiceWorker would only try to read the client's ID off
of the `FetchEvent`'s `clientId` property. However, for navigation
requests the new client's ID will be set on [resultingClientId][1],
while `clientId` will either be empty or hold the ID of the client where
the request initiated from. See also related discussions in
w3c/ServiceWorker#870 and w3c/ServiceWorker#1266.
In theory, this could lead to the navigation request (i.e. `index.html`)
being served from a different app-version than the subsequent
sub-resource requests (i.e. assets). In practice, the likelihood of this
happening is probably very low though, since it would require the latest
app-version to be updated between the initial navigation request and the
first sub-resource request, which should happen very shortly after the
navigation request.
This commit ensures that the correct client ID is determined even for
navigation requests by also taking the `resultingClientId` property into
account.
[1]: https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/resultingClientId
PR Close#42607
Switches the repository to TypeScript 4.3 and the latest
version of tslib. This involves updating the peer dependency
ranges on `typescript` for the compiler CLI and for the Bazel
package. Tests for new TypeScript features have been added to
ensure compatibility with Angular's ngtsc compiler.
PR Close#42022
Update the supported range of node versions for to be less restrictive, no longer causing
yarn or npm to fail engine's checks for future versions of node.
While this change will no longer cause yarn or npm to fail these engine's check, this does
not reflect a change in the officially supported versions of node for Angular. Angular
continues to maintain support for Active LTS and Maintenance LTS versions of node.
PR Close#42205
Remove publishConfig property from the package.json entry for each of the entries in
the publish configuration. Using the wombat proxy is now ensured/managed by the
ng-dev release tooling.
PR Close#42104
`global` property is not available in the browser, previously this was polyfilled through `core-js`. This now fails with `global is not defined`, since global cannot be accessed when not defined.
PR Close#41739
This commit reverts commit [_fix(service-worker): handle error with
ErrorHandler_](https://github.com/angular/angular/pull/39990/commits/552419d).
With Angular v11.0.4 and commit [_fix(service-worker): handle error with
ErrorHandler_](https://github.com/angular/angular/pull/39990/commits/552419d)
Angular start to send all service worker registration errors to the Angular
standard `ErrorHandler#handleError()` interface, instead of logging them in the
console.
But users existing `ErrorHandler#handleError()` implementations are not adapted
to service worker registration errors and it might result in broken apps or
bad UI.
Passing to `ErrorHandler` is desirable for some and undesirable for others and
the same is true for passing to `console.error()`.
But `console.error()` was used for a long time and thus it is preferable to keep
it as long as a good solution is not found with `ErrorHandler`.
Right now it's hard to define a good solution for `ErrorHandler` because:
1. Given the nature of the SW registration errors (usually outside the control
of the developer, different error messages on each browser/version, often
quite generic error messages, etc.), passing them to the `ErrorHandler` is
not particularly helpful.
2. While `ErrorHandler#handleError()` accepts an argument of type `any` (so
theoretically we could pass any object without changing the public API), most
apps expect an `Error` instance, so many apps could break if we changed the
shape.
3. Ideally, the Angular community want to re-think the `ErrorHandler` API
and add support for being able to pass additional metadata for each error
(such as the source of the error or some identifier, etc.). This change,
however, could potentially affect many apps out there, so the community must
put some thought into it and design it in a way that accounts for the needs
of all packages (not just the SW).
4. Given that we want to more holistically revisit the `ErrorHandler` API, any
changes we make in the short term to address the issue just for the SW will
make it more difficult/breaky for people to move to a new API in the future.
To see the whole explanation see GitHub PR #40236.
PR Close#40236
Previously, the SW would wait to become idle before executing scheduled
tasks (including checks for newer app versions). It was considered idle
when it hadn't received any request for at least 5 seconds. As a result,
if the app performed polling (i.e. sent requests to the server) in a
shorter than 5 seconds interval, the SW would never detect and update to
a newer app version.
Related issue: #40207
This commit fixes this by adding a max delay to `IdleScheduler` to
ensure that no scheduled task will remain pending for longer than the
specified max delay.
PR Close#40234