a2ui-project / a2ui-project/a2ui

[v0.9] Fix memory leaks and standardize subscription lifecycle in web_core and react renderers

Open
#963 12 comments 0 reactions 1 assignee Claimed by @josemontespg View on GitHub
component: angular renderer component: react renderer javascript P2 sprint ready type: bug type: feature/enhancement
Dominant language
TypeScript
Stars
16.4k
Forks
1.3k
Avg merge
2d 13h
Merged PRs (30d)
134

Description

This issue tracks critical memory leaks and architectural redundancies identified in the A2UI v0.9 Web, React, and Angular renderers. These issues lead to unbounded memory growth in long-lived surfaces and leaked background processes (e.g., timers) when components are unmounted.

#### Identified Issues

**Core & Shared Logic:**
1. **Unbounded Signal Cache in `DataModel`**: The `DataModel.signals` Map caches every path ever queried. These are only cleared when the entire surface is disposed. In surfaces with many ephemeral components or large lists, this leads to significant memory growth.
2. **`FormatString` Recursive Leaks**: The `formatString` implementation in `basic_functions.ts` resolves internal expressions into signals using `context.resolveSignal(part)` but never calls `unsubscribe` on them. Any function call (like `${now()}`) inside a string will leak its listeners/timers until the DataModel is disposed.
3. **Broken Cleanup Chain in `DataContext`**: `resolveSignal` in `DataContext.ts` creates recursive effects for function arguments but does not properly take ownership of `unsubscribe` methods on signals returned by function implementations.

**React Renderer:**
4. **Redundant Subscription Layers**: The React adapter wraps the `GenericBinder` in additional `useSyncExternalStore` and `useEffect` layers. While not a leak, it increases complexity and makes cleanup harder to audit.

**Angular Renderer:**
5. **Root-Scoped Leaks in `ComponentBinder`**: `ComponentBinder.ts` injects `DestroyRef` at the root level. All subscriptions created via `bind()` are tied to the application lifetime rather than the component lifetime, causing massive leaks as users navigate surfaces.
6. **Stale Bindings**: Angular does not listen to `ComponentModel.onUpdated`. If component properties change in the protocol (e.g., type remains same but props change), the Angular UI stays stale.

#### Goals
* Implement reference counting or explicit signal removal in `DataModel`.
* Ensure all recursive signals created by `formatString` and function calls are tracked and disposed of via a unified `unsubscribe` chain.
* Fix the `DestroyRef` scoping in the Angular renderer.
* Implement property refresh logic in Angular to match the React `GenericBinder` behavior.
* Add exhaustive regression tests verifying that `AbortSignal` listeners and internal Maps are cleared after component unmount across all frameworks.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.