@microsoft/fast-element 2.8.0 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.json CHANGED
@@ -2,7 +2,22 @@
2
2
  "name": "@microsoft/fast-element",
3
3
  "entries": [
4
4
  {
5
- "date": "Mon, 13 Oct 2025 00:36:35 GMT",
5
+ "date": "Tue, 21 Oct 2025 16:17:03 GMT",
6
+ "version": "2.8.1",
7
+ "tag": "@microsoft/fast-element_v2.8.1",
8
+ "comments": {
9
+ "patch": [
10
+ {
11
+ "author": "863023+radium-v@users.noreply.github.com",
12
+ "package": "@microsoft/fast-element",
13
+ "commit": "d66ae6571c97341890e5320048fc1a3ef38dc5be",
14
+ "comment": "feat: add methods to track hydrating instances"
15
+ }
16
+ ]
17
+ }
18
+ },
19
+ {
20
+ "date": "Mon, 13 Oct 2025 00:37:08 GMT",
6
21
  "version": "2.8.0",
7
22
  "tag": "@microsoft/fast-element_v2.8.0",
8
23
  "comments": {
package/CHANGELOG.md CHANGED
@@ -1,12 +1,20 @@
1
1
  # Change Log - @microsoft/fast-element
2
2
 
3
- <!-- This log was last generated on Mon, 13 Oct 2025 00:36:35 GMT and should not be manually modified. -->
3
+ <!-- This log was last generated on Tue, 21 Oct 2025 16:17:03 GMT and should not be manually modified. -->
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 2.8.1
8
+
9
+ Tue, 21 Oct 2025 16:17:03 GMT
10
+
11
+ ### Patches
12
+
13
+ - feat: add methods to track hydrating instances (863023+radium-v@users.noreply.github.com)
14
+
7
15
  ## 2.8.0
8
16
 
9
- Mon, 13 Oct 2025 00:36:35 GMT
17
+ Mon, 13 Oct 2025 00:37:08 GMT
10
18
 
11
19
  ### Minor changes
12
20
 
@@ -241,18 +241,44 @@ export declare class HydratableElementController<TElement extends HTMLElement =
241
241
  /**
242
242
  * Lifecycle callbacks for hydration events
243
243
  */
244
- private static lifecycleCallbacks?;
244
+ static lifecycleCallbacks?: HydrationControllerCallbacks;
245
+ /**
246
+ * An idle callback ID used to track hydration completion
247
+ */
248
+ private static idleCallbackId;
249
+ /**
250
+ * Adds the current element instance to the hydrating instances map
251
+ */
252
+ private addHydratingInstance;
245
253
  /**
246
254
  * Configure lifecycle callbacks for hydration events
247
255
  */
248
256
  static config(callbacks: HydrationControllerCallbacks): typeof HydratableElementController;
249
257
  private static hydrationObserverHandler;
250
258
  /**
251
- * Checks if all elements have completed hydration and dispatches event if complete
259
+ * Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
260
+ * Then resets the ElementController strategy to the default so that future elements
261
+ * don't use the HydratableElementController.
262
+ *
263
+ * @param deadline - the idle deadline object
252
264
  */
253
265
  private static checkHydrationComplete;
254
266
  static forCustomElement(element: HTMLElement, override?: boolean): ElementController<HTMLElement>;
255
267
  connect(): void;
268
+ /**
269
+ * A map of element instances by the name of the custom element they are
270
+ * associated with. The key is the custom element name, and the value is the
271
+ * instances of hydratable elements which currently need to be hydrated.
272
+ *
273
+ * When all of the instances in the set have been hydrated, the set is
274
+ * cleared and removed from the map. If the map is empty, the
275
+ * hydrationComplete callback is invoked.
276
+ */
277
+ private static hydratingInstances?;
278
+ /**
279
+ * Removes the current element instance from the hydrating instances map
280
+ */
281
+ private removeHydratingInstance;
256
282
  disconnect(): void;
257
283
  static install(): void;
258
284
  }
@@ -591,6 +591,21 @@ export const needsHydrationAttribute = "needs-hydration";
591
591
  * @beta
592
592
  */
593
593
  export class HydratableElementController extends ElementController {
594
+ /**
595
+ * Adds the current element instance to the hydrating instances map
596
+ */
597
+ addHydratingInstance() {
598
+ if (!HydratableElementController.hydratingInstances) {
599
+ return;
600
+ }
601
+ const name = this.definition.name;
602
+ let instances = HydratableElementController.hydratingInstances.get(name);
603
+ if (!instances) {
604
+ instances = new Set();
605
+ HydratableElementController.hydratingInstances.set(name, instances);
606
+ }
607
+ instances.add(this.source);
608
+ }
594
609
  /**
595
610
  * Configure lifecycle callbacks for hydration events
596
611
  */
@@ -600,17 +615,30 @@ export class HydratableElementController extends ElementController {
600
615
  }
601
616
  static hydrationObserverHandler(records) {
602
617
  for (const record of records) {
603
- HydratableElementController.hydrationObserver.unobserve(record.target);
604
- record.target.$fastController.connect();
618
+ if (!record.target.hasAttribute(deferHydrationAttribute)) {
619
+ HydratableElementController.hydrationObserver.unobserve(record.target);
620
+ record.target.$fastController.connect();
621
+ }
605
622
  }
606
623
  }
607
624
  /**
608
- * Checks if all elements have completed hydration and dispatches event if complete
625
+ * Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
626
+ * Then resets the ElementController strategy to the default so that future elements
627
+ * don't use the HydratableElementController.
628
+ *
629
+ * @param deadline - the idle deadline object
609
630
  */
610
- static checkHydrationComplete() {
611
- var _a, _b;
612
- if (!document.querySelector(`[${needsHydrationAttribute}]`)) {
613
- (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.hydrationComplete) === null || _b === void 0 ? void 0 : _b.call(_a);
631
+ static checkHydrationComplete(deadline) {
632
+ var _a, _b, _c;
633
+ if (deadline.didTimeout) {
634
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
635
+ return;
636
+ }
637
+ // If there are no more hydrating instances, invoke the hydrationComplete callback
638
+ if (((_a = HydratableElementController.hydratingInstances) === null || _a === void 0 ? void 0 : _a.size) === 0) {
639
+ (_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.hydrationComplete) === null || _c === void 0 ? void 0 : _c.call(_b);
640
+ // Reset to the default strategy after hydration is complete
641
+ ElementController.setStrategy(ElementController);
614
642
  }
615
643
  }
616
644
  static forCustomElement(element, override) {
@@ -623,15 +651,17 @@ export class HydratableElementController extends ElementController {
623
651
  return super.forCustomElement(element, override);
624
652
  }
625
653
  connect() {
626
- var _a, _b, _c, _d, _e, _f;
654
+ var _a, _b, _c, _d, _e;
627
655
  // Initialize needsHydration on first connect
628
- if (this.needsHydration === undefined) {
629
- this.needsHydration =
630
- this.source.getAttribute(needsHydrationAttribute) !== null;
656
+ this.needsHydration =
657
+ (_a = this.needsHydration) !== null && _a !== void 0 ? _a : this.source.getAttribute(needsHydrationAttribute) !== null;
658
+ if (this.needsHydration) {
659
+ (_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementWillHydrate) === null || _c === void 0 ? void 0 : _c.call(_b, this.definition.name);
631
660
  }
632
661
  // If the `defer-hydration` attribute exists on the source,
633
662
  // wait for it to be removed before continuing connection behavior.
634
663
  if (this.source.hasAttribute(deferHydrationAttribute)) {
664
+ this.addHydratingInstance();
635
665
  HydratableElementController.hydrationObserver.observe(this.source, {
636
666
  attributeFilter: [deferHydrationAttribute],
637
667
  });
@@ -643,20 +673,19 @@ export class HydratableElementController extends ElementController {
643
673
  // class
644
674
  if (!this.needsHydration) {
645
675
  super.connect();
676
+ this.removeHydratingInstance();
646
677
  return;
647
678
  }
648
679
  if (this.stage !== 3 /* Stages.disconnected */) {
649
680
  return;
650
681
  }
651
- // Callback: Before hydration has started
652
- (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementWillHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
653
682
  this.stage = 0 /* Stages.connecting */;
654
683
  this.bindObservables();
655
684
  this.connectBehaviors();
656
- const element = this.source;
657
- const host = (_c = getShadowRoot(element)) !== null && _c !== void 0 ? _c : element;
658
685
  if (this.template) {
659
686
  if (isHydratable(this.template)) {
687
+ const element = this.source;
688
+ const host = (_d = getShadowRoot(element)) !== null && _d !== void 0 ? _d : element;
660
689
  let firstChild = host.firstChild;
661
690
  let lastChild = host.lastChild;
662
691
  if (element.shadowRoot === null) {
@@ -671,7 +700,7 @@ export class HydratableElementController extends ElementController {
671
700
  }
672
701
  }
673
702
  this.view = this.template.hydrate(firstChild, lastChild, element);
674
- (_d = this.view) === null || _d === void 0 ? void 0 : _d.bind(this.source);
703
+ (_e = this.view) === null || _e === void 0 ? void 0 : _e.bind(this.source);
675
704
  }
676
705
  else {
677
706
  this.renderTemplate(this.template);
@@ -681,11 +710,31 @@ export class HydratableElementController extends ElementController {
681
710
  this.stage = 1 /* Stages.connected */;
682
711
  this.source.removeAttribute(needsHydrationAttribute);
683
712
  this.needsInitialization = this.needsHydration = false;
713
+ this.removeHydratingInstance();
684
714
  Observable.notify(this, isConnectedPropertyName);
715
+ }
716
+ /**
717
+ * Removes the current element instance from the hydrating instances map
718
+ */
719
+ removeHydratingInstance() {
720
+ var _a, _b;
721
+ if (!HydratableElementController.hydratingInstances) {
722
+ return;
723
+ }
724
+ const name = this.definition.name;
725
+ const instances = HydratableElementController.hydratingInstances.get(name);
685
726
  // Callback: After hydration has finished
686
- (_f = (_e = HydratableElementController.lifecycleCallbacks) === null || _e === void 0 ? void 0 : _e.elementDidHydrate) === null || _f === void 0 ? void 0 : _f.call(_e, this.definition.name);
687
- // Check if hydration is complete after this element is hydrated
688
- HydratableElementController.checkHydrationComplete();
727
+ (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
728
+ if (instances) {
729
+ instances.delete(this.source);
730
+ if (!instances.size) {
731
+ HydratableElementController.hydratingInstances.delete(name);
732
+ }
733
+ if (HydratableElementController.idleCallbackId) {
734
+ cancelIdleCallback(HydratableElementController.idleCallbackId);
735
+ }
736
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
737
+ }
689
738
  }
690
739
  disconnect() {
691
740
  super.disconnect();
@@ -696,3 +745,17 @@ export class HydratableElementController extends ElementController {
696
745
  }
697
746
  }
698
747
  HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
748
+ /**
749
+ * An idle callback ID used to track hydration completion
750
+ */
751
+ HydratableElementController.idleCallbackId = null;
752
+ /**
753
+ * A map of element instances by the name of the custom element they are
754
+ * associated with. The key is the custom element name, and the value is the
755
+ * instances of hydratable elements which currently need to be hydrated.
756
+ *
757
+ * When all of the instances in the set have been hydrated, the set is
758
+ * cleared and removed from the map. If the map is empty, the
759
+ * hydrationComplete callback is invoked.
760
+ */
761
+ HydratableElementController.hydratingInstances = new Map();
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { isFunction } from "../interfaces.js";
11
2
  import { ElementController } from "./element-controller.js";
12
3
  import { FASTElementDefinition, } from "./fast-definitions.js";
@@ -41,11 +32,21 @@ function compose(type, nameOrDef) {
41
32
  return FASTElementDefinition.compose(this, type);
42
33
  }
43
34
  function defineAsync(type, nameOrDef) {
44
- return __awaiter(this, void 0, void 0, function* () {
45
- if (isFunction(type)) {
46
- return (yield FASTElementDefinition.composeAsync(type, nameOrDef)).define().type;
47
- }
48
- return (yield FASTElementDefinition.composeAsync(this, type)).define().type;
35
+ if (isFunction(type)) {
36
+ return new Promise(resolve => {
37
+ FASTElementDefinition.composeAsync(type, nameOrDef).then(value => {
38
+ resolve(value);
39
+ });
40
+ }).then(value => {
41
+ return value.define().type;
42
+ });
43
+ }
44
+ return new Promise(resolve => {
45
+ FASTElementDefinition.composeAsync(this, type).then(value => {
46
+ resolve(value);
47
+ });
48
+ }).then(value => {
49
+ return value.define().type;
49
50
  });
50
51
  }
51
52
  function define(type, nameOrDef) {
@@ -25,3 +25,36 @@
25
25
  result.globalThis = result;
26
26
  }
27
27
  })();
28
+ (function requestIdleCallbackPolyfill() {
29
+ if ("requestIdleCallback" in globalThis) {
30
+ return;
31
+ }
32
+ /**
33
+ * A polyfill for requestIdleCallback that falls back to setTimeout.
34
+ *
35
+ * @param callback - The function to call when the browser is idle.
36
+ * @param options - Options object that may contain a timeout property.
37
+ * @returns An ID that can be used to cancel the callback.
38
+ * @public
39
+ */
40
+ globalThis.requestIdleCallback = function requestIdleCallback(callback, options) {
41
+ const start = Date.now();
42
+ return setTimeout(() => {
43
+ callback({
44
+ didTimeout: (options === null || options === void 0 ? void 0 : options.timeout)
45
+ ? Date.now() - start >= options.timeout
46
+ : false,
47
+ timeRemaining: () => 0,
48
+ });
49
+ }, 1);
50
+ };
51
+ /**
52
+ * A polyfill for cancelIdleCallback that falls back to clearTimeout.
53
+ *
54
+ * @param id - The ID of the callback to cancel.
55
+ * @public
56
+ */
57
+ globalThis.cancelIdleCallback = function cancelIdleCallback(id) {
58
+ clearTimeout(id);
59
+ };
60
+ })();
@@ -12593,6 +12593,37 @@
12593
12593
  "isAbstract": false,
12594
12594
  "name": "install"
12595
12595
  },
12596
+ {
12597
+ "kind": "Property",
12598
+ "canonicalReference": "@microsoft/fast-element!HydratableElementController.lifecycleCallbacks:member",
12599
+ "docComment": "/**\n * Lifecycle callbacks for hydration events\n */\n",
12600
+ "excerptTokens": [
12601
+ {
12602
+ "kind": "Content",
12603
+ "text": "static lifecycleCallbacks?: "
12604
+ },
12605
+ {
12606
+ "kind": "Reference",
12607
+ "text": "HydrationControllerCallbacks",
12608
+ "canonicalReference": "@microsoft/fast-element!HydrationControllerCallbacks:interface"
12609
+ },
12610
+ {
12611
+ "kind": "Content",
12612
+ "text": ";"
12613
+ }
12614
+ ],
12615
+ "isReadonly": false,
12616
+ "isOptional": true,
12617
+ "releaseTag": "Beta",
12618
+ "name": "lifecycleCallbacks",
12619
+ "propertyTypeTokenRange": {
12620
+ "startIndex": 1,
12621
+ "endIndex": 2
12622
+ },
12623
+ "isStatic": true,
12624
+ "isProtected": false,
12625
+ "isAbstract": false
12626
+ },
12596
12627
  {
12597
12628
  "kind": "Property",
12598
12629
  "canonicalReference": "@microsoft/fast-element!HydratableElementController#needsHydration:member",
@@ -152,6 +152,39 @@ const noop = () => void 0;
152
152
  result.globalThis = result;
153
153
  }
154
154
  })();
155
+ (function requestIdleCallbackPolyfill() {
156
+ if ("requestIdleCallback" in globalThis) {
157
+ return;
158
+ }
159
+ /**
160
+ * A polyfill for requestIdleCallback that falls back to setTimeout.
161
+ *
162
+ * @param callback - The function to call when the browser is idle.
163
+ * @param options - Options object that may contain a timeout property.
164
+ * @returns An ID that can be used to cancel the callback.
165
+ * @public
166
+ */
167
+ globalThis.requestIdleCallback = function requestIdleCallback(callback, options) {
168
+ const start = Date.now();
169
+ return setTimeout(() => {
170
+ callback({
171
+ didTimeout: (options === null || options === void 0 ? void 0 : options.timeout)
172
+ ? Date.now() - start >= options.timeout
173
+ : false,
174
+ timeRemaining: () => 0,
175
+ });
176
+ }, 1);
177
+ };
178
+ /**
179
+ * A polyfill for cancelIdleCallback that falls back to clearTimeout.
180
+ *
181
+ * @param id - The ID of the callback to cancel.
182
+ * @public
183
+ */
184
+ globalThis.cancelIdleCallback = function cancelIdleCallback(id) {
185
+ clearTimeout(id);
186
+ };
187
+ })();
155
188
 
156
189
  // ensure FAST global - duplicated debug.ts
157
190
  const propConfig = {
@@ -6023,6 +6056,21 @@ const needsHydrationAttribute = "needs-hydration";
6023
6056
  * @beta
6024
6057
  */
6025
6058
  class HydratableElementController extends ElementController {
6059
+ /**
6060
+ * Adds the current element instance to the hydrating instances map
6061
+ */
6062
+ addHydratingInstance() {
6063
+ if (!HydratableElementController.hydratingInstances) {
6064
+ return;
6065
+ }
6066
+ const name = this.definition.name;
6067
+ let instances = HydratableElementController.hydratingInstances.get(name);
6068
+ if (!instances) {
6069
+ instances = new Set();
6070
+ HydratableElementController.hydratingInstances.set(name, instances);
6071
+ }
6072
+ instances.add(this.source);
6073
+ }
6026
6074
  /**
6027
6075
  * Configure lifecycle callbacks for hydration events
6028
6076
  */
@@ -6032,17 +6080,30 @@ class HydratableElementController extends ElementController {
6032
6080
  }
6033
6081
  static hydrationObserverHandler(records) {
6034
6082
  for (const record of records) {
6035
- HydratableElementController.hydrationObserver.unobserve(record.target);
6036
- record.target.$fastController.connect();
6083
+ if (!record.target.hasAttribute(deferHydrationAttribute)) {
6084
+ HydratableElementController.hydrationObserver.unobserve(record.target);
6085
+ record.target.$fastController.connect();
6086
+ }
6037
6087
  }
6038
6088
  }
6039
6089
  /**
6040
- * Checks if all elements have completed hydration and dispatches event if complete
6090
+ * Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
6091
+ * Then resets the ElementController strategy to the default so that future elements
6092
+ * don't use the HydratableElementController.
6093
+ *
6094
+ * @param deadline - the idle deadline object
6041
6095
  */
6042
- static checkHydrationComplete() {
6043
- var _a, _b;
6044
- if (!document.querySelector(`[${needsHydrationAttribute}]`)) {
6045
- (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.hydrationComplete) === null || _b === void 0 ? void 0 : _b.call(_a);
6096
+ static checkHydrationComplete(deadline) {
6097
+ var _a, _b, _c;
6098
+ if (deadline.didTimeout) {
6099
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
6100
+ return;
6101
+ }
6102
+ // If there are no more hydrating instances, invoke the hydrationComplete callback
6103
+ if (((_a = HydratableElementController.hydratingInstances) === null || _a === void 0 ? void 0 : _a.size) === 0) {
6104
+ (_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.hydrationComplete) === null || _c === void 0 ? void 0 : _c.call(_b);
6105
+ // Reset to the default strategy after hydration is complete
6106
+ ElementController.setStrategy(ElementController);
6046
6107
  }
6047
6108
  }
6048
6109
  static forCustomElement(element, override) {
@@ -6055,15 +6116,17 @@ class HydratableElementController extends ElementController {
6055
6116
  return super.forCustomElement(element, override);
6056
6117
  }
6057
6118
  connect() {
6058
- var _a, _b, _c, _d, _e, _f;
6119
+ var _a, _b, _c, _d, _e;
6059
6120
  // Initialize needsHydration on first connect
6060
- if (this.needsHydration === undefined) {
6061
- this.needsHydration =
6062
- this.source.getAttribute(needsHydrationAttribute) !== null;
6121
+ this.needsHydration =
6122
+ (_a = this.needsHydration) !== null && _a !== void 0 ? _a : this.source.getAttribute(needsHydrationAttribute) !== null;
6123
+ if (this.needsHydration) {
6124
+ (_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementWillHydrate) === null || _c === void 0 ? void 0 : _c.call(_b, this.definition.name);
6063
6125
  }
6064
6126
  // If the `defer-hydration` attribute exists on the source,
6065
6127
  // wait for it to be removed before continuing connection behavior.
6066
6128
  if (this.source.hasAttribute(deferHydrationAttribute)) {
6129
+ this.addHydratingInstance();
6067
6130
  HydratableElementController.hydrationObserver.observe(this.source, {
6068
6131
  attributeFilter: [deferHydrationAttribute],
6069
6132
  });
@@ -6075,20 +6138,19 @@ class HydratableElementController extends ElementController {
6075
6138
  // class
6076
6139
  if (!this.needsHydration) {
6077
6140
  super.connect();
6141
+ this.removeHydratingInstance();
6078
6142
  return;
6079
6143
  }
6080
6144
  if (this.stage !== 3 /* Stages.disconnected */) {
6081
6145
  return;
6082
6146
  }
6083
- // Callback: Before hydration has started
6084
- (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementWillHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
6085
6147
  this.stage = 0 /* Stages.connecting */;
6086
6148
  this.bindObservables();
6087
6149
  this.connectBehaviors();
6088
- const element = this.source;
6089
- const host = (_c = getShadowRoot(element)) !== null && _c !== void 0 ? _c : element;
6090
6150
  if (this.template) {
6091
6151
  if (isHydratable(this.template)) {
6152
+ const element = this.source;
6153
+ const host = (_d = getShadowRoot(element)) !== null && _d !== void 0 ? _d : element;
6092
6154
  let firstChild = host.firstChild;
6093
6155
  let lastChild = host.lastChild;
6094
6156
  if (element.shadowRoot === null) {
@@ -6103,7 +6165,7 @@ class HydratableElementController extends ElementController {
6103
6165
  }
6104
6166
  }
6105
6167
  this.view = this.template.hydrate(firstChild, lastChild, element);
6106
- (_d = this.view) === null || _d === void 0 ? void 0 : _d.bind(this.source);
6168
+ (_e = this.view) === null || _e === void 0 ? void 0 : _e.bind(this.source);
6107
6169
  }
6108
6170
  else {
6109
6171
  this.renderTemplate(this.template);
@@ -6113,11 +6175,31 @@ class HydratableElementController extends ElementController {
6113
6175
  this.stage = 1 /* Stages.connected */;
6114
6176
  this.source.removeAttribute(needsHydrationAttribute);
6115
6177
  this.needsInitialization = this.needsHydration = false;
6178
+ this.removeHydratingInstance();
6116
6179
  Observable.notify(this, isConnectedPropertyName);
6180
+ }
6181
+ /**
6182
+ * Removes the current element instance from the hydrating instances map
6183
+ */
6184
+ removeHydratingInstance() {
6185
+ var _a, _b;
6186
+ if (!HydratableElementController.hydratingInstances) {
6187
+ return;
6188
+ }
6189
+ const name = this.definition.name;
6190
+ const instances = HydratableElementController.hydratingInstances.get(name);
6117
6191
  // Callback: After hydration has finished
6118
- (_f = (_e = HydratableElementController.lifecycleCallbacks) === null || _e === void 0 ? void 0 : _e.elementDidHydrate) === null || _f === void 0 ? void 0 : _f.call(_e, this.definition.name);
6119
- // Check if hydration is complete after this element is hydrated
6120
- HydratableElementController.checkHydrationComplete();
6192
+ (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
6193
+ if (instances) {
6194
+ instances.delete(this.source);
6195
+ if (!instances.size) {
6196
+ HydratableElementController.hydratingInstances.delete(name);
6197
+ }
6198
+ if (HydratableElementController.idleCallbackId) {
6199
+ cancelIdleCallback(HydratableElementController.idleCallbackId);
6200
+ }
6201
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
6202
+ }
6121
6203
  }
6122
6204
  disconnect() {
6123
6205
  super.disconnect();
@@ -6128,6 +6210,20 @@ class HydratableElementController extends ElementController {
6128
6210
  }
6129
6211
  }
6130
6212
  HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
6213
+ /**
6214
+ * An idle callback ID used to track hydration completion
6215
+ */
6216
+ HydratableElementController.idleCallbackId = null;
6217
+ /**
6218
+ * A map of element instances by the name of the custom element they are
6219
+ * associated with. The key is the custom element name, and the value is the
6220
+ * instances of hydratable elements which currently need to be hydrated.
6221
+ *
6222
+ * When all of the instances in the set have been hydrated, the set is
6223
+ * cleared and removed from the map. If the map is empty, the
6224
+ * hydrationComplete callback is invoked.
6225
+ */
6226
+ HydratableElementController.hydratingInstances = new Map();
6131
6227
 
6132
6228
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
6133
6229
  function createFASTElement(BaseType) {
@@ -6160,11 +6256,21 @@ function compose(type, nameOrDef) {
6160
6256
  return FASTElementDefinition.compose(this, type);
6161
6257
  }
6162
6258
  function defineAsync(type, nameOrDef) {
6163
- return __awaiter(this, void 0, void 0, function* () {
6164
- if (isFunction(type)) {
6165
- return (yield FASTElementDefinition.composeAsync(type, nameOrDef)).define().type;
6166
- }
6167
- return (yield FASTElementDefinition.composeAsync(this, type)).define().type;
6259
+ if (isFunction(type)) {
6260
+ return new Promise(resolve => {
6261
+ FASTElementDefinition.composeAsync(type, nameOrDef).then(value => {
6262
+ resolve(value);
6263
+ });
6264
+ }).then(value => {
6265
+ return value.define().type;
6266
+ });
6267
+ }
6268
+ return new Promise(resolve => {
6269
+ FASTElementDefinition.composeAsync(this, type).then(value => {
6270
+ resolve(value);
6271
+ });
6272
+ }).then(value => {
6273
+ return value.define().type;
6168
6274
  });
6169
6275
  }
6170
6276
  function define(type, nameOrDef) {