From deaf77953a674177cb48f11a525cd2ffd956bb39 Mon Sep 17 00:00:00 2001 From: Jane Chu <7559015+janechu@users.noreply.github.com> Date: Fri, 26 Jun 2026 11:09:31 -0700 Subject: [PATCH 1/6] fix: avoid rebinding attr backing fields on connect Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...-262079bc-d6c8-432a-bd92-a943733b1ee3.json | 7 + packages/fast-element/DESIGN.md | 4 +- .../docs/architecture/fastelement.md | 11 +- .../components/element-controller.pw.spec.ts | 124 ++++++++++++++++++ .../src/components/element-controller.ts | 24 +--- .../declarative/extension-subpaths.pw.spec.ts | 59 +++++++++ 6 files changed, 207 insertions(+), 22 deletions(-) create mode 100644 change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json diff --git a/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json b/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json new file mode 100644 index 00000000000..b656b9e19d0 --- /dev/null +++ b/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: avoid rebinding internal attribute backing fields during connect", + "packageName": "@microsoft/fast-element", + "email": "7559015+janechu@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/fast-element/DESIGN.md b/packages/fast-element/DESIGN.md index bdc092413fa..5e6a8a8c6fb 100644 --- a/packages/fast-element/DESIGN.md +++ b/packages/fast-element/DESIGN.md @@ -101,7 +101,7 @@ The previous `FAST.getById()` slot registry, `FASTGlobal` type, and `KernelServi - Holds the element's `FASTElementDefinition` (name, template, styles, observed attributes). - Manages a `Stages` state machine: `disconnected → connecting → connected → disconnecting → disconnected`. - Exposes `isPrerendered: Promise` which resolves to `true` when the element had a declarative shadow root (DSD) at connect time, regardless of whether hydration ran. Exposes `isHydrated: Promise` which resolves to `true` only when hydration actually ran successfully. The `ViewController` interface also exposes both `isPrerendered` and `isHydrated` as `Promise` for custom directives. Attribute-skip logic during the hydration bind uses an internal `_skipAttrUpdates` flag that is never exposed as a public boolean. -- On `connect()`: restores pre-upgrade observable values, synchronizes any late-defined attribute-map attributes and observes only those late attributes for future DOM changes, calls `connectedCallback` on all `HostBehavior`s, renders the current template into the shadow root when one is available, and applies styles. +- On `connect()`: restores public pre-upgrade observable values that were set as own properties, synchronizes any late-defined attribute-map attributes and observes only those late attributes for future DOM changes, calls `connectedCallback` on all `HostBehavior`s, renders the current template into the shadow root when one is available, and applies styles. Internal observable backing slots such as `_foo` are not normalized into public `foo` assignments; they already represent FAST-owned model state. Opt-in extensions such as `observerMap()` perform any additional proxying through the public accessor paths they install. - Rendering is split into two modular paths. Hydration is pluggable: `enableHydration()` from `@microsoft/fast-element/hydration.js` installs a hook via `ElementController.installHydrationHook()`, keeping zero hydration imports in the core controller: - **Prerendered**: The hydration hook (installed by `enableHydration()`) registers the element in the active `HydrationTracker`, swaps `onAttributeChangedCallback` to a no-op so the upgrade-time burst of callbacks is discarded, hydrates the existing DOM via `template.hydrate()`, then restores the standard handler and removes the element from the tracker. The entire method is wrapped in `try/finally` to guarantee cleanup even if an error occurs during hydration. After this point, all future attribute changes flow through the real handler with zero overhead. - **Client-side**: `renderClientSide()` clones the compiled fragment, binds, and appends to the host — the standard path with no prerender logic. @@ -211,6 +211,8 @@ MyComponent.define({ name: "my-component" }, [myPlugin()]); 1. Stores the new value in a backing slot. 2. Calls `Observable.getNotifier(this).notify(name)` to fan out to subscribers. +Backing slots are internal implementation details. Lifecycle rebinding treats public own properties as pre-upgrade assignments that need to flow through the accessor, but it does not reinterpret FAST-owned backing slots as public property writes. + `Observable.getNotifier(object)` returns the `PropertyChangeNotifier` for an object, creating one on demand. Notifiers are stored in a `WeakMap` so they don't prevent GC. #### SubscriberSet / PropertyChangeNotifier diff --git a/packages/fast-element/docs/architecture/fastelement.md b/packages/fast-element/docs/architecture/fastelement.md index 680148dc477..37a208d61cc 100644 --- a/packages/fast-element/docs/architecture/fastelement.md +++ b/packages/fast-element/docs/architecture/fastelement.md @@ -37,7 +37,7 @@ flowchart TD B --> D B --> E B --> F - C[bind observables by capturing the Custom Elements properties and setting the values from the bound observables on the Custom Element] + C[bind observables by capturing public own Custom Element properties and setting those values through the matching accessors] D[connect behaviors by call the connectedCallback on all behaviors] E[render template, execute an ElementViewTemplate's render method] F[add styles either as an appended StyleElement node or an adoptedStylesheet which may include and attach behaviors] @@ -65,4 +65,11 @@ flowchart TD D[An Updates.enqueue is called which places the update in the task queue which is then executed, these are performed async unless otherwise specified] ``` -These changes are observed and similar to the way `Observables` work, they utilize an `Accessor` pattern which has a `getValue` and `setValue` in which DOM updates are applied. \ No newline at end of file +These changes are observed and similar to the way `Observables` work, they utilize an `Accessor` pattern which has a `getValue` and `setValue` in which DOM updates are applied. + +Observable backing slots such as `_foo` are FAST-owned internal state. The +controller's connection-time observable binding only replays public own +properties that shadow prototype accessors; it does not normalize backing slots +into public property assignments. This keeps attribute converter output in its +already-converted model form while preserving pre-upgrade public property +assignments and opt-in extension behavior. \ No newline at end of file diff --git a/packages/fast-element/src/components/element-controller.pw.spec.ts b/packages/fast-element/src/components/element-controller.pw.spec.ts index 83011798514..3c4f385217c 100644 --- a/packages/fast-element/src/components/element-controller.pw.spec.ts +++ b/packages/fast-element/src/components/element-controller.pw.spec.ts @@ -166,6 +166,130 @@ test.describe("The ElementController", () => { }); test.describe("during connect", () => { + test("does not rebind converted attribute backing fields through converters", async ({ + page, + }) => { + await page.goto("/"); + + const result = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { FASTElement, FASTElementDefinition, uniqueElementName } = + await import("/main.js"); + + const calls: Array<{ type: string; value: string }> = []; + const json = '{"title":"hello"}'; + const converter = { + toView(value: any) { + return JSON.stringify(value); + }, + fromView(value: any) { + calls.push({ type: typeof value, value: String(value) }); + return JSON.parse(value); + }, + }; + + const name = uniqueElementName(); + ( + await FASTElementDefinition.compose( + class ControllerTest extends FASTElement { + static definition = { + name, + attributes: [{ property: "data", converter }], + }; + }, + ) + ).define(); + + const element = document.createElement(name) as any; + element.setAttribute("data", json); + document.body.appendChild(element); + + const value = { + calls, + data: element.data, + }; + + document.body.removeChild(element); + + return value; + }); + + expect(result.calls).toEqual([ + { type: "string", value: '{"title":"hello"}' }, + ]); + expect(result.data).toEqual({ title: "hello" }); + }); + + test("rebinds public own observable properties before rendering", async ({ + page, + }) => { + await page.goto("/"); + + const result = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { + FASTElement, + FASTElementDefinition, + ElementController, + Observable, + html, + uniqueElementName, + } = await import("/main.js"); + + const changes: string[] = []; + const name = uniqueElementName(); + + class ControllerTest extends FASTElement { + public messageChanged( + _oldValue: string | undefined, + newValue: string, + ) { + changes.push(newValue); + } + } + + Observable.defineProperty(ControllerTest.prototype, "message"); + + ( + await FASTElementDefinition.compose(ControllerTest, { + name, + template: html`${x => x.message}`, + }) + ).define(); + + const element = document.createElement(name) as any; + const controller = ElementController.forCustomElement(element); + + Object.defineProperty(element, "message", { + configurable: true, + enumerable: true, + value: "hello", + writable: true, + }); + + controller.connect(); + + const value = { + changes, + hasOwnMessage: Object.prototype.hasOwnProperty.call( + element, + "message", + ), + message: element.message, + text: element.shadowRoot?.textContent ?? "", + }; + + controller.disconnect(); + + return value; + }); + + expect(result.changes).toEqual(["hello"]); + expect(result.hasOwnMessage).toBe(false); + expect(result.message).toBe("hello"); + expect(result.text).toBe("hello"); + }); + test("renders nothing to shadow dom in shadow dom mode when there's no template", async ({ page, }) => { diff --git a/packages/fast-element/src/components/element-controller.ts b/packages/fast-element/src/components/element-controller.ts index 5fe3ef551b0..4c0ce799b48 100644 --- a/packages/fast-element/src/components/element-controller.ts +++ b/packages/fast-element/src/components/element-controller.ts @@ -534,8 +534,8 @@ export class ElementController } /** - * Captures own-properties that shadow observable accessors on the prototype so - * they can be rebound through the accessor before rendering. + * Captures public own-properties that shadow observable accessors on the + * prototype so they can be rebound through the accessor before rendering. */ protected captureBoundObservables() { const element = this.source; @@ -564,35 +564,21 @@ export class ElementController for (let i = 0, ii = propertyNames.length; i < ii; ++i) { const ownPropertyName = propertyNames[i]; - const propertyName = ( - ownPropertyName[0] === "_" ? ownPropertyName.slice(1) : ownPropertyName - ) as keyof TElement; + const propertyName = ownPropertyName as keyof TElement; if (!hasPrototypeAccessor(propertyName as string)) { continue; } const value = (element as any)[propertyName]; - const isBackingField = ownPropertyName !== propertyName; - const isRebindableObject = - value !== null && - typeof value === "object" && - !value?.$isProxy && - !(Array.isArray(value) && (value as any)?.$fastController); if (value === void 0) { - if (!isBackingField) { - delete element[ownPropertyName as keyof TElement]; - } - - continue; - } + delete element[propertyName]; - if (isBackingField && !isRebindableObject) { continue; } - delete element[ownPropertyName as keyof TElement]; + delete element[propertyName]; (boundObservables ??= this.boundObservables = Object.create(null))[ propertyName ] = value; diff --git a/packages/fast-element/src/declarative/extension-subpaths.pw.spec.ts b/packages/fast-element/src/declarative/extension-subpaths.pw.spec.ts index 0c414bdcfcc..993dd72ef5c 100644 --- a/packages/fast-element/src/declarative/extension-subpaths.pw.spec.ts +++ b/packages/fast-element/src/declarative/extension-subpaths.pw.spec.ts @@ -119,6 +119,65 @@ test.describe("extension subpaths", () => { expect(result.replacedOriginal).toBe(true); }); + test("observerMap proxies public own properties rebound on connect", async ({ + page, + }) => { + await page.goto("/"); + + const result = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { Schema, observerMap } = await import("/extension-subpaths-main.js"); + // @ts-expect-error: Client module. + const { FASTElement, uniqueElementName } = await import("/main.js"); + + const name = uniqueElementName(); + const schema = new Schema(name); + schema.addPath({ + rootPropertyName: "model", + pathConfig: { + type: "default", + path: "model.value", + currentContext: null, + parentContext: null, + }, + childrenMap: null, + }); + + class ObserverMapElement extends FASTElement { + public model: any; + } + + await ObserverMapElement.define({ name, schema }, [observerMap({ schema })]); + + const element = document.createElement(name) as any; + const originalValue = { value: "before" }; + Object.defineProperty(element, "model", { + configurable: true, + enumerable: true, + value: originalValue, + writable: true, + }); + + document.body.appendChild(element); + + const value = { + hasOwnModel: Object.prototype.hasOwnProperty.call(element, "model"), + isProxy: element.model.$isProxy === true, + preservesValue: element.model.value === "before", + replacedOriginal: element.model !== originalValue, + }; + + document.body.removeChild(element); + + return value; + }); + + expect(result.hasOwnModel).toBe(false); + expect(result.isProxy).toBe(true); + expect(result.preservesValue).toBe(true); + expect(result.replacedOriginal).toBe(true); + }); + test("observerMap observes items spliced into nested $defs arrays", async ({ page, }) => { From 011fa547827db7473244850c4df0849c1bdaac3e Mon Sep 17 00:00:00 2001 From: Jane Chu <7559015+janechu@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:16:57 -0700 Subject: [PATCH 2/6] test: cover observable underscore backing fields Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...-262079bc-d6c8-432a-bd92-a943733b1ee3.json | 2 +- .../components/element-controller.pw.spec.ts | 83 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json b/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json index b656b9e19d0..a31cd437829 100644 --- a/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json +++ b/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json @@ -1,5 +1,5 @@ { - "type": "patch", + "type": "prerelease", "comment": "fix: avoid rebinding internal attribute backing fields during connect", "packageName": "@microsoft/fast-element", "email": "7559015+janechu@users.noreply.github.com", diff --git a/packages/fast-element/src/components/element-controller.pw.spec.ts b/packages/fast-element/src/components/element-controller.pw.spec.ts index 3c4f385217c..c630e441409 100644 --- a/packages/fast-element/src/components/element-controller.pw.spec.ts +++ b/packages/fast-element/src/components/element-controller.pw.spec.ts @@ -290,6 +290,89 @@ test.describe("The ElementController", () => { expect(result.text).toBe("hello"); }); + test("preserves observable backing fields set on the instance", async ({ + page, + }) => { + await page.goto("/"); + + const result = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { + FASTElement, + FASTElementDefinition, + ElementController, + Observable, + html, + uniqueElementName, + } = await import("/main.js"); + + const changes: Array<{ oldValue: string; newValue: string }> = []; + const name = uniqueElementName(); + + class ControllerTest extends FASTElement { + public _message = "hello"; + + public messageChanged(oldValue: string, newValue: string) { + changes.push({ oldValue, newValue }); + } + } + + Observable.defineProperty(ControllerTest.prototype, "message"); + + ( + await FASTElementDefinition.compose(ControllerTest, { + name, + template: html`${x => x.message}`, + }) + ).define(); + + const element = document.createElement(name) as any; + const controller = ElementController.forCustomElement(element); + + controller.connect(); + + const initial = { + hasOwnBackingField: Object.prototype.hasOwnProperty.call( + element, + "_message", + ), + hasOwnMessage: Object.prototype.hasOwnProperty.call( + element, + "message", + ), + message: element.message, + text: element.shadowRoot?.textContent ?? "", + }; + + element.message = "updated"; + await new Promise(resolve => requestAnimationFrame(resolve)); + + const updated = { + backingField: element._message, + changes, + message: element.message, + text: element.shadowRoot?.textContent ?? "", + }; + + controller.disconnect(); + + return { initial, updated }; + }); + + expect(result.initial).toEqual({ + hasOwnBackingField: true, + hasOwnMessage: false, + message: "hello", + text: "hello", + }); + expect(result.updated).toEqual({ + backingField: "updated", + changes: [{ oldValue: "hello", newValue: "updated" }], + message: "updated", + text: "updated", + }); + }); + test("renders nothing to shadow dom in shadow dom mode when there's no template", async ({ page, }) => { From 15b6301861d60d8a302f9925c397eabe0578308b Mon Sep 17 00:00:00 2001 From: Jane Chu <7559015+janechu@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:25:21 -0700 Subject: [PATCH 3/6] chore: use patch change type Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...osoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json b/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json index a31cd437829..b656b9e19d0 100644 --- a/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json +++ b/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json @@ -1,5 +1,5 @@ { - "type": "prerelease", + "type": "patch", "comment": "fix: avoid rebinding internal attribute backing fields during connect", "packageName": "@microsoft/fast-element", "email": "7559015+janechu@users.noreply.github.com", From b0f61bff4b5f9a334720f4304896017e049598bd Mon Sep 17 00:00:00 2001 From: Jane Chu <7559015+janechu@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:25:54 -0700 Subject: [PATCH 4/6] chore: update changefile wording Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...osoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json b/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json index b656b9e19d0..2f6472044d8 100644 --- a/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json +++ b/change/@microsoft-fast-element-262079bc-d6c8-432a-bd92-a943733b1ee3.json @@ -1,6 +1,6 @@ { "type": "patch", - "comment": "fix: avoid rebinding internal attribute backing fields during connect", + "comment": "Avoid rebinding internal attribute backing fields during connect.", "packageName": "@microsoft/fast-element", "email": "7559015+janechu@users.noreply.github.com", "dependentChangeType": "none" From 662392800a729c7a971e333d234512298f72202d Mon Sep 17 00:00:00 2001 From: Jane Chu <7559015+janechu@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:49:14 -0700 Subject: [PATCH 5/6] chore: update fast-element sizes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/fast-element/SIZES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/fast-element/SIZES.md b/packages/fast-element/SIZES.md index c9ec8f502c2..642994fa188 100644 --- a/packages/fast-element/SIZES.md +++ b/packages/fast-element/SIZES.md @@ -4,8 +4,8 @@ Bundle sizes for `@microsoft/fast-element` exports. | Export | Minified | Gzip | Brotli | |--------|----------|------|--------| -| CDN Rollup Bundle | 78.77 KB | 23.48 KB | 20.87 KB | -| FASTElement (@microsoft/fast-element/fast-element.js) | 23.24 KB | 7.19 KB | 6.49 KB | +| CDN Rollup Bundle | 78.61 KB | 23.40 KB | 20.77 KB | +| FASTElement (@microsoft/fast-element/fast-element.js) | 23.11 KB | 7.12 KB | 6.41 KB | | Updates (@microsoft/fast-element/updates.js) | 473 B | 335 B | 290 B | | Observable (@microsoft/fast-element/observable.js) | 6.75 KB | 2.51 KB | 2.23 KB | | observable (@microsoft/fast-element/observable.js) | 6.79 KB | 2.52 KB | 2.24 KB | @@ -18,7 +18,7 @@ Bundle sizes for `@microsoft/fast-element` exports. | html (@microsoft/fast-element/html.js) | 27.73 KB | 8.96 KB | 8.02 KB | | repeat (@microsoft/fast-element/repeat.js) | 31.80 KB | 10.00 KB | 9.03 KB | | css (@microsoft/fast-element/css.js) | 2.43 KB | 1.00 KB | 911 B | -| enableHydration (@microsoft/fast-element/hydration.js) | 46.66 KB | 13.99 KB | 12.56 KB | +| enableHydration (@microsoft/fast-element/hydration.js) | 46.53 KB | 13.91 KB | 12.49 KB | | declarativeTemplate (@microsoft/fast-element/declarative.js) | 62.15 KB | 19.46 KB | 17.42 KB | | ArrayObserver (@microsoft/fast-element/arrays.js) | 12.55 KB | 4.46 KB | 4.03 KB | | observerMap (@microsoft/fast-element/observer-map.js) | 20.08 KB | 7.15 KB | 6.43 KB | From a7ac4512d4f9ac33ca038ba73cb7c7d3191fbf43 Mon Sep 17 00:00:00 2001 From: Jane Chu <7559015+janechu@users.noreply.github.com> Date: Fri, 26 Jun 2026 13:37:01 -0700 Subject: [PATCH 6/6] docs: update generated fast-element docs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...ast-element.elementcontroller.captureboundobservables.md | 2 +- .../src/docs/3.x/api/fast-element.elementcontroller.md | 2 +- sites/website/src/docs/3.x/resources/export-sizes.md | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sites/website/src/docs/3.x/api/fast-element.elementcontroller.captureboundobservables.md b/sites/website/src/docs/3.x/api/fast-element.elementcontroller.captureboundobservables.md index b276fcaa3ba..0f93e046209 100644 --- a/sites/website/src/docs/3.x/api/fast-element.elementcontroller.captureboundobservables.md +++ b/sites/website/src/docs/3.x/api/fast-element.elementcontroller.captureboundobservables.md @@ -15,7 +15,7 @@ navigationOptions: ## ElementController.captureBoundObservables() method -Captures own-properties that shadow observable accessors on the prototype so they can be rebound through the accessor before rendering. +Captures public own-properties that shadow observable accessors on the prototype so they can be rebound through the accessor before rendering. **Signature:** diff --git a/sites/website/src/docs/3.x/api/fast-element.elementcontroller.md b/sites/website/src/docs/3.x/api/fast-element.elementcontroller.md index 4f8e1c58d48..d94210ef7ee 100644 --- a/sites/website/src/docs/3.x/api/fast-element.elementcontroller.md +++ b/sites/website/src/docs/3.x/api/fast-element.elementcontroller.md @@ -478,7 +478,7 @@ Binds any observables that were set before upgrade. -Captures own-properties that shadow observable accessors on the prototype so they can be rebound through the accessor before rendering. +Captures public own-properties that shadow observable accessors on the prototype so they can be rebound through the accessor before rendering. diff --git a/sites/website/src/docs/3.x/resources/export-sizes.md b/sites/website/src/docs/3.x/resources/export-sizes.md index 91dd969748b..6e8a10b8fa9 100644 --- a/sites/website/src/docs/3.x/resources/export-sizes.md +++ b/sites/website/src/docs/3.x/resources/export-sizes.md @@ -19,8 +19,8 @@ Bundle sizes for `@microsoft/fast-element` exports. | Export | Minified | Gzip | Brotli | |--------|----------|------|--------| -| CDN Rollup Bundle | 78.77 KB | 23.48 KB | 20.87 KB | -| FASTElement (@microsoft/fast-element/fast-element.js) | 23.24 KB | 7.19 KB | 6.49 KB | +| CDN Rollup Bundle | 78.61 KB | 23.40 KB | 20.77 KB | +| FASTElement (@microsoft/fast-element/fast-element.js) | 23.11 KB | 7.12 KB | 6.41 KB | | Updates (@microsoft/fast-element/updates.js) | 473 B | 335 B | 290 B | | Observable (@microsoft/fast-element/observable.js) | 6.75 KB | 2.51 KB | 2.23 KB | | observable (@microsoft/fast-element/observable.js) | 6.79 KB | 2.52 KB | 2.24 KB | @@ -33,7 +33,7 @@ Bundle sizes for `@microsoft/fast-element` exports. | html (@microsoft/fast-element/html.js) | 27.73 KB | 8.96 KB | 8.02 KB | | repeat (@microsoft/fast-element/repeat.js) | 31.80 KB | 10.00 KB | 9.03 KB | | css (@microsoft/fast-element/css.js) | 2.43 KB | 1.00 KB | 911 B | -| enableHydration (@microsoft/fast-element/hydration.js) | 46.66 KB | 13.99 KB | 12.56 KB | +| enableHydration (@microsoft/fast-element/hydration.js) | 46.53 KB | 13.91 KB | 12.49 KB | | declarativeTemplate (@microsoft/fast-element/declarative.js) | 62.15 KB | 19.46 KB | 17.42 KB | | ArrayObserver (@microsoft/fast-element/arrays.js) | 12.55 KB | 4.46 KB | 4.03 KB | | observerMap (@microsoft/fast-element/observer-map.js) | 20.08 KB | 7.15 KB | 6.43 KB |