This commit updates a flaky test to increase the amount of work (more `for` loop iterations) to minimize the chance of getting the same timestamp after that work.
PR Close#59653
This commit updates the `fetch` patch for zone.js. Currently, we're attaching an
`abort` event listener on the signal (when it's provided) and never removing it.
We should be good citizens and remove event listeners whenever objects need to be
properly collected. In Firefox, when saving a heap snapshot and running it through
`fxsnapshot`, querying `AbortSignal` will print a so-called "CaptureMap" with a list
of "lambdas," indicating that the signal is not garbage collected because of the event
listener lambda function.
PR Close#57882
From the internal issue on the matter:
> When using the standard Jasmine version of it promises returned by the body function are automatically awaited. The Catalyst version of it is fake-async, so awaiting the promise does not make sense; however it would be nice if Catalyst automatically flushed the promise to replicate the experience of using standard it. This would allow users to do the following:
```
it('should fail later', async () => {
await new Promise(r => setTimeout(r));
fail('failure');
});
```
> In Catalyst today the above test will pass. If this proposal to automatically flush the resulting promise were implemented it would fail.
Flushing after the tests complete has been the default behavior inside
Google since 2020. Very few tests remain that use the old behavior of
only flushing microtasks. The example above would actually fail with
`fakeAsync` due to the pending timer, but the argument still remains the
same. We might as well just flush if we're going to fail the test
anyways by throwing if there's no flush at the end.
BREAKING CHANGE: `fakeAsync` will now flush pending timers at the end of
the given function by default. To opt-out of this, you can use `{flush:
false}` in options parameter of `fakeAsync`
PR Close#57240
The `Timeout` object in Node.js has a `refresh` method, used to restart `setTimeout`/`setInterval` timers. Before this commit, `Timeout.refresh` was not handled, leading to memory leaks when using `fetch` in Node.js. This issue arose because `undici` (the Node.js fetch implementation) uses a refreshed `setTimeout` for cleanup operations.
For reference, see: 1dff4fd9b1/lib/util/timers.js (L45)Fixes: #56586
PR Close#56852
Prior to this commit, a memory leak occurred when the `abort` listener was
not removed from the `AbortSignal`. We introduced a fix to remove the event
listener, but it was erroneously stored on the `taskData`, which is a shared
global object. Consequently, when something attempted to remove an event listener,
it immediately removed the last stored abort listener. As a result, events would
never be canceled.
We have now rectified this by storing the remove abort listener function directly on
the task itself. This adjustment ensures that the abort listener is tied only to the
specific task. When the `abort` function is called, it cancels the task. Therefore, it
is safe to associate the cleanup function directly with the task.
Closes: #56148
PR Close#56160
Prior to this commit, event listener options were mutated directly, for example,
`options.signal = undefined` or `options.once = false`.
This issue arises in apps using third-party libraries where the responsibility lies
with the library provider. Some libraries, like WalletConnect, pass an abort controller
as `addEventListener` options. Because the abort controller has the `signal` property,
this is a valid case. Thus, mutating options would throw an error since `signal`
is a readonly property.
Even though zone.js is being deprecated as Angular moves towards zoneless change detection,
this fix is necessary for apps that still use zone.js and cannot migrate to zoneless change
detection because they rely on third-party libraries and are not responsible for the code
used in them.
Closes#54142
PR Close#55796
This was a bit complicated, but the typings test (`packages/zone.js/test/typings/...`) was failing due to an unresolved import on `./zone-impl`.
The main cause is that `//packages/zone.js:zone_js_d_ts` was generating the output `zone.d.ts` file by _concatenating_ `zone.d.ts` with `zone.api.extensions.d.ts` and `zone.configurations.api.d.ts`. Now that `zone.d.ts` imports `zone-impl.d.ts`, concatenation is no longer a viable means of bundling this content.
To fix this, I created a new `packages/zone.js/zone.ts` entry point and imported the underlying `zone.ts` file as well as the two extensions. I added `extract_types` to pull the `*.d.ts` files out of the target (because all the JS is bundled separately) and used those files in the final NPM package. This is sufficient to pass the typings test and should be equivalent to what exists today.
PR Close#53443
Not sure why this becomes a problem now. These errors appear to be preexisting. Most likely the file wasn't loaded previously and now it is.
PR Close#53443
While `TaskTrackingZoneSpec` was implicitly global previously, it does not need to be exposed in a `declare global {}` block. This is because classic scripts in TypeScript are only implicitly global within the same compilation. `TaskTrackingZoneSpec` was not exposed in the existing `.d.ts` files shipped with the `zone.js` package. Within google3, this is also a separate compilation and was not accessible. As a result, `TaskTrackingZoneSpec` was always private and we do not need a global to maintain compatibility.
PR Close#53443
While `ProxyZoneSpec` was implicitly global previously, it does not need to be exposed in a `declare global {}` block. This is because classic scripts in TypeScript are only implicitly global within the same compilation. `ProxyZoneSpec` was not exposed in the existing `.d.ts` files shipped with the `zone.js` package. Within google3, this is also a separate compilation and was not accessible. As a result, `ProxyZoneSpec` was always private and we do not need a global to maintain compatibility.
PR Close#53443
Since each patch no longer contains top-level side effects, each bundled entry point needs to import and call its associated patch. For the most part this just means that each entry point imports the associated patch and invokes it at the top-level scope.
Note that many of these entry points did not actually have a dependency on `Zone` and had no guarantee that it was loaded prior to execution. To maintain consistency, the missing dependencies on `Zone` are left as-is. They will use the global instance of `Zone` and if users fail to load it prior to importing a specific patch, then the patch will fail just as it did previously.
PR Close#53443
This commit updates the implementation of the `customElements` patch and also
patches FACE callbacks (`formAssociatedCallback`, `formDisabledCallback`, `formResetCallback`
and `formStateRestoreCallback`). This now allows invoking those callbacks in the same zone
where the custom element has been defined.
PR Close#50686
This commit updates the implementation of the zone.js `fs` patch to
restore the implementation of `realpath.native` and patches it as a macrotask,
along with other functions of the `fs` package. This is the only nested function
that must be patched.
Closes: #45546
PR Close#54208
This commit updates the implementation of the `fetch` patch and additionally
patches `Response` methods which return promises. These are `arrayBuffer`, `blob`,
`formData`, `json` and `text`. This fixes the issue when zone becomes stable too early
before all of the `fetch` tasks complete. Given the following code:
```ts
appRef.isStable.subscribe(console.log);
fetch(...).then(response => response.json()).then(console.log);
```
The `isStable` observer would log `false, true, false, true`. This was happening because
`json()` was returning a native promise (and not a `ZoneAwarePromise`). But calling `then`
on the native promise returns a `ZoneAwarePromise` which notifies Angular about the task
being scheduled and forces to re-calculate the `isStable` state.
Issue: #50327
PR Close#50653
Currently the ZoneJS typing tests executes outside of Bazel, as a legacy
artifact of the monorepo merging (as it seems - not ideal at all).
Looks like this test relies on its own node modules, that were NOT
locked using a yarn lock file. This commit adds one, and specifically
locks it to a `@types/node` version that does not include the most
recent patch release (which seemingly introduced a breaking change)
that causes issues with TypeScript's lib checking.
Whenever we perform lock file maintenance in the future, we have the
following options:
- Consider disabling lib checking via `skipLibCheck` for this test. This
may be acceptable.
- Continue locking the node version,
- Waiting for chokidar to comply with the new signature
- Waiting for the breaking change to be rolled back.
Culprit change:
https://github.com/DefinitelyTyped/DefinitelyTyped/pull/68300
PR Close#54048
Make Zone.js compatible with moduleDetection:force by turning files that
are currently incompatible from scripts into modules using an empty
export statement.
PR Close#53445
fetch support AbortSignal, zone.js schedules a macroTask when fetch()
```
fetch(..., {signal: abortSignal});
```
we should also be able to cancel fetch with `zoneTask.cancel` call.
So this commit create an internal AbortSignal to handle
`zoneTask.cancel()` call and also delegate the `options.signal` from the
user code.
PR Close#49595
Close#49591
```
const ac = new AbortController();
addEventListener(eventName, handler, {signal: ac.signal);`
ac.abort();
```
Currently `zone.js` doesn't support the `signal` option, this PR allows
the user to use AbortContoller to remove the event listener.
PR Close#49595
The `nodejs-websocket` package has been replace with the `ws` package.
Both provide `WebSocket` server support and both of zero transitive
dependencies. However, the `ws` package has ~78 million weekly downloads
and was last updated this week (as of the writing of this commit) while
the `nodejs-websocket` package has ~7,600 weekly downloads and was last
update 5 years ago. The `ws` package is also already a transitive dependency
of the repository which allows for a reduction in the total dependency count
for the repository.
PR Close#53482
In the original `Promise` impelmentation, zone.js follow the spec from
https://promisesaplus.com/#point-51.
```
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(p1);
p1 === p2; // false
```
in this case, `p2` should be the same status with `p1` but they are
still different instances.
And for some edge case.
```
class MyPromise extends Promise {
constructor(sub) {
super((res) => res(null));
this.sub = sub;
}
then(onFufilled, onRejected) {
this.sub.then(onFufilled, onRejected);
}
}
const p1 = new Promise(setTimeout(res), 100);
const myP = new MyPromise(p1);
const r = await myP;
r === 1; // false
```
So in the above code, `myP` is not the same instance with `p1`,
and since `myP` is resolved in constructor, so `await myP` will
just pass without waiting for `p1`.
And in the current `tc39` spec here https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-promise-resolve
`Promise.resolve(subP)` should return `subP`.
```
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(p1);
p1 === p2; // true
```
So the above `MyPromise` can wait for the `p1` correctly.
PR Close#53423
Before this commit, zone.js wraps the uncaught promise rejection error
to a new Error object includes more information such as Zone stack
traces. This feature is provided from the very beginning of Zone.js,
but this feature becomes very annoying and make the user difficult to
debug.
So this commit disable this wrapping behavior by default, and user can
enable this feature back by setting
`DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION` to `false`.
PR Close#52492
`globalThis` global property contains the global `this` value, which is usually akin to the global object. This is needed for better compatibility with CloudFlare workers were global nor window are defined as globals.
PR Close#52367
A lot of our tests are wrapped in `{}` which serves no purpose, aside from increasing the nesting level and, in some cases, causing confusion. The braces appear to be a leftover from a time when all tests were wrapped in a `function main() {}`. The function declaration was removed in #21053, but the braces remained, presumably because it was easier to search&replace for `function main()`, but not to remove the braces at the same time.
PR Close#52239
This commit removes access to deep imports and `zone-testing-bundle` and `zone-testing-node-bundle`
This commit removed access to deep and legacy `dist` imports. `zone-testing-bundle` and `zone-testing-node-bundle` are also no longer generated.
BREAKING CHANGE:
Deep and legacy `dist/` imports like `zone.js/bundles/zone-testing.js` and `zone.js/dist/zone` are no longer allowed. `zone-testing-bundle` and `zone-testing-node-bundle` are also no longer part of the package.
The proper way to import `zone.js` and `zone.js/testing` is:
```js
import 'zone.js';
import 'zone.js/testing';
```
PR Close#51752
Currently internally Angular has some customized tsconfig files, because we don't align with the tsconfig of the rest of g3. These changes enable `noImplicitReturns` and `noPropertyAccessFromIndexSignature` to align better with the internal config.
PR Close#51728
This is needed to better support native ESM modules and avoid the otherwise necessary deep imports like `zone.js/fesm2015/zone-node.js` due to disallowed directory imports.
PR Close#51652