a2ui-project / a2ui-project/a2ui
[v0.9] Fix memory leaks and standardize subscription lifecycle in web_core and react renderers
- 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
Assessment
This issue has not been assessed yet.