Add the runtime primitives `ɵɵboundaryCreate` and `ɵɵboundaryUpdate` to
the core instructions, which handle synchronous view destruction and
provide the `ON_ERROR` interceptor hooks.
Also include the initial compiler AST representations for the new syntax
including the Lexer tokenization and HTML Parser integration. This lays
the foundational structure for `@boundary` prior to code generation.
Co-authored-by: Matthieu Riegler <kyro38@gmail.com>
PR Close#70463
Implement error interception during refreshView and provide onError callback options in ViewContainerRef for programmatic rendering and encapsulation of boundary errors.
PR Close#70463
The static `ɵprov` field emitted on `@Injectable()` classes uses `ɵɵInjectableDeclaration<T>`.
When a subclass extends a generic `@Injectable()` base class with contravariant parameters
(such as callback/transformer methods depending on generic type parameters), TypeScript's
static side inheritance check (`typeof Sub extends typeof Super`) fails with `TS2417` because
`ɵɵInjectableDeclaration<Sub>` is not assignable to `ɵɵInjectableDeclaration<Super<any>>`.
Using `any` (`o.DYNAMIC_TYPE`) in `createInjectableType` avoids strict variance checks on
static inheritance for internal Ivy definitions and aligns with other Ivy declaration types.
This updates `declareExperimentalWebMcpTool` to allow annotations to be provided to opt-in the tool explicitly into being read only or returning untrusted content.
RESOURCE_COMPLETED_BEFORE_PRODUCING_VALUE had no guide, no JSDoc on
RxResourceOptions.stream, and — since the code was positive rather
than negative — could never get an auto-linked docs page even if one
existed. Flip it to -991, add the NG0991 reference page, and document
the "stream must emit a value or an error before completing"
requirement on stream's JSDoc and in the RxJS interop guide.
Also documents and tests that an unguarded template read of an
errored resource's .value() propagates to the global ErrorHandler,
and recommends guarding with .hasValue() as defense in depth.
httpResource can throw the same error, but for a different reason:
its internal request Observable isn't something app code writes
directly, so an empty completion there is almost always an
HttpInterceptor swallowing the response (catchError(() => EMPTY))
rather than a stream authored in the resource() call itself. The page
covers both APIs with guidance matched to what's actually going on
for each.
This started from a real production crash trace:
TypeError: Cannot read properties of null (reading 'nextSibling')
Traced through a minified bundle back to siblingAfter() in
packages/core/src/hydration/node_lookup_utils.ts. It walks forward a
fixed number of DOM siblings during hydration, based on how many the
server rendered. Its only guard, validateSiblingNodeExists(), is
gated behind ngDevMode and stripped from production builds. If the
client DOM has fewer real siblings than the server-serialized data
expects — client/server render diverged, or something outside
Angular (a browser extension, an injected script) removed a node —
the loop ran fully unguarded in production: currentNode went null
partway through, and the next iteration's currentNode.nextSibling
threw a raw, uncoded TypeError instead of a coded, debuggable one.
Reproduced first, not just theorized: added a test that server-
renders a @for loop with 3 items, removes 2 of them before hydration
runs, and confirmed it actually throws that exact TypeError against
the original code before touching anything.
Fix: add a null check at the top of the loop, matching the pattern
already used for the existing NG0500/NG0502 hydration checks in
element.ts — the dev-mode check runs first as before (a no-op in
production), and the new check is a pure safety net that only fires
once that dev-mode check has already been compiled away. In dev mode
this is provably dead code, so nothing about dev behavior changes.
Coded as NG0501 (HYDRATION_MISSING_SIBLINGS, already existed).
While investigating, found and fixed two more call sites with the
exact same shape of bug — a dev-only check that leaves production
completely unguarded:
- navigateToNode() (same file): walks an explicit "firstChild /
nextSibling" navigation path recorded for nodes that can't rely on
simple sibling-walking (e.g. content right after an <ng-content>
slot). Its only guard was also ngDevMode-only. Added the same
production safety net, both mid-loop (the raw-crash case) and
post-loop (where it could previously return null silently despite
the function's return type promising a non-null RNode). New code:
NG0509 (HYDRATION_MISSING_NODE_ON_PATH).
- getParentRElement() (render3/node_manipulation.ts): typed its
tNode parameter as always non-null, but a real production trace
showed it can be null at runtime, crashing on tNode.parent with no
useful information. Widened the type to TNode | null and added a
guard that throws a coded error instead. New code: NG0510
(PARENT_NODE_NOT_FOUND).
Unlike the other two, this one isn't gated behind ngDevMode at all
in the original code — it reproduces unconditionally. Decoded the
actual production minified stack trace against this source
(matched every frame character-for-character) to find the real
cause instead of guessing: an @if/@switch branch's content is its
own embedded template with its own TView, built lazily the first
time that branch renders. If an error interrupts that first pass —
here, a hydration mismatch on the branch's second child, after the
first child's TNode was already created — TView.firstCreatePass
still gets flipped to false in render.ts's `catch` block before the
error propagates, permanently marking that TView as corrupted.
Unlike a component's TView (rebuilt from scratch next time via
getOrCreateComponentTView()'s incompleteFirstPass check), nothing
rebuilds an embedded view's TView. The next time that exact branch
is selected again, its instructions read straight from the
corrupted tView.data instead of creating a fresh TNode, and any
node past the interruption point is still null.
The real test added for this (full_app_hydration_spec.ts) exercises
that whole chain for real: a hydration mismatch on an @if branch's
second child, then re-entering the same branch. Confirmed it throws
the exact byte-for-byte production error message ("Cannot read
properties of null (reading 'parent')") without the fix, and the
coded NG0510 with it. This replaces an earlier synthetic unit test
that just cast a variable to null to bypass the type system rather
than reproducing the actual failure.
Note: a more thorough fix would rebuild the corrupted embedded TView
itself (mirroring getOrCreateComponentTView()), which does make the
crash disappear — but doing that surfaces a second, separate bug:
the aborted first attempt's partially-created LView/DOM is never
torn down, so the retried branch's content gets appended alongside
leftover orphaned nodes instead of replacing them, corrupting
content silently instead of crashing loudly. That's a materially
bigger fix (proper LView cleanup after a mid-creation exception) and
is intentionally out of scope here; this commit keeps the narrower,
safe fix (a clear coded error) rather than trading a loud crash for
silent data corruption.
Along the way, deduplicated describeDomNode(), which existed as two
near-identical private copies (one in this file's own NG0500 check,
one in platform-browser's dom_renderer.ts insertBefore check) and
moved it into core's hydration/error_handling.ts, shared via the
private ɵdescribeDomNode export.
That function was then removed from every production code path
entirely, per review feedback: computing a human-readable node
description is debug-oriented work that shouldn't ship unconditionally
in production bundles, regardless of how cheap it is — production
should get the bare coded error only, the same as everywhere else in
this file. All three fixes above follow that: ngDevMode && 'message'
in production, no computed description. Confirmed the removal
actually shrinks output, not just assumed it: regenerated all 8
affected bundling symbol goldens, and each one shows exactly one
symbol removed — "describeDomNode" — and nothing else.
Implement \`toggleWatchSignal(id)\` in \`signal_debug.ts\` to enable toggling reactive watch listeners on individual signal graph nodes.
- Create reactive \`Watch\` instances using \`createWatch\` to log signal value/state changes to the console when active.
- Use \`WeakRef\` mapping and \`FinalizationRegistry\` (\`watchCleanupRegistry\`) so active watches do not retain strong references to signal nodes or prevent garbage collection.
- Expose \`watched\` status on \`DebugSignalGraphNode\` and publish \`toggleWatchSignal\` onto \`window.ng\` in development mode.
- Add acceptance tests covering watch activation, signal mutation logging, manual disposal, GC cleanup, and safe invalid ID handling.
`stableTypeOrdering` was recently enabled in the internal builds. While there aren't any breakages in Angular, these changes enable it so we can catch potential issues earlier.
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.
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.
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.
Introduce a custom `set` option in `linkedSignal` options to allow overriding and customizing the default write-back behavior of writable signals. This lets developers route updates back to the source of truth (e.g., converting Fahrenheit back to Celsius) or perform other side effects like updating properties inside a parent signal.
Additionally, the custom callback receives the standard signal setter as its second parameter (`rawSet`) to allow direct internal mutation if desired.
Fixes#59665
TAG=agy
CONV=addbb5c4-4233-49e8-b844-6f732d7d5c72
Updates `DomElementSchemaRegistry` to strip `:svg:` and `:math:` namespace prefixes
from tag names before querying `SECURITY_SCHEMA` at compile-time. This allows SVG
and MathML attributes to correctly match their security contexts during compilation.
`UNSAFE_IFRAME_ATTRS` in the `RuntimeErrorCode` enum is a `@deprecated`
alias of `UNSAFE_ATTRIBUTE_BINDING` (same value -910) with no usages
anywhere. Drop it along with the paired
`tslint:disable-next-line:no-duplicate-enum-values` suppression.
`RuntimeErrorCode` is re-exported as `ɵRuntimeErrorCode`, so the
enum's value set is not a stability commitment.
WebMCP is still an experimental standard and going through frequent changes in the Chrome implementation and the standards process. As a result, we should be clear about the support status of this API and its overall stability guarantees.
This is an ergonomic wrapper around `declareWebMcpTool`, allowing a user to define multiple tools directly on an injector's providers, rather than needing to find an injection context.
Example:
```typescript
import {bootstrapApplication, provideWebMcpTools} from '@angular/core';
await bootstrapApplication(RootComp, {
providers: [
provideWebMcpTools([
{
name: 'hello',
description: 'Says hello',
inputSchema: {type: 'object', properties: {}},
execute: async () => ({content: [{type: 'text', text: 'Hello, World!'}]});
},
]),
],
});
```
The `execute` function is invoked in the injection context of the `Injector` it is provided to, meaning you can easily `inject` dependencies and invoke them.
This also works particularly well with route `providers` and `withExperimentalAutoCleanupInjectors`, registering the tools when the router is navigated to and then automatically unregistering them when navigating away. Note that `withExperimentalAutoCleanupInjectors` is required for unregistration to work.
```typescript
import {provideWebMcpTools} from '@angular/core';
import {provideRouter} from '@angular/router';
provideRouter(
[
{
path: '',
component: Home,
providers: [
provideWebMcpTools([
{
name: 'hello',
description: 'Says hello',
inputSchema: {type: 'object', properties: {}},
execute: async () => ({content: [{type: 'text', text: 'Hello, World!'}]}),
},
]),
],
},
],
withExperimentalAutoCleanupInjectors(),
);
```
This exports `declareWebMcpTool`, a mechanism for registering WebMCP tools and tying them to Angular's `Injector` lifecycle. This function immediately registers the given tool and automatically unregisters it once the associated `Injector` is destroyed.
This exports the function and all transitively reachable types *except* for JSON Schema types as there are quite a lot and we don't want to couple to this particular implementation will likely be obsoleted by built-in types as the standard develops. If users want to leverage those, they should add their own dependency on `@mcp-b/webmcp-types`.
The time has come.
Note: #67382 introduced a breaking change where you could notice some sublte timing change on how `value` is set when using `rxResource` or a `stream` on a `resource`
Using explicit single generic arguments with transforms (for example, input<boolean>(false, {transform: booleanAttribute})) previously failed overload resolution.
Before this fix, type-checking produced:
````
✘ [ERROR] TS2769: No overload matches this call.
Overload 1 of 5, '(initialValue: boolean, opts?: InputOptionsWithoutTransform<boolean> | undefined): InputSignal<boolean>', gave the following error.
Type '(value: unknown) => boolean' is not assignable to type 'undefined'.
Overload 2 of 5, '(initialValue: undefined, opts: InputOptionsWithoutTransform<boolean>): InputSignal<boolean | undefined>', gave the following error.
Argument of type 'true' is not assignable to parameter of type 'undefined'. [plugin angular-compiler]
```
This change adds specialized overloads for explicit read generics.
ex:
```
await injectAsync(() => import('./test_service'))
```
We'll be reusing the features that were already used by the router to support components lazy-loading.
The commit introduces a new function to assist users who want to lazy load services and use the DI system to create them.
Example:
```ts
import {injectAsync} from 'angular/core';
class MyCmp {
someSvc = injectAsync(() => import('..'));
async onClick() {
(await this.someSvc()).handleClick();
}
}
```
These changes introduce the new `@Service` decorator which is a more ergonomic alternative to `@Injectable`. The reason we're adding a new decorator is that `@Injectable` has been around since the beginning of Angular and it has a lot of baggage that adds unnecessary overhead for users that generally want to define a singleton service, available in their entire app. The key differences between `@Service` and `@Injectable` are:
1. `@Service` is `providedIn: 'root'` by default. You can opt into providing the service yourself by setting `autoProvided: false` on it.
2. `@Service` doesn't allow constructor-based injection, only the `inject` function.
3. `@Service` doesn't support the complex type signature of `@Injectable` (`useClass`, `useValue` etc.). Instead it supports a single `factory` function.
Example:
```ts
import {Service} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {AuthService} from './auth';
@Service()
export class PostService {
private readonly httpClient = inject(HttpClient);
private readonly authService = inject(AuthService);
getUserPosts() {
return this.httpClient.get('/api/posts/' + this.authService.userId);
}
}
```
In order for resources to allow caching in SSR context (eg in the TransferState), resource need to be able to set their value synchronously.
If the resource value is not set synchronously, the resource will be in in a "loading" state which is responsible for destroying the server-hydrated resolved DOM.
The `ComponentFactory` has been removed from the public API in #68055.
This commit continues the cleanup and also removes `ModuleWithComponentFactories` from the public API.
This API was deprecated for a longtime, and was remove by #68055 from the public API.
We do have alternatives to this old API, so we can entirely remove it to spare some bytes
This is to align the shape of the method with `createComponent`
BREAKING CHANGE:The second arguement of appRef.bootstrap does not accept `any` anymore. Make sure the element you pass is not nullable.
fixes#67946
Those APIs date back to pre-ivy times and are long deprecated.
BREAKING CHANGE: `ComponentFactoryResolver` and `ComponentFactory` are no longer available. Pass the component class directly to APIs that previously required a factory, such as `ViewContainerRef.createComponent` or use the standalone `createComponent` function.
Those APIs date back to pre-ivy times and are long deprecated.
BREAKING CHANGE: `ComponentFactoryResolver` and `ComponentFactory` are no longer available. Pass the component class directly to APIs that previously required a factory, such as `ViewContainerRef.createComponent` or use the standalone `createComponentFunction`.
This adds support for a `debounce` option to the `validateAsync` and `validateHttp` functions.
This allows developers to debounce the triggering of async validators to improve performance.
A `DebounceTimer` type was also added to `@angular/core` to represent the wait condition parameters uniformly.
Includes the following changes to make sure the definitions for injectable compiler:
1. The types for the `factory` function now include the `parent` parameter.
2. `ɵɵFactoryDeclaration` is now defined as a function. We need this since the provider definition gets passed into the inejctable definition by reference.
3. `ɵɵdefineInjectable`, `ɵɵdefineNgModule` and `ɵɵdefinePipe` now return the typed definition, rather than `unknown`. This aligns with what we do for components and directives.
Adds a utility `debounced` to create a debounced version of a signal,
represented as a `Resource`. The resource's value contained the
debounced value of the signal, while its status (`resolved`, `loading`,
or `error`) indicates if the value is settled, if there is a value
currently pending debounce, or if the source signal threw an error.
Allows throwing from the resource's params function to transition the
resource to a status other than resolved.
In particular, the following values can be thrown from params:
- `ResourceParamsStatus.IDLE` causes the resource to become `idle`
(equivalent to returning `undefined`)
- `ResourceParamsStatus.LOADING` causes the resource to become `loading`
- Any `Error` object causes the resource to become `error` and report
the error that was thrown via `.error()`
To simplify chaining together resources, this PR also introduces a
context object passed into to the `params` functon. This context
contains a `chain` function that can be used to get the value of a
resource that the params want to depend on, while automatically
propagating the idle, loading, and erorr states of the resource forward.
This commit makes the behavior of `on idle` in `@defer` configurable via DI.
It defines an `IdleService` interface that an application can implement and
provide to Angular:
```ts
@Injectable({providedIn: 'root'})
export class CustomIdleService implements IdleService {
requestOnIdle(callback: () => void): number {...}
cancelOnIdle(id: number): void {...}
}
```
Then the idle service can be used by providing the IDLE_SERVICE token with the custom implementation.