#7581·fast

fix: avoid same-reference observerMap assignment reprocessing in fast-html

Author: janechuCreated Jun 10, 2026Updated Sep 10, 2026
Labelsimprovementstatus:plannedarea:fast-elementfast-element-v3

Bug Report

The observerMap accessor created by defineObservableProperty calls assignObservables before checking whether the assigned value is already the same reference stored in the backing field. For object and array values, this can walk the schema and reprocess nested objects/arrays even when the assignment was intended to be a no-op.

This is a follow-up hardening issue for #7559.

Repro or Code Sample

Conceptual repro:

typescript
const orders = element.user.orders;

element.user.orders = orders;

Assigning the same observerMap-managed array reference back to the property should not re-run schema processing, proxy installation, or notification work.

Expected Behavior

When an observerMap-generated setter receives the same reference currently stored in its backing field, it should return before doing any expensive wrapping or notification work.

Current Behavior

The setter computes:

typescript
const oldValue = source[field];
const newValue = assignObservables(schema, rootSchema, value, target, rootProperty);

if (oldValue !== newValue) {
    source[field] = newValue;
    Observable.notify(source, key);
}

For object and array values, assignObservables can walk the schema and reprocess nested structures before the same-reference no-op is known.

Possible Solution

Add a guard at the start of the generated setter:

typescript
if (value === source[field]) {
    return;
}

Leave primitive handling simple unless profiling shows a real issue; primitive assignments are already cheap compared with object/array reprocessing.

Suggested acceptance coverage:

  • A focused test that assigns the same observerMap-managed object/array reference back to a generated observable property and verifies no notification fires.
  • Benchmark or profiling numbers for a representative nested observerMap tree, if available.
  • Audit examples/ssr/webui-todo-app and examples/ssr/chat-app for per-frame or per-keystroke deepMerge usage and confirm no visible regression.

Context

PR #7559 added a custom accessor path so replacement arrays are processed through observerMap and remain observable. This follow-up keeps that behavior while avoiding unnecessary work for same-reference assignments.

Searched existing issues for overlapping defineObservableProperty or observerMap assignment reprocessing reports and did not find a duplicate.

Your Environment

  • OS & Device: Not OS-specific
  • Browser: Not browser-specific
  • Version: @microsoft/fast-html after #7559