This forwards the `AbortSignal` natively supplied by Chrome 153.0.8009.0's update to `registerTool` executions down into the Angular context. This helps tools gracefully handle execution cancellations initiated by agents or users, preventing unnecessary background work and leaking resources.
We combine the injector/component destruction abort signals with the native WebMCP execution cancellation signals so the underlying tools correctly cancel long-running requests without producing invalid states.
Justification:
https://groups.google.com/a/chromium.org/g/chrome-ai-dev-preview/c/9291sjhIRz0/m/RFuyRrs5AAAJ
Introduces support for route-level resources via the `resources` property on route definitions, enabled with `withRouterResources()`.
Router resources provide a reactive, signal-based alternative to resolvers, allowing routes to declare resources tied to route parameters and route lifecycle:
- The `resources` function executes during navigation transitions within an injection context scoped to the route (`_localInjector`).
- It receives a `ResourceContext` containing signals for `params`, `queryParams`, `fragment`, and `data`, alongside the static `snapshot`.
- For newly created routes, `resources` runs once and attaches to `ActivatedRoute.resources`. For reused routes, parameter signals update reactively to trigger new data fetches while keeping resource references stable.
- Wrapped resources (`routerResource`) provide transactional stability: snapshots are frozen during active navigations to prevent UI jitter, unfreezing on `NavigationEnd`.
- On cancelled navigations or errors, rollback recovery retains the frozen snapshot until reverted signals settle, avoiding flashes of loading state. Manual reloads are rejected while frozen.
- Local injectors are automatically cleaned up if navigations are cancelled or rolled back.
Note that this commit only implements non-blocking resources (marked with `nonBlocking()`), deferring blocking resource resolution to future work to keep the initial changeset smaller and less complex.
PR Close#70211
Defer blocks that ask to run during browser idle time with the same
options get grouped into one batch ("bucket") and processed together
the next time the browser is idle. While that batch runs, if one
callback (or the change-detection check right after it) asks to
schedule more idle work for that same batch, Angular used to register
a second idle-callback handle instead of reusing the one already in
flight.
That second handle became orphaned. By the time the batch finished
running, its queue was empty, so Angular threw away the bookkeeping
for it — including the only reference that could have cancelled the
handle. If the app was destroyed before the browser got around to
calling it, it fired anyway, against a scheduler that no longer
existed.
The root cause was a marker (idleId) that IdleScheduler uses to know
"I already have a browser callback pending for this batch, don't
request another one." That marker was being cleared to null right at
the start of processing a batch, before any of its callbacks had
actually run. So a callback that re-entrantly asked to schedule more
work mid-batch saw "nothing pending" and requested a redundant handle.
The fix: leave the marker set for the entire batch instead of clearing
it up front. Only clear it once every callback in the batch has run —
at that point, if there's leftover work, it's safe to request a fresh
handle for it.
Added a test for the basic re-entrant scenario, plus five more for
edge cases: a re-entrant add() into a different batch, and re-entrant
remove() of a sibling callback that hasn't run yet versus one that
already has.
Before: getSignalGraph only exposed debuggableFn on computed and template
nodes, preventing DevTools from linking to source code for other node types.
After: debuggableFn is also populated for linkedSignal, effect, and
afterRenderEffectPhase nodes.
We keep getting PRs that target single usages of `hasOwnProperty` and we have ~100 of them. These changes aim to address the issue centrally by swapping out all the instances and adding a lint rule against introducing new ones.
When a view has more than one effect scheduled to run, and one of them
destroys the view (e.g. by calling `componentRef.destroy()`), the
remaining effects in that same flush could still run afterward,
against a view that no longer exists. In some cases this crashed
outright with `TypeError: view[EFFECTS] is not iterable`.
Here's why: `runEffectsInView` walks a view's effects in a `for...of`
loop, wrapped in an outer `while` loop that re-checks for any effects
that became dirty as a side effect of ones that already ran. When an
effect destroys its view, `view[EFFECTS]` gets set to `null` as part
of tearing the view down.
First attempt checked for that inside the `for...of` loop, before each
effect runs. That covers a sibling effect later in the *same* pass,
but misses a second case: if the effect that destroys the view *also*
dirties another effect on that same view in the process (e.g. by
writing a signal the sibling depends on), the outer `while` loop sees
`HasChildViewsToRefresh` set and tries to restart — and immediately
crashes re-entering `for (const effect of view[EFFECTS])` on a
now-null value, before the in-loop check ever gets a chance to run.
Reproduced that exact crash with a test first: two effects on one
view, the second one writes a signal the first depends on and then
destroys the view in the same call — confirmed it throws before
touching the fix.
Fixed by checking right after `effect.run()` instead of before it,
covering both cases in one place: the remaining effects in the current
pass, and the loop trying to restart afterward. As soon as one effect
destroys the view, nothing else runs against it again.
This is intentionally narrow in scope. An earlier version of this fix
also tried to guarantee that `onCleanup()` callbacks still ran even
when registered after an effect destroyed its own view. That's been
dropped — destroying your own view and then continuing to register
more work for it isn't something the framework should have to paper
over. If you need to do both, register `onCleanup` first, then
destroy.
Before: The View Source button tooltip in signal details static ('View source') even when disabled.
After: The tooltip explains 'Source location is not available for this node' when disabled.
Before: getSignalGraph only exposed debuggableFn on computed and template
nodes, preventing DevTools from linking to source code for other node types.
After: debuggableFn is also populated for linkedSignal, effect, and
afterRenderEffectPhase nodes.
locateOrCreateElementNodeImpl looks up the DOM node for an element
during hydration and immediately checks its nodeType. If the
client-rendered DOM has fewer nodes than the server-rendered HTML,
the lookup returns null, and in production that null flows straight
into the nodeType check and crashes with a raw, uncoded
"Cannot read properties of null (reading 'nodeType')" TypeError.
The dev-mode check that would normally catch this (validateMatchingNode)
already handles a missing node, but it's compiled out of production
builds, so the crash only shows up outside of dev mode.
Add a null check ahead of the nodeType check that throws a coded
RuntimeError using the existing HYDRATION_MISSING_NODE (NG0502) code,
with a descriptive message in dev mode and a cheap fallback in
production. Also add a regression test that removes a server-rendered
element before hydration runs and asserts a coded RuntimeError is
thrown instead of a raw TypeError.
Angular's internal LView/TNode bookkeeping can get out of sync with the
real DOM: manual DOM manipulation, a browser extension, or an edge case in
Angular's own view-insertion/reordering code can all leave Angular believing
a node is still attached at a given position when it isn't. The next time
Angular's renderer calls `insertBefore` relative to that stale reference
node, the native DOM API throws an opaque `NotFoundError` with no indication
of which node or component was involved, making these errors effectively
undebuggable in production:
NotFoundError: Failed to execute 'insertBefore' on 'Node': The node
before which the new node is to be inserted is not a child of this node.
at Node.insertBefore (native)
at DefaultDomRenderer2.insertBefore (packages/platform-browser/src/dom/dom_renderer.ts)
at nativeInsertBefore (packages/core/src/render3/dom_node_manipulation.ts)
at nativeAppendOrInsertBefore (packages/core/src/render3/dom_node_manipulation.ts)
... (called while Angular inserts or moves a view during change detection)
Check the reference node's actual parent against the expected parent before
calling the native `insertBefore`, and throw a descriptive `RuntimeError`
(NG05106) instead, following the same pattern already used for hydration
node mismatches.
Exposes the internal fn reference of effect reactive nodes on DebugSignalGraphNode as debuggableFn. This allows Angular DevTools to inspect and set breakpoints on effect callbacks.
When reflection metadata is emitted via setClassMetadata, passing readonly arrays or const tuples for the decorators parameter causes TypeScript type checking errors because setClassMetadata previously expected decorators to be a mutable any[] or null.
This change updates the setClassMetadata type signature to accept decorators as readonly any[] or null and casts the parameter internally when mutating the class metadata property.
When components pass arrays or readonly tuples to decorator metadata fields (such as ), typechecking generated decorator reflection metadata causes a TS2322 type mismatch error because previously only accepted mutable .
This change updates to accept , allowing arrays and readonly tuples to be assigned without TypeScript compilation errors.
When hydration locates the DOM node for an ɵɵelementStart/ɵɵdomElementStart
instruction, locateOrCreateElementNodeImpl assumed the located node was
always an Element and called hasSkipHydrationAttrOnRElement(native), which
does native.hasAttribute(...). The check that would normally catch this
class of mismatch, validateMatchingNode, is gated behind `ngDevMode &&` and
is compiled out of production builds. So when a real SSR/hydration
structural mismatch located a Text or Comment node instead of the expected
Element, production builds hit .hasAttribute on a node type that doesn't
have it and crashed with a raw, uncoded TypeError instead of a coded
hydration-mismatch RuntimeError.
Add a cheap, always-on nodeType check ahead of that call. On mismatch it
throws RuntimeError(HYDRATION_NODE_MISMATCH, ngDevMode && '...'), the same
pattern used elsewhere in the codebase, so the descriptive message is only
built in dev mode and production keeps throwing just the bare NG0500 code
without pulling validateMatchingNode's DOM-printing machinery into the
production bundle (verified via the bundling/hydration golden-symbols test,
which is unchanged).
animate.leave waits for the longest-running animation before removing an element. When multiple animations use the same keyframe name, their animationend events have the same animationName, so a shorter animation can be mistaken for the longest one and remove the element too early.
Track the longest animation duration returned by getAnimations() and compare it with the duration from event.animation when available. Keep the existing name/property checks as a fallback for older browsers and computed-style detection.
Allow a 1ms tolerance for rounding differences between CSSOM and Web Animations and add a regression test using values equivalent to fractional calc() durations.
`getTIcu` and `setTIcu` performed `hasOwnProperty` checks with string
literals (`'currentCaseLViewIndex'` and `'tView'`). Under Closure Compiler
property renaming optimizations, these properties are minified but the
string literals are not, causing property lookups and type discriminations
to fail.
This commit defines closure-safe property constants using
`getClosureSafeProperty` so the property names are renamed consistently.
Use the host element's namespace-aware local name when checking for script
elements. A prefixed SVG script can expose a qualified tag name such as
"x:script" while its local name remains "script".
After incremental hydration became tree-shakable, application bootstrap could finish before a routed component activated the runtime. The one-time trigger scan was then skipped, leaving routed and nested hydration triggers uninitialized.
Coordinate application bootstrap with runtime activation and initialize once both have occurred.
Fixes#69908
Report unsupported style property binding values in development mode while preserving existing binding behavior. Unwrap trusted style values before appending unit suffixes and link NG0318 warnings to the corresponding error guide.
Make runtime URL sanitizer selection namespace-aware so SVG and MathML host bindings match the security schema.
Cover SVG href/xlink:href and MathML href host binding cases, including dynamic hostElement resolution.
PR Close#69558
Host binding sanitization previously used the declaring directive or component selector to choose a compile-time security context. The same host binding can execute on a different concrete element through hostDirectives, inherited host bindings, dynamic directives, or createComponent hostElement usage.
Compute host binding security contexts against possible concrete hosts and defer URL versus ResourceURL selection to runtime when necessary. Resolve dynamic root host TNodes to their native tag before sanitizer and security-sensitive attribute checks.
Fixes angular#69550
PR Close#69558
The control flow migration determines whether an `*ngIf` uses a `then`
and/or `else` clause by regex matching the raw microsyntax string for
the literal keywords `then`/`else`. The regexes only checked that the
keyword was preceded by a non-word character, but not that it was
followed by one.
As a result, a template reference name that merely starts with `then`
(e.g. `else thenBlock`) or `else` was misidentified as the `then`/`else`
keyword itself. This caused the migration to take the wrong code path
(e.g. then+else instead of else-only), which in turn made
`getTemplateName()` compute a `slice(start, end)` with `start > end`,
producing an empty template name. That empty placeholder was never
resolved and was silently emitted as an invalid
`<ng-template [ngTemplateOutlet]=""></ng-template>`, dropping the
original template content without any warning.
Add a negative lookahead `(?![\w\d])` to both regexes so `then`/`else`
are only matched as whole keywords, not as a prefix of a longer
template reference name.
Fixes#69914
Currently, the SVG sanitizer checks a static set of candidate attribute
names (`attributeName` and `attributename`). This approach misses other case
variations (such as `attributenAme` or others), which could potentially
bypass sanitization when binding sensitive attributes like `href` on
`<set>` or `<animate>` elements.
This change retrieves all attribute names of the SVG element, performs a
case-insensitive comparison with `'attributename'`, and sanitizes the value
if a match is found.
When only some NgClass usages are migrated (partial migration),
the NgClass import should be preserved in the module/component
imports if it is still used elsewhere.
If a directive has an input declared as `dismissible = input<boolean>(true, {transform: booleanAttribute});` then the following templates were not compiling:
```
<div directiveName dismissible="true"></div>
<div directiveName dismissible></div>
```
This commit fixes the issue, without breaking contravariant consumers.
Move the DebugSignalGraph, DebugSignalGraphEdge, and DebugSignalGraphNode interfaces from packages/core/src/render3/util/signal_debug.ts into the packages/core/primitives/devtools/src package. This decouples the signal graph debug types from runtime render3 utilities and allows devtools and internal core tooling to import them directly from primitives as type-only exports.
In the WebMCP specification, tools are unregistered by aborting the `AbortSignal` provided in `registerTool(tool, {signal})`. The deprecated `unregisterTool` method on `ModelContext` is no longer needed or part of the standard, and is now removed from the TypeScript interface definition.
This is a follow-up to #67997, which allowed explicit read generics with input transforms, such as `input<boolean>(false, {transform: booleanAttribute})`.
That fixed the declaration, but static template attributes like `dismissible="true"` and bare `dismissible` were still checked as strings against the read type. Allow the fallback write type to include static attribute strings so these template forms compile.
When a parent form element defines an async validator, its resource's `params` function needs to evaluate `syncValid()`, which causes unvisited child form nodes to be lazily instantiated. If any of these lazily instantiated child nodes also define an async validator, their resource is initialized while the parent's `params` function is still evaluating. This incorrectly triggers Angular core's `NG0992` guard (`Cannot create a resource inside the params of another resource`).
This commit exports `ɵsetInParamsFunction` and `ɵisInParamsFunction` from `@angular/core` and uses them in `FieldMetadataState.runMetadataCreateLifecycle` to explicitly detach the lazy creation of form metadata from the parent's reactive `params` context.
fixes#69620
Prepend generated view scope variables to `view.create` in addition to
`view.update` so that expressions evaluated during creation (such as
foreign component property bindings) can resolve context variables from
parent views when nested inside control flow blocks (`@if`, `@switch`,
`@for`). This is necessary to support binding properties to foreign
components inside control flow blocks.
reflect this broader behavior.)
PR Close#69674
Adds build-time validation to catch broken, stale, or miscased internal documentation links in both JSDoc and markdown, including `/api/` and `/guide/` URLs and their fragments. Updates the documentation pipeline to share the canonical route manifest, ensuring that all references are checked against the current navigation structure.
Extract the AI tool definition interfaces from the core debug module to the devtools primitives module. This relocates ToolDefinition, ToolGroup, and DevtoolsToolDiscoveryEvent to the primitives folder, exposing them cleanly via "export type" in the devtools entry point, improving module organization and readability.