@microsoft/fast-html 1.0.0-alpha.3 → 1.0.0-alpha.31

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.
Files changed (56) hide show
  1. package/README.md +255 -18
  2. package/dist/dts/components/element.d.ts +10 -0
  3. package/dist/dts/components/index.d.ts +3 -1
  4. package/dist/dts/components/observer-map.d.ts +26 -0
  5. package/dist/dts/components/schema.d.ts +144 -0
  6. package/dist/dts/components/template.d.ts +83 -7
  7. package/dist/dts/components/utilities.d.ts +109 -18
  8. package/dist/dts/fixtures/lifecycle-callbacks/lifecycle-callbacks.spec.d.ts +1 -0
  9. package/dist/dts/fixtures/lifecycle-callbacks/main.d.ts +5 -0
  10. package/dist/dts/fixtures/observer-map/main.d.ts +1 -0
  11. package/dist/dts/fixtures/observer-map/observer-map.spec.d.ts +1 -0
  12. package/dist/dts/index.d.ts +1 -1
  13. package/dist/esm/components/element.js +28 -0
  14. package/dist/esm/components/index.js +3 -1
  15. package/dist/esm/components/observer-map.js +53 -0
  16. package/dist/esm/components/observer-map.spec.js +19 -0
  17. package/dist/esm/components/schema.js +250 -0
  18. package/dist/esm/components/schema.spec.js +485 -0
  19. package/dist/esm/components/template.js +199 -111
  20. package/dist/esm/components/utilities.js +741 -43
  21. package/dist/esm/components/utilities.spec.js +317 -44
  22. package/dist/esm/fixtures/attribute/main.js +3 -2
  23. package/dist/esm/fixtures/binding/binding.spec.js +6 -0
  24. package/dist/esm/fixtures/binding/main.js +13 -2
  25. package/dist/esm/fixtures/children/children.spec.js +4 -0
  26. package/dist/esm/fixtures/children/main.js +3 -2
  27. package/dist/esm/fixtures/dot-syntax/dot-syntax.spec.js +109 -2
  28. package/dist/esm/fixtures/dot-syntax/main.js +30 -4
  29. package/dist/esm/fixtures/event/event.spec.js +28 -5
  30. package/dist/esm/fixtures/event/main.js +21 -5
  31. package/dist/esm/fixtures/lifecycle-callbacks/lifecycle-callbacks.spec.js +166 -0
  32. package/dist/esm/fixtures/lifecycle-callbacks/main.js +126 -0
  33. package/dist/esm/fixtures/observer-map/main.js +375 -0
  34. package/dist/esm/fixtures/observer-map/observer-map.spec.js +251 -0
  35. package/dist/esm/fixtures/ref/main.js +3 -2
  36. package/dist/esm/fixtures/ref/ref.spec.js +2 -6
  37. package/dist/esm/fixtures/repeat/main.js +27 -2
  38. package/dist/esm/fixtures/repeat/repeat.spec.js +16 -6
  39. package/dist/esm/fixtures/slotted/main.js +15 -4
  40. package/dist/esm/fixtures/slotted/slotted.spec.js +18 -19
  41. package/dist/esm/fixtures/when/main.js +139 -2
  42. package/dist/esm/fixtures/when/when.spec.js +64 -1
  43. package/dist/esm/index.js +1 -1
  44. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  45. package/dist/fast-html.api.json +333 -0
  46. package/dist/fast-html.d.ts +282 -6
  47. package/dist/fast-html.untrimmed.d.ts +282 -6
  48. package/package.json +12 -7
  49. package/rules/attribute-directives.yml +38 -0
  50. package/rules/call-expression-with-event-argument.yml +41 -0
  51. package/rules/member-expression.yml +33 -0
  52. package/rules/tag-function-to-template-literal.yml +16 -0
  53. package/dist/esm/fixtures/partial/main.js +0 -31
  54. package/dist/esm/fixtures/partial/partial.spec.js +0 -14
  55. /package/dist/dts/{fixtures/partial/main.d.ts → components/observer-map.spec.d.ts} +0 -0
  56. /package/dist/dts/{fixtures/partial/partial.spec.d.ts → components/schema.spec.d.ts} +0 -0
@@ -1,16 +1,42 @@
1
- import { TemplateElement } from "@microsoft/fast-html";
2
1
  import { FASTElement } from "@microsoft/fast-element";
2
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
3
3
  class TestElement extends FASTElement {
4
4
  constructor() {
5
5
  super(...arguments);
6
6
  this.object = {
7
- foo: "bar",
7
+ b: "bar",
8
+ a: {
9
+ b2: {
10
+ c: "FOO",
11
+ },
12
+ },
13
+ };
14
+ this.handleBClick = () => {
15
+ this.object.b = "Hello";
16
+ };
17
+ this.handleAB1Click = () => {
18
+ if (this.object.a) {
19
+ this.object.a.b1 = "World";
20
+ }
21
+ else {
22
+ this.object.a = {
23
+ b1: "World",
24
+ };
25
+ }
26
+ };
27
+ this.handleAB2CClick = () => {
28
+ this.object.a.b2.c = "Pluto";
8
29
  };
9
30
  }
10
31
  }
11
- TestElement.define({
32
+ RenderableFASTElement(TestElement).defineAsync({
12
33
  name: "test-element",
34
+ templateOptions: "defer-and-hydrate",
13
35
  });
14
- TemplateElement.define({
36
+ TemplateElement.options({
37
+ "test-element": {
38
+ observerMap: "all",
39
+ },
40
+ }).define({
15
41
  name: "f-template",
16
42
  });
@@ -1,12 +1,35 @@
1
1
  import { __awaiter } from "tslib";
2
2
  import { expect, test } from "@playwright/test";
3
3
  test.describe("f-template", () => __awaiter(void 0, void 0, void 0, function* () {
4
- test("create an event attribute", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
4
+ test("create an event attribute without arguments", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
5
5
  yield page.goto("/event");
6
6
  const customElement = yield page.locator("test-element");
7
- let msgCount = 0;
8
- yield page.on("console", msg => msgCount++);
9
- yield customElement.locator("button").click();
10
- expect(msgCount).toEqual(1);
7
+ let message;
8
+ yield page.on("console", msg => message = msg.text());
9
+ yield customElement.locator("button").nth(0).click();
10
+ expect(message).toEqual("no args");
11
+ }));
12
+ test("create an event attribute with an event argument", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
13
+ yield page.goto("/event");
14
+ const customElement = yield page.locator("test-element");
15
+ let message;
16
+ yield page.on("console", msg => message = msg.text());
17
+ yield customElement.locator("button").nth(1).click();
18
+ expect(message).toEqual("click");
19
+ }));
20
+ test("create an event attribute with an attribute argument", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
21
+ yield page.goto("/event");
22
+ const customElement = yield page.locator("test-element");
23
+ let message;
24
+ yield page.on("console", msg => message = msg.text());
25
+ yield customElement.locator("button").nth(2).click();
26
+ expect(message).toEqual("bar");
27
+ }));
28
+ test("should properly bind events with `this`", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
29
+ yield page.goto("/event");
30
+ const customElement = yield page.locator("test-element");
31
+ yield expect(customElement).toHaveJSProperty("foo", "bar");
32
+ yield customElement.locator("button").nth(3).click();
33
+ yield expect(customElement).toHaveJSProperty("foo", "modified-by-click");
11
34
  }));
12
35
  }));
@@ -1,15 +1,31 @@
1
- import { TemplateElement } from "@microsoft/fast-html";
2
- import { FASTElement } from "@microsoft/fast-element";
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
3
+ import { attr, FASTElement } from "@microsoft/fast-element";
3
4
  class TestElement extends FASTElement {
4
5
  constructor() {
5
6
  super(...arguments);
6
- this.handleClick = () => {
7
- console.log("click!");
7
+ this.foo = "";
8
+ this.handleNoArgsClick = () => {
9
+ console.log("no args");
10
+ };
11
+ this.handleEventArgClick = (e) => {
12
+ console.log(e.type);
13
+ };
14
+ this.handleAttributeArgClick = (foo) => {
15
+ console.log(foo);
16
+ };
17
+ this.handleModifyAttributeClick = () => {
18
+ this.foo = "modified-by-click";
8
19
  };
9
20
  }
10
21
  }
11
- TestElement.define({
22
+ __decorate([
23
+ attr,
24
+ __metadata("design:type", String)
25
+ ], TestElement.prototype, "foo", void 0);
26
+ RenderableFASTElement(TestElement).defineAsync({
12
27
  name: "test-element",
28
+ templateOptions: "defer-and-hydrate",
13
29
  });
14
30
  TemplateElement.define({
15
31
  name: "f-template",
@@ -0,0 +1,166 @@
1
+ import { __awaiter } from "tslib";
2
+ import { expect, test } from "@playwright/test";
3
+ test.describe("Lifecycle Callbacks", () => __awaiter(void 0, void 0, void 0, function* () {
4
+ test("should invoke all lifecycle callbacks in correct order for simple element", ({ page, }) => __awaiter(void 0, void 0, void 0, function* () {
5
+ yield page.goto("/lifecycle-callbacks");
6
+ // Wait for hydration to complete
7
+ yield page.waitForFunction(() => window.getHydrationCompleteStatus());
8
+ const events = yield page.evaluate(() => window.lifecycleEvents);
9
+ // Verify callbacks for simple-element were called
10
+ const simpleElementEvents = events.filter((e) => e.name === "simple-element");
11
+ expect(simpleElementEvents.length).toBeGreaterThan(0);
12
+ // Check that callbacks were called in the correct order
13
+ const callbackOrder = simpleElementEvents.map((e) => e.callback);
14
+ expect(callbackOrder).toContain("elementDidRegister");
15
+ expect(callbackOrder).toContain("templateWillUpdate");
16
+ expect(callbackOrder).toContain("templateDidUpdate");
17
+ expect(callbackOrder).toContain("elementDidDefine");
18
+ expect(callbackOrder).toContain("elementWillHydrate");
19
+ expect(callbackOrder).toContain("elementDidHydrate");
20
+ // Verify the order is correct
21
+ const registerIndex = callbackOrder.indexOf("elementDidRegister");
22
+ const willUpdateIndex = callbackOrder.indexOf("templateWillUpdate");
23
+ const didUpdateIndex = callbackOrder.indexOf("templateDidUpdate");
24
+ const defineIndex = callbackOrder.indexOf("elementDidDefine");
25
+ const willHydrateIndex = callbackOrder.indexOf("elementWillHydrate");
26
+ const didHydrateIndex = callbackOrder.indexOf("elementDidHydrate");
27
+ expect(registerIndex).toBeLessThan(willUpdateIndex);
28
+ expect(willUpdateIndex).toBeLessThan(didUpdateIndex);
29
+ expect(didUpdateIndex).toBeLessThan(defineIndex);
30
+ expect(willHydrateIndex).toBeGreaterThan(-1);
31
+ expect(didHydrateIndex).toBeGreaterThan(willHydrateIndex);
32
+ }));
33
+ test("should invoke callbacks for multiple elements", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
34
+ yield page.goto("/lifecycle-callbacks");
35
+ yield page.waitForFunction(() => window.getHydrationCompleteStatus());
36
+ const events = yield page.evaluate(() => window.lifecycleEvents);
37
+ // Check that callbacks were invoked for all elements
38
+ const elementNames = new Set(events.map((e) => e.name));
39
+ expect(elementNames.has("simple-element")).toBe(true);
40
+ expect(elementNames.has("complex-element")).toBe(true);
41
+ expect(elementNames.has("nested-element")).toBe(true);
42
+ }));
43
+ test("should invoke elementWillHydrate before hydration", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
44
+ yield page.goto("/lifecycle-callbacks");
45
+ yield page.waitForFunction(() => window.getHydrationCompleteStatus());
46
+ const events = yield page.evaluate(() => window.lifecycleEvents);
47
+ const willHydrateEvents = events.filter((e) => e.callback === "elementWillHydrate");
48
+ const didHydrateEvents = events.filter((e) => e.callback === "elementDidHydrate");
49
+ // Every elementWillHydrate should be followed by elementDidHydrate for the same element
50
+ willHydrateEvents.forEach((willEvent) => {
51
+ const correspondingDidEvent = didHydrateEvents.find((e) => e.name === willEvent.name);
52
+ expect(correspondingDidEvent).toBeDefined();
53
+ });
54
+ }));
55
+ test("should invoke hydrationComplete callback after all elements hydrate", ({ page, }) => __awaiter(void 0, void 0, void 0, function* () {
56
+ yield page.goto("/lifecycle-callbacks");
57
+ const hydrationComplete = yield page.waitForFunction(() => window.getHydrationCompleteStatus(), { timeout: 5000 });
58
+ expect(hydrationComplete).toBeTruthy();
59
+ const events = yield page.evaluate(() => window.lifecycleEvents);
60
+ // Verify hydrationComplete callback was invoked
61
+ const hydrationCompleteEvents = events.filter((e) => e.callback === "hydrationComplete");
62
+ expect(hydrationCompleteEvents.length).toBe(1);
63
+ // Verify all elements are hydrated
64
+ const simpleElement = yield page.locator("simple-element");
65
+ const complexElement = yield page.locator("complex-element");
66
+ const nestedElement = yield page.locator("nested-element");
67
+ yield expect(simpleElement).not.toHaveAttribute("needs-hydration");
68
+ yield expect(complexElement).not.toHaveAttribute("needs-hydration");
69
+ yield expect(nestedElement).not.toHaveAttribute("needs-hydration");
70
+ // Verify hydrationComplete was called AFTER all individual element hydrations
71
+ const lastElementDidHydrateIndex = events.reduce((maxIndex, e, index) => {
72
+ return e.callback === "elementDidHydrate" ? index : maxIndex;
73
+ }, -1);
74
+ const hydrationCompleteIndex = events.findIndex((e) => e.callback === "hydrationComplete");
75
+ expect(hydrationCompleteIndex).toBeGreaterThan(lastElementDidHydrateIndex);
76
+ }));
77
+ test("should handle complex element with observer map", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
78
+ yield page.goto("/lifecycle-callbacks");
79
+ yield page.waitForFunction(() => window.getHydrationCompleteStatus());
80
+ const complexElement = yield page.locator("complex-element");
81
+ // Verify the element has hydrated correctly
82
+ yield expect(complexElement).toHaveText(/Complex/);
83
+ yield expect(complexElement).not.toHaveAttribute("needs-hydration");
84
+ const events = yield page.evaluate(() => window.lifecycleEvents);
85
+ const complexEvents = events.filter((e) => e.name === "complex-element");
86
+ // Complex element should have all lifecycle callbacks
87
+ expect(complexEvents.length).toBeGreaterThan(0);
88
+ expect(complexEvents.some((e) => e.callback === "elementDidHydrate")).toBe(true);
89
+ }));
90
+ test("should handle nested elements correctly", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
91
+ yield page.goto("/lifecycle-callbacks");
92
+ yield page.waitForFunction(() => window.getHydrationCompleteStatus());
93
+ const events = yield page.evaluate(() => window.lifecycleEvents);
94
+ // Both parent complex-element and nested nested-element should have callbacks
95
+ const complexEvents = events.filter((e) => e.name === "complex-element");
96
+ const nestedEvents = events.filter((e) => e.name === "nested-element");
97
+ expect(complexEvents.length).toBeGreaterThan(0);
98
+ expect(nestedEvents.length).toBeGreaterThan(0);
99
+ // Both should have hydrated
100
+ expect(complexEvents.some((e) => e.callback === "elementDidHydrate")).toBe(true);
101
+ expect(nestedEvents.some((e) => e.callback === "elementDidHydrate")).toBe(true);
102
+ }));
103
+ test("should handle deferred hydration element", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
104
+ yield page.goto("/lifecycle-callbacks");
105
+ // The deferred element has a prepare() method that delays hydration
106
+ yield page.waitForFunction(() => window.getHydrationCompleteStatus(), {
107
+ timeout: 5000,
108
+ });
109
+ const events = yield page.evaluate(() => window.lifecycleEvents);
110
+ const deferredEvents = events.filter((e) => e.name === "deferred-element");
111
+ // Should have hydration callbacks
112
+ expect(deferredEvents.some((e) => e.callback === "elementWillHydrate")).toBe(true);
113
+ expect(deferredEvents.some((e) => e.callback === "elementDidHydrate")).toBe(true);
114
+ const deferredElement = yield page.locator("deferred-element");
115
+ yield expect(deferredElement).not.toHaveAttribute("needs-hydration");
116
+ yield expect(deferredElement).not.toHaveAttribute("defer-hydration");
117
+ }));
118
+ test("should verify template and hydration callbacks are invoked", ({ page, }) => __awaiter(void 0, void 0, void 0, function* () {
119
+ yield page.goto("/lifecycle-callbacks");
120
+ yield page.waitForFunction(() => window.getHydrationCompleteStatus());
121
+ const events = yield page.evaluate(() => window.lifecycleEvents);
122
+ // For each element, verify both template and hydration callbacks are invoked
123
+ // Note: These callbacks can be interleaved as template processing is async
124
+ const elementNames = ["simple-element", "complex-element", "nested-element"];
125
+ elementNames.forEach(name => {
126
+ const elementEvents = events.filter((e) => e.name === name);
127
+ const callbacks = elementEvents.map((e) => e.callback);
128
+ // Verify all expected callbacks were invoked
129
+ expect(callbacks).toContain("elementDidRegister");
130
+ expect(callbacks).toContain("templateWillUpdate");
131
+ expect(callbacks).toContain("templateDidUpdate");
132
+ expect(callbacks).toContain("elementDidDefine");
133
+ expect(callbacks).toContain("elementWillHydrate");
134
+ expect(callbacks).toContain("elementDidHydrate");
135
+ // Verify hydration callbacks are in order
136
+ const willHydrateIndex = callbacks.indexOf("elementWillHydrate");
137
+ const didHydrateIndex = callbacks.indexOf("elementDidHydrate");
138
+ expect(willHydrateIndex).toBeLessThan(didHydrateIndex);
139
+ // Verify template callbacks are in order
140
+ const registerIndex = callbacks.indexOf("elementDidRegister");
141
+ const willUpdateIndex = callbacks.indexOf("templateWillUpdate");
142
+ const didUpdateIndex = callbacks.indexOf("templateDidUpdate");
143
+ const defineIndex = callbacks.indexOf("elementDidDefine");
144
+ expect(registerIndex).toBeLessThan(willUpdateIndex);
145
+ expect(willUpdateIndex).toBeLessThan(didUpdateIndex);
146
+ expect(didUpdateIndex).toBeLessThan(defineIndex);
147
+ });
148
+ }));
149
+ test("should properly hydrate elements and maintain functionality", ({ page, }) => __awaiter(void 0, void 0, void 0, function* () {
150
+ yield page.goto("/lifecycle-callbacks");
151
+ yield page.waitForFunction(() => window.getHydrationCompleteStatus());
152
+ const simpleElement = yield page.locator("simple-element");
153
+ const complexElement = yield page.locator("complex-element");
154
+ // Verify simple element displays correctly
155
+ yield expect(simpleElement).toHaveText("Hello World");
156
+ // Verify complex element displays correctly
157
+ yield expect(complexElement).toHaveText(/Complex/);
158
+ // Verify elements are interactive after hydration
159
+ const currentCount = yield complexElement.evaluate((el) => el.count);
160
+ expect(currentCount).toBe(0);
161
+ // Interact with the element
162
+ yield complexElement.evaluate((el) => el.increment());
163
+ const newCount = yield complexElement.evaluate((el) => el.count);
164
+ expect(newCount).toBe(1);
165
+ }));
166
+ }));
@@ -0,0 +1,126 @@
1
+ import { __awaiter, __decorate, __metadata } from "tslib";
2
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
3
+ import { attr, FASTElement, observable } from "@microsoft/fast-element";
4
+ // Track lifecycle callbacks for testing
5
+ export const lifecycleEvents = [];
6
+ // Track hydration complete
7
+ export let hydrationCompleteEmitted = false;
8
+ // Configure lifecycle callbacks
9
+ TemplateElement.config({
10
+ elementDidRegister(name) {
11
+ lifecycleEvents.push({ callback: "elementDidRegister", name });
12
+ },
13
+ templateWillUpdate(name) {
14
+ lifecycleEvents.push({ callback: "templateWillUpdate", name });
15
+ },
16
+ templateDidUpdate(name) {
17
+ lifecycleEvents.push({ callback: "templateDidUpdate", name });
18
+ },
19
+ elementDidDefine(name) {
20
+ lifecycleEvents.push({ callback: "elementDidDefine", name });
21
+ },
22
+ elementWillHydrate(name) {
23
+ lifecycleEvents.push({ callback: "elementWillHydrate", name });
24
+ },
25
+ elementDidHydrate(name) {
26
+ lifecycleEvents.push({ callback: "elementDidHydrate", name });
27
+ },
28
+ hydrationComplete() {
29
+ lifecycleEvents.push({ callback: "hydrationComplete" });
30
+ hydrationCompleteEmitted = true;
31
+ },
32
+ });
33
+ // Simple element with basic property
34
+ class SimpleElement extends FASTElement {
35
+ constructor() {
36
+ super(...arguments);
37
+ this.message = "Hello";
38
+ }
39
+ }
40
+ __decorate([
41
+ attr,
42
+ __metadata("design:type", String)
43
+ ], SimpleElement.prototype, "message", void 0);
44
+ RenderableFASTElement(SimpleElement).defineAsync({
45
+ name: "simple-element",
46
+ templateOptions: "defer-and-hydrate",
47
+ });
48
+ // Complex element with multiple properties and methods
49
+ class ComplexElement extends FASTElement {
50
+ constructor() {
51
+ super(...arguments);
52
+ this.title = "Complex";
53
+ this.count = 0;
54
+ this.items = [];
55
+ }
56
+ increment() {
57
+ this.count++;
58
+ }
59
+ addItem(item) {
60
+ this.items = [...this.items, item];
61
+ }
62
+ }
63
+ __decorate([
64
+ attr,
65
+ __metadata("design:type", String)
66
+ ], ComplexElement.prototype, "title", void 0);
67
+ __decorate([
68
+ observable,
69
+ __metadata("design:type", Number)
70
+ ], ComplexElement.prototype, "count", void 0);
71
+ __decorate([
72
+ observable,
73
+ __metadata("design:type", Array)
74
+ ], ComplexElement.prototype, "items", void 0);
75
+ RenderableFASTElement(ComplexElement).defineAsync({
76
+ name: "complex-element",
77
+ templateOptions: "defer-and-hydrate",
78
+ });
79
+ // Nested element
80
+ class NestedElement extends FASTElement {
81
+ constructor() {
82
+ super(...arguments);
83
+ this.label = "Nested";
84
+ }
85
+ }
86
+ __decorate([
87
+ attr,
88
+ __metadata("design:type", String)
89
+ ], NestedElement.prototype, "label", void 0);
90
+ RenderableFASTElement(NestedElement).defineAsync({
91
+ name: "nested-element",
92
+ templateOptions: "defer-and-hydrate",
93
+ });
94
+ // Element with deferred hydration
95
+ class DeferredElement extends FASTElement {
96
+ constructor() {
97
+ super(...arguments);
98
+ this.status = "pending";
99
+ }
100
+ prepare() {
101
+ return __awaiter(this, void 0, void 0, function* () {
102
+ // Simulate async work
103
+ yield new Promise(resolve => setTimeout(resolve, 100));
104
+ this.status = "ready";
105
+ });
106
+ }
107
+ }
108
+ __decorate([
109
+ attr,
110
+ __metadata("design:type", String)
111
+ ], DeferredElement.prototype, "status", void 0);
112
+ RenderableFASTElement(DeferredElement).defineAsync({
113
+ name: "deferred-element",
114
+ templateOptions: "defer-and-hydrate",
115
+ });
116
+ // Define templates
117
+ TemplateElement.options({
118
+ "complex-element": {
119
+ observerMap: "all",
120
+ },
121
+ }).define({
122
+ name: "f-template",
123
+ });
124
+ // Make lifecycleEvents available globally for testing
125
+ window.lifecycleEvents = lifecycleEvents;
126
+ window.getHydrationCompleteStatus = () => hydrationCompleteEmitted;