@microsoft/fast-element 3.0.0-rc.1 → 3.0.0-rc.2

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 (286) hide show
  1. package/CHANGELOG.md +51 -1
  2. package/README.md +50 -14
  3. package/dist/context/context.api.json +13 -13
  4. package/dist/declarative/declarative.api.json +654 -15
  5. package/dist/di/di.api.json +15 -15
  6. package/dist/dts/__test__/helpers.d.ts +6 -0
  7. package/dist/dts/__test__/setup-node.d.ts +0 -0
  8. package/dist/dts/binding/binding.d.ts +15 -5
  9. package/dist/dts/binding/one-time.d.ts +1 -1
  10. package/dist/dts/binding/one-way.d.ts +1 -1
  11. package/dist/dts/binding/signal.d.ts +1 -1
  12. package/dist/dts/binding/two-way.d.ts +1 -1
  13. package/dist/dts/components/attributes.d.ts +1 -1
  14. package/dist/dts/components/enable-hydration.d.ts +22 -2
  15. package/dist/dts/components/fast-definitions.d.ts +7 -4
  16. package/dist/dts/components/fast-element.d.ts +42 -12
  17. package/dist/dts/components/hydration-tracker.d.ts +47 -4
  18. package/dist/dts/components/hydration.d.ts +5 -0
  19. package/dist/dts/context.d.ts +7 -7
  20. package/dist/dts/declarative/debug.d.ts +2 -3
  21. package/dist/dts/declarative/index.d.ts +3 -2
  22. package/dist/dts/declarative/interfaces.d.ts +1 -2
  23. package/dist/dts/declarative/template.d.ts +2 -1
  24. package/dist/dts/declarative/utilities.d.ts +50 -4
  25. package/dist/dts/di/di.d.ts +6 -6
  26. package/dist/dts/dom-policy.d.ts +22 -4
  27. package/dist/dts/dom.d.ts +4 -16
  28. package/dist/dts/hydration/diagnostics.d.ts +93 -0
  29. package/dist/dts/hydration/hydration-debugger.d.ts +35 -0
  30. package/dist/dts/hydration/messages.d.ts +62 -0
  31. package/dist/dts/hydration/target-builder.d.ts +26 -1
  32. package/dist/dts/hydration.d.ts +7 -3
  33. package/dist/dts/index.d.ts +7 -3
  34. package/dist/dts/interfaces.d.ts +1 -0
  35. package/dist/dts/observation/observable.d.ts +3 -3
  36. package/dist/dts/platform.d.ts +20 -4
  37. package/dist/dts/registry.d.ts +1 -0
  38. package/dist/dts/templating/children.d.ts +1 -1
  39. package/dist/dts/templating/compiler.d.ts +1 -1
  40. package/dist/dts/templating/html-binding-directive.d.ts +6 -2
  41. package/dist/dts/templating/html-directive.d.ts +2 -1
  42. package/dist/dts/templating/hydration-view.d.ts +24 -3
  43. package/dist/dts/templating/ref.d.ts +1 -1
  44. package/dist/dts/templating/render.d.ts +2 -2
  45. package/dist/dts/templating/repeat.d.ts +1 -1
  46. package/dist/dts/templating/slotted.d.ts +1 -1
  47. package/dist/dts/templating/template.d.ts +5 -5
  48. package/dist/dts/templating/when.d.ts +1 -1
  49. package/dist/dts/testing/fakes.d.ts +4 -4
  50. package/dist/esm/__test__/helpers.js +22 -0
  51. package/dist/esm/__test__/setup-node.js +18 -0
  52. package/dist/esm/binding/two-way.js +1 -2
  53. package/dist/esm/components/attributes.js +12 -8
  54. package/dist/esm/components/element-controller.js +11 -6
  55. package/dist/esm/components/enable-hydration.js +27 -3
  56. package/dist/esm/components/fast-definitions.js +19 -18
  57. package/dist/esm/components/hydration-tracker.js +34 -5
  58. package/dist/esm/components/hydration.js +85 -6
  59. package/dist/esm/debug.js +1 -0
  60. package/dist/esm/declarative/attribute-map.js +2 -1
  61. package/dist/esm/declarative/debug.js +0 -1
  62. package/dist/esm/declarative/index.js +1 -0
  63. package/dist/esm/declarative/interfaces.js +0 -1
  64. package/dist/esm/declarative/observer-map-utilities.js +58 -55
  65. package/dist/esm/declarative/template-bridge.js +4 -14
  66. package/dist/esm/declarative/template.js +4 -3
  67. package/dist/esm/declarative/utilities.js +236 -1
  68. package/dist/esm/di/di.js +2 -1
  69. package/dist/esm/dom-policy.js +33 -4
  70. package/dist/esm/hydration/diagnostics.js +50 -0
  71. package/dist/esm/hydration/hydration-debugger.js +112 -0
  72. package/dist/esm/hydration/messages.js +84 -0
  73. package/dist/esm/hydration/target-builder.js +65 -19
  74. package/dist/esm/hydration.js +3 -1
  75. package/dist/esm/index.js +6 -2
  76. package/dist/esm/interfaces.js +1 -0
  77. package/dist/esm/metadata.js +2 -8
  78. package/dist/esm/observation/notifier.js +2 -4
  79. package/dist/esm/registry.js +1 -0
  80. package/dist/esm/templating/html-binding-directive.js +1 -1
  81. package/dist/esm/templating/hydration-view.js +20 -27
  82. package/dist/esm/templating/render.js +39 -18
  83. package/dist/esm/templating/repeat.js +51 -17
  84. package/dist/esm/templating/view.js +1 -1
  85. package/dist/esm/testing/fixture.js +2 -2
  86. package/dist/esm/testing/timeout.js +2 -2
  87. package/dist/fast-element.api.json +1329 -99
  88. package/dist/fast-element.d.ts +147 -66
  89. package/dist/fast-element.debug.js +392 -99
  90. package/dist/fast-element.debug.min.js +2 -2
  91. package/dist/fast-element.js +392 -99
  92. package/dist/fast-element.min.js +2 -2
  93. package/dist/fast-element.untrimmed.d.ts +133 -70
  94. package/dist/hydration/hydration.api.json +1280 -57
  95. package/dist/styles/styles.api.json +1 -1
  96. package/package.json +21 -9
  97. package/ARCHITECTURE_FASTELEMENT.md +0 -63
  98. package/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md +0 -36
  99. package/ARCHITECTURE_INTRO.md +0 -10
  100. package/ARCHITECTURE_OVERVIEW.md +0 -52
  101. package/ARCHITECTURE_UPDATES.md +0 -11
  102. package/CHANGELOG.json +0 -2275
  103. package/DECLARATIVE_DESIGN.md +0 -806
  104. package/DECLARATIVE_HTML.md +0 -470
  105. package/DECLARATIVE_MIGRATION.md +0 -215
  106. package/DECLARATIVE_RENDERING.md +0 -530
  107. package/DECLARATIVE_RENDERING_LIFECYCLE.md +0 -288
  108. package/DECLARATIVE_SCHEMA_OBSERVER_MAP.md +0 -489
  109. package/DESIGN.md +0 -615
  110. package/MIGRATION.md +0 -387
  111. package/SIZES.md +0 -25
  112. package/api-extractor.arrays.json +0 -15
  113. package/api-extractor.context.json +0 -15
  114. package/api-extractor.declarative.json +0 -15
  115. package/api-extractor.di.json +0 -15
  116. package/api-extractor.hydration.json +0 -15
  117. package/api-extractor.styles.json +0 -15
  118. package/biome.json +0 -4
  119. package/docs/ACKNOWLEDGEMENTS.md +0 -12
  120. package/docs/api-report.api.md +0 -1299
  121. package/docs/arrays/api-report.api.md +0 -114
  122. package/docs/context/api-report.api.md +0 -69
  123. package/docs/declarative/api-report.api.md +0 -397
  124. package/docs/di/api-report.api.md +0 -315
  125. package/docs/fast-element-2-changes.md +0 -15
  126. package/docs/hydration/api-report.api.md +0 -285
  127. package/docs/styles/api-report.api.md +0 -135
  128. package/playwright.config.ts +0 -26
  129. package/playwright.declarative.config.ts +0 -26
  130. package/playwright.declarative.webui.config.ts +0 -20
  131. package/scripts/declarative/build-fixtures-with-webui.js +0 -135
  132. package/scripts/declarative/build-fixtures.js +0 -49
  133. package/scripts/declarative/build-fixtures.utilities.js +0 -101
  134. package/scripts/measure-sizes.js +0 -219
  135. package/scripts/run-api-extractor.js +0 -70
  136. package/test/declarative/fixtures/README.md +0 -72
  137. package/test/declarative/fixtures/WRITING_FIXTURES.md +0 -330
  138. package/test/declarative/fixtures/bindings/README.md +0 -12
  139. package/test/declarative/fixtures/bindings/attribute/entry.html +0 -13
  140. package/test/declarative/fixtures/bindings/attribute/fast-build.config.json +0 -6
  141. package/test/declarative/fixtures/bindings/attribute/index.html +0 -25
  142. package/test/declarative/fixtures/bindings/attribute/main.ts +0 -41
  143. package/test/declarative/fixtures/bindings/attribute/state.json +0 -8
  144. package/test/declarative/fixtures/bindings/attribute/templates.html +0 -11
  145. package/test/declarative/fixtures/bindings/content/entry.html +0 -12
  146. package/test/declarative/fixtures/bindings/content/fast-build.config.json +0 -6
  147. package/test/declarative/fixtures/bindings/content/index.html +0 -19
  148. package/test/declarative/fixtures/bindings/content/main.ts +0 -27
  149. package/test/declarative/fixtures/bindings/content/state.json +0 -4
  150. package/test/declarative/fixtures/bindings/content/templates.html +0 -6
  151. package/test/declarative/fixtures/bindings/dot-syntax/entry.html +0 -11
  152. package/test/declarative/fixtures/bindings/dot-syntax/fast-build.config.json +0 -6
  153. package/test/declarative/fixtures/bindings/dot-syntax/index.html +0 -47
  154. package/test/declarative/fixtures/bindings/dot-syntax/main.ts +0 -59
  155. package/test/declarative/fixtures/bindings/dot-syntax/state.json +0 -16
  156. package/test/declarative/fixtures/bindings/dot-syntax/templates.html +0 -17
  157. package/test/declarative/fixtures/bindings/event/entry.html +0 -11
  158. package/test/declarative/fixtures/bindings/event/fast-build.config.json +0 -6
  159. package/test/declarative/fixtures/bindings/event/index.html +0 -43
  160. package/test/declarative/fixtures/bindings/event/main.ts +0 -43
  161. package/test/declarative/fixtures/bindings/event/state.json +0 -3
  162. package/test/declarative/fixtures/bindings/event/templates.html +0 -18
  163. package/test/declarative/fixtures/bindings/host/entry.html +0 -40
  164. package/test/declarative/fixtures/bindings/host/fast-build.config.json +0 -6
  165. package/test/declarative/fixtures/bindings/host/index.html +0 -96
  166. package/test/declarative/fixtures/bindings/host/main.ts +0 -222
  167. package/test/declarative/fixtures/bindings/host/state.json +0 -9
  168. package/test/declarative/fixtures/bindings/host/templates.html +0 -55
  169. package/test/declarative/fixtures/directives/README.md +0 -12
  170. package/test/declarative/fixtures/directives/children/entry.html +0 -11
  171. package/test/declarative/fixtures/directives/children/fast-build.config.json +0 -6
  172. package/test/declarative/fixtures/directives/children/index.html +0 -15
  173. package/test/declarative/fixtures/directives/children/main.ts +0 -22
  174. package/test/declarative/fixtures/directives/children/state.json +0 -3
  175. package/test/declarative/fixtures/directives/children/templates.html +0 -3
  176. package/test/declarative/fixtures/directives/ref/entry.html +0 -11
  177. package/test/declarative/fixtures/directives/ref/fast-build.config.json +0 -6
  178. package/test/declarative/fixtures/directives/ref/index.html +0 -15
  179. package/test/declarative/fixtures/directives/ref/main.ts +0 -17
  180. package/test/declarative/fixtures/directives/ref/state.json +0 -1
  181. package/test/declarative/fixtures/directives/ref/templates.html +0 -3
  182. package/test/declarative/fixtures/directives/repeat/entry.html +0 -21
  183. package/test/declarative/fixtures/directives/repeat/fast-build.config.json +0 -6
  184. package/test/declarative/fixtures/directives/repeat/index.html +0 -133
  185. package/test/declarative/fixtures/directives/repeat/main.ts +0 -110
  186. package/test/declarative/fixtures/directives/repeat/sprites.svg +0 -8
  187. package/test/declarative/fixtures/directives/repeat/state.json +0 -10
  188. package/test/declarative/fixtures/directives/repeat/templates.html +0 -75
  189. package/test/declarative/fixtures/directives/slotted/entry.html +0 -17
  190. package/test/declarative/fixtures/directives/slotted/fast-build.config.json +0 -6
  191. package/test/declarative/fixtures/directives/slotted/index.html +0 -27
  192. package/test/declarative/fixtures/directives/slotted/main.ts +0 -29
  193. package/test/declarative/fixtures/directives/slotted/state.json +0 -1
  194. package/test/declarative/fixtures/directives/slotted/templates.html +0 -7
  195. package/test/declarative/fixtures/directives/when/entry.html +0 -51
  196. package/test/declarative/fixtures/directives/when/fast-build.config.json +0 -6
  197. package/test/declarative/fixtures/directives/when/index.html +0 -136
  198. package/test/declarative/fixtures/directives/when/main.ts +0 -172
  199. package/test/declarative/fixtures/directives/when/state.json +0 -12
  200. package/test/declarative/fixtures/directives/when/templates.html +0 -75
  201. package/test/declarative/fixtures/ecosystem/README.md +0 -11
  202. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/entry.html +0 -12
  203. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/fast-build.config.json +0 -6
  204. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/index.html +0 -20
  205. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/main.ts +0 -68
  206. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/state.json +0 -4
  207. package/test/declarative/fixtures/ecosystem/declarative-no-hydration/templates.html +0 -7
  208. package/test/declarative/fixtures/ecosystem/errors/entry.html +0 -12
  209. package/test/declarative/fixtures/ecosystem/errors/fast-build.config.json +0 -6
  210. package/test/declarative/fixtures/ecosystem/errors/index.html +0 -20
  211. package/test/declarative/fixtures/ecosystem/errors/main.ts +0 -17
  212. package/test/declarative/fixtures/ecosystem/errors/state.json +0 -1
  213. package/test/declarative/fixtures/ecosystem/errors/templates.html +0 -7
  214. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/entry.html +0 -17
  215. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/fast-build.config.json +0 -6
  216. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/index.html +0 -56
  217. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/main.ts +0 -134
  218. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/state.json +0 -12
  219. package/test/declarative/fixtures/ecosystem/lifecycle-callbacks/templates.html +0 -34
  220. package/test/declarative/fixtures/ecosystem/performance-metrics/entry.html +0 -25
  221. package/test/declarative/fixtures/ecosystem/performance-metrics/fast-build.config.json +0 -6
  222. package/test/declarative/fixtures/ecosystem/performance-metrics/fast-card.css +0 -10
  223. package/test/declarative/fixtures/ecosystem/performance-metrics/index.html +0 -181
  224. package/test/declarative/fixtures/ecosystem/performance-metrics/main.ts +0 -58
  225. package/test/declarative/fixtures/ecosystem/performance-metrics/state.json +0 -6
  226. package/test/declarative/fixtures/ecosystem/performance-metrics/templates.html +0 -15
  227. package/test/declarative/fixtures/extensions/README.md +0 -15
  228. package/test/declarative/fixtures/extensions/attribute-map/entry.html +0 -14
  229. package/test/declarative/fixtures/extensions/attribute-map/fast-build.config.json +0 -6
  230. package/test/declarative/fixtures/extensions/attribute-map/index.html +0 -31
  231. package/test/declarative/fixtures/extensions/attribute-map/main.ts +0 -40
  232. package/test/declarative/fixtures/extensions/attribute-map/state.json +0 -4
  233. package/test/declarative/fixtures/extensions/attribute-map/templates.html +0 -14
  234. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/entry.html +0 -12
  235. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/fast-build.config.json +0 -7
  236. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/index.html +0 -25
  237. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/main.ts +0 -31
  238. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/state.json +0 -5
  239. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy/templates.html +0 -11
  240. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/entry.html +0 -13
  241. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/fast-build.config.json +0 -7
  242. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/index.html +0 -23
  243. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/main.ts +0 -37
  244. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/state.json +0 -1
  245. package/test/declarative/fixtures/extensions/attribute-map-naming-strategy-camel-case/templates.html +0 -9
  246. package/test/declarative/fixtures/extensions/observer-map/entry.html +0 -15
  247. package/test/declarative/fixtures/extensions/observer-map/fast-build.config.json +0 -6
  248. package/test/declarative/fixtures/extensions/observer-map/index.html +0 -442
  249. package/test/declarative/fixtures/extensions/observer-map/main.ts +0 -482
  250. package/test/declarative/fixtures/extensions/observer-map/state.json +0 -158
  251. package/test/declarative/fixtures/extensions/observer-map/templates.html +0 -172
  252. package/test/declarative/fixtures/extensions/observer-map-config-object/entry.html +0 -16
  253. package/test/declarative/fixtures/extensions/observer-map-config-object/fast-build.config.json +0 -6
  254. package/test/declarative/fixtures/extensions/observer-map-config-object/index.html +0 -27
  255. package/test/declarative/fixtures/extensions/observer-map-config-object/main.ts +0 -53
  256. package/test/declarative/fixtures/extensions/observer-map-config-object/state.json +0 -9
  257. package/test/declarative/fixtures/extensions/observer-map-config-object/templates.html +0 -12
  258. package/test/declarative/fixtures/extensions/observer-map-deep-merge/README.md +0 -98
  259. package/test/declarative/fixtures/extensions/observer-map-deep-merge/entry.html +0 -156
  260. package/test/declarative/fixtures/extensions/observer-map-deep-merge/fast-build.config.json +0 -6
  261. package/test/declarative/fixtures/extensions/observer-map-deep-merge/index.html +0 -376
  262. package/test/declarative/fixtures/extensions/observer-map-deep-merge/main.ts +0 -366
  263. package/test/declarative/fixtures/extensions/observer-map-deep-merge/state.json +0 -69
  264. package/test/declarative/fixtures/extensions/observer-map-deep-merge/templates.html +0 -91
  265. package/test/declarative/fixtures/extensions/observer-map-properties/entry.html +0 -14
  266. package/test/declarative/fixtures/extensions/observer-map-properties/fast-build.config.json +0 -6
  267. package/test/declarative/fixtures/extensions/observer-map-properties/index.html +0 -110
  268. package/test/declarative/fixtures/extensions/observer-map-properties/main.ts +0 -175
  269. package/test/declarative/fixtures/extensions/observer-map-properties/state.json +0 -29
  270. package/test/declarative/fixtures/extensions/observer-map-properties/templates.html +0 -55
  271. package/test/declarative/fixtures/scenarios/README.md +0 -7
  272. package/test/declarative/fixtures/scenarios/nested-elements/entry.html +0 -16
  273. package/test/declarative/fixtures/scenarios/nested-elements/fast-build.config.json +0 -6
  274. package/test/declarative/fixtures/scenarios/nested-elements/index.html +0 -126
  275. package/test/declarative/fixtures/scenarios/nested-elements/main.ts +0 -214
  276. package/test/declarative/fixtures/scenarios/nested-elements/state.json +0 -10
  277. package/test/declarative/fixtures/scenarios/nested-elements/templates.html +0 -54
  278. package/test/declarative/index.html +0 -12
  279. package/test/declarative/vite.config.ts +0 -55
  280. package/test/declarative-main.ts +0 -6
  281. package/test/extension-subpaths-main.ts +0 -9
  282. package/test/index.html +0 -11
  283. package/test/main.ts +0 -109
  284. package/test/pure-declarative-main.ts +0 -1
  285. package/test/vite.config.ts +0 -19
  286. package/tsconfig.api-extractor.json +0 -6
@@ -0,0 +1,112 @@
1
+ import { DOMAspect } from "../dom.js";
2
+ import { aspectLabelAttribute, aspectLabelBooleanAttribute, aspectLabelContent, aspectLabelEvent, aspectLabelProperty, aspectLabelTokenList, aspectLabelUnknown, formatAspect, formatExpectedTarget, formatRichMismatchMessage, unknownHostName, } from "./messages.js";
3
+ const aspectLabelsByCode = Object.freeze({
4
+ [DOMAspect.attribute]: aspectLabelAttribute,
5
+ [DOMAspect.booleanAttribute]: aspectLabelBooleanAttribute,
6
+ [DOMAspect.property]: aspectLabelProperty,
7
+ [DOMAspect.content]: aspectLabelContent,
8
+ [DOMAspect.tokenList]: aspectLabelTokenList,
9
+ [DOMAspect.event]: aspectLabelEvent,
10
+ });
11
+ const defaultSnippetLength = 500;
12
+ function describeAspect(aspectType, sourceAspect) {
13
+ var _a;
14
+ const base = aspectType !== undefined
15
+ ? ((_a = aspectLabelsByCode[aspectType]) !== null && _a !== void 0 ? _a : aspectLabelUnknown)
16
+ : aspectLabelUnknown;
17
+ return formatAspect(base, sourceAspect);
18
+ }
19
+ function describeExpectedTarget(factory) {
20
+ var _a;
21
+ const aspectType = factory.aspectType;
22
+ const sourceAspect = factory.sourceAspect;
23
+ return {
24
+ tagName: (_a = factory.targetTagName) !== null && _a !== void 0 ? _a : null,
25
+ aspect: describeAspect(aspectType, sourceAspect),
26
+ };
27
+ }
28
+ function stripEmptyComments(root) {
29
+ var _a;
30
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
31
+ const empties = [];
32
+ let current;
33
+ while ((current = walker.nextNode()) !== null) {
34
+ if (current.data === "") {
35
+ empties.push(current);
36
+ }
37
+ }
38
+ for (const comment of empties) {
39
+ (_a = comment.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(comment);
40
+ }
41
+ }
42
+ function truncate(value, maxLength) {
43
+ return value.length > maxLength ? `${value.slice(0, maxLength)}…` : value;
44
+ }
45
+ function serializeNodeForError(node, maxLength = defaultSnippetLength) {
46
+ const wrapper = document.createElement("div");
47
+ wrapper.appendChild(node.cloneNode(true));
48
+ stripEmptyComments(wrapper);
49
+ return truncate(wrapper.innerHTML.trim(), maxLength);
50
+ }
51
+ function serializeRangeForError(first, last, maxLength = defaultSnippetLength) {
52
+ const wrapper = document.createElement("div");
53
+ let current = first;
54
+ while (current !== null) {
55
+ wrapper.appendChild(current.cloneNode(true));
56
+ if (current === last) {
57
+ break;
58
+ }
59
+ current = current.nextSibling;
60
+ }
61
+ stripEmptyComments(wrapper);
62
+ return truncate(wrapper.innerHTML.trim(), maxLength);
63
+ }
64
+ function formatMismatchMessage(hostName, expected, received) {
65
+ const host = (hostName !== null && hostName !== void 0 ? hostName : unknownHostName).toLowerCase();
66
+ const expectedText = typeof expected === "string"
67
+ ? expected
68
+ : formatExpectedTarget(expected.tagName, expected.aspect);
69
+ return formatRichMismatchMessage(host, expectedText, received.html);
70
+ }
71
+ const richDiagnostic = {
72
+ formatBindingMismatch(factory, firstChild, lastChild, hostName) {
73
+ const expected = describeExpectedTarget(factory);
74
+ const received = {
75
+ html: serializeRangeForError(firstChild, lastChild),
76
+ };
77
+ const result = {
78
+ message: formatMismatchMessage(hostName, expected, received),
79
+ expected,
80
+ received,
81
+ };
82
+ return result;
83
+ },
84
+ formatStructuralError(node, hostName, expectedDescription) {
85
+ const received = {
86
+ html: serializeNodeForError(node),
87
+ };
88
+ const result = {
89
+ message: formatMismatchMessage(hostName, expectedDescription, received),
90
+ expected: expectedDescription,
91
+ received,
92
+ };
93
+ return result;
94
+ },
95
+ };
96
+ /**
97
+ * Returns a {@link HydrationDebugger} that, when supplied to
98
+ * `enableHydration({ debugger })`, installs the rich hydration mismatch
99
+ * formatter: a single-line "Expected … / Received …" message plus an HTML
100
+ * snippet of the SSR DOM and structured `expected`/`received` fields on
101
+ * the thrown error (both `HydrationBindingError` and
102
+ * `HydrationTargetElementError`).
103
+ *
104
+ * Without the debugger, hydration errors emit only a minimal one-line
105
+ * message pointing at this function — keeping the runtime hydration cost
106
+ * small for production bundles that do not need rich diagnostics.
107
+ *
108
+ * @public
109
+ */
110
+ export function hydrationDebugger() {
111
+ return { diagnostic: richDiagnostic };
112
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Centralized hydration mismatch message strings used by both the default
3
+ * minimal `HydrationDiagnostic` and the opt-in `hydrationDebugger` rich
4
+ * formatter, and by the structural-error throw sites in
5
+ * `target-builder.ts`.
6
+ *
7
+ * Static text is exported as a plain `const`; interpolated text is exported
8
+ * as a small builder function. Plain `export const` declarations tree-shake
9
+ * better than frozen-object property bags, so unused strings drop out of
10
+ * bundles cleanly.
11
+ */
12
+ /**
13
+ * Fallback host tag name used when a hydration mismatch is detected on a
14
+ * node that is not inside a shadow root.
15
+ */
16
+ export const unknownHostName = "unknown";
17
+ // -- Aspect labels (consumed by the opt-in hydrationDebugger) ----------------
18
+ export const aspectLabelAttribute = "attribute";
19
+ export const aspectLabelBooleanAttribute = "boolean attribute";
20
+ export const aspectLabelProperty = "property";
21
+ export const aspectLabelContent = "content";
22
+ export const aspectLabelTokenList = "token list";
23
+ export const aspectLabelEvent = "event";
24
+ /** Fallback used when the aspectType is missing or unknown. */
25
+ export const aspectLabelUnknown = "binding";
26
+ /**
27
+ * Combines an aspect label with the original source aspect identifier from
28
+ * markup (e.g. `"property className"`). Returns the bare label when no
29
+ * source aspect was captured.
30
+ */
31
+ export function formatAspect(label, sourceAspect) {
32
+ return sourceAspect ? `${label} \`${sourceAspect}\`` : label;
33
+ }
34
+ /**
35
+ * Formats the "Expected" half of the rich hydration mismatch message, e.g.
36
+ * `"<span> with content binding"` or `"content binding"` when no tag is
37
+ * associated with the binding factory.
38
+ */
39
+ export function formatExpectedTarget(tagName, aspect) {
40
+ return tagName
41
+ ? `<${tagName.toLowerCase()}> with ${aspect} binding`
42
+ : `${aspect} binding`;
43
+ }
44
+ /**
45
+ * Default minimal hydration mismatch message used when the
46
+ * `hydrationDebugger` opt-in is not installed. The optional `detail` string
47
+ * carries the structural expectation surfaced by `target-builder.ts`.
48
+ */
49
+ export function formatDefaultMismatchMessage(hostName, detail) {
50
+ const suffix = detail ? `: ${detail}` : "";
51
+ return (`Hydration mismatch in <${hostName}>${suffix}. Install ` +
52
+ `hydrationDebugger() from "@microsoft/fast-element/hydration.js" and ` +
53
+ `pass it as enableHydration({ debugger: hydrationDebugger() }) for an ` +
54
+ `"Expected / Received" report including the SSR HTML snippet.`);
55
+ }
56
+ /**
57
+ * Rich `Expected … / Received …` hydration mismatch message format produced
58
+ * by the `hydrationDebugger` formatter.
59
+ */
60
+ export function formatRichMismatchMessage(hostName, expectedText, receivedHtml) {
61
+ return (`Hydration mismatch in <${hostName}>.\n` +
62
+ ` Expected: ${expectedText}\n` +
63
+ ` Received: ${receivedHtml}`);
64
+ }
65
+ // -- Structural expectations (used by target-builder.ts throw sites) ---------
66
+ export const expectedContentAfterStartMarker = "content following `<!--fe:b-->` content binding marker";
67
+ export const expectedContentEndMarker = "matching `<!--fe:/b-->` content binding close marker";
68
+ export const expectedElementBoundaryEndMarker = "matching `<!--fe:/e-->` element boundary close marker";
69
+ /**
70
+ * Builds the "no more attribute bindings" structural expectation message
71
+ * thrown when an element's `data-fe` count claims more attribute bindings
72
+ * than the compiled template defines.
73
+ */
74
+ export function formatNoMoreAttributeBindings(factoryCount) {
75
+ return `no more attribute bindings (template defines ${factoryCount})`;
76
+ }
77
+ /**
78
+ * Builds the "no more content bindings" structural expectation message
79
+ * thrown when the SSR DOM contains more content binding markers than the
80
+ * compiled template defines.
81
+ */
82
+ export function formatNoMoreContentBindings(factoryCount) {
83
+ return `no more content bindings (template defines ${factoryCount})`;
84
+ }
@@ -1,4 +1,6 @@
1
1
  import { HydrationMarkup } from "../components/hydration.js";
2
+ import { getHostName, getHydrationDiagnostic, } from "./diagnostics.js";
3
+ import { expectedContentAfterStartMarker, expectedContentEndMarker, expectedElementBoundaryEndMarker, formatNoMoreAttributeBindings, formatNoMoreContentBindings, } from "./messages.js";
2
4
  export class HydrationTargetElementError extends Error {
3
5
  constructor(
4
6
  /**
@@ -12,10 +14,24 @@ export class HydrationTargetElementError extends Error {
12
14
  /**
13
15
  * The node to target factory.
14
16
  */
15
- node) {
17
+ node,
18
+ /**
19
+ * Structured description of the binding the hydration walk was
20
+ * attempting to apply when the mismatch was detected. Free-form
21
+ * string for structural errors that do not correspond to a single
22
+ * binding factory.
23
+ */
24
+ expected,
25
+ /**
26
+ * Structured description of the server-rendered DOM that was
27
+ * encountered at the mismatch point.
28
+ */
29
+ received) {
16
30
  super(message);
17
31
  this.factories = factories;
18
32
  this.node = node;
33
+ this.expected = expected;
34
+ this.received = received;
19
35
  }
20
36
  }
21
37
  function isComment(node) {
@@ -64,7 +80,6 @@ export function createRangeForNodes(first, last) {
64
80
  * @returns - A {@link ViewBehaviorTargets } object for the factories in the view.
65
81
  */
66
82
  export function buildViewBindingTargets(firstNode, lastNode, factories) {
67
- var _a, _b;
68
83
  const range = createRangeForNodes(firstNode, lastNode);
69
84
  const treeRoot = range.commonAncestorContainer;
70
85
  const walker = document.createTreeWalker(treeRoot, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_COMMENT + NodeFilter.SHOW_TEXT, {
@@ -77,36 +92,63 @@ export function buildViewBindingTargets(firstNode, lastNode, factories) {
77
92
  const targets = {};
78
93
  const boundaries = {};
79
94
  // Sequential factory pointer — skip host bindings at the start
80
- let factoryPointer = getHydrationIndexOffset(factories);
95
+ const hydrationIndexOffset = getHydrationIndexOffset(factories);
96
+ let factoryPointer = hydrationIndexOffset;
81
97
  let node = (walker.currentNode = firstNode);
82
98
  while (node !== null) {
83
99
  switch (node.nodeType) {
84
100
  case Node.ELEMENT_NODE: {
85
- const count = HydrationMarkup.parseAttributeBindingCount(node);
101
+ const element = node;
102
+ const legacyIndices = HydrationMarkup.parseLegacyAttributeBindingIndices(element);
103
+ if (legacyIndices !== null) {
104
+ for (const index of legacyIndices) {
105
+ const factoryIndex = index + hydrationIndexOffset;
106
+ const factory = factories[factoryIndex];
107
+ if (!factory) {
108
+ const expected = formatNoMoreAttributeBindings(factories.length);
109
+ const result = getHydrationDiagnostic().formatStructuralError(node, getHostName(node), expected);
110
+ throw new HydrationTargetElementError(result.message, factories, element, result.expected, result.received);
111
+ }
112
+ targetFactory(factory, node, targets);
113
+ factoryPointer = Math.max(factoryPointer, factoryIndex + 1);
114
+ }
115
+ HydrationMarkup.removeLegacyAttributeBindingMarkers(element);
116
+ break;
117
+ }
118
+ const count = HydrationMarkup.parseAttributeBindingCount(element);
86
119
  if (count !== null) {
87
120
  for (let i = 0; i < count; i++) {
88
121
  const factory = factories[factoryPointer++];
89
122
  if (!factory) {
90
- throw new HydrationTargetElementError(`HydrationView was unable to successfully target factory on ${node.nodeName} inside ${node.getRootNode().host.nodeName}. This likely indicates a template mismatch between SSR rendering and hydration.`, factories, node);
123
+ const expected = formatNoMoreAttributeBindings(factories.length);
124
+ const result = getHydrationDiagnostic().formatStructuralError(node, getHostName(node), expected);
125
+ throw new HydrationTargetElementError(result.message, factories, node, result.expected, result.received);
91
126
  }
92
127
  targetFactory(factory, node, targets);
93
128
  }
94
- node.removeAttribute(HydrationMarkup.attributeMarkerName);
129
+ element.removeAttribute(HydrationMarkup.attributeMarkerName);
95
130
  }
96
131
  break;
97
132
  }
98
133
  case Node.COMMENT_NODE: {
99
134
  const data = node.data;
100
- if (data === "fe:e") {
135
+ if (HydrationMarkup.isElementBoundaryStartMarker(node)) {
101
136
  // Element boundary — clear start marker and skip subtree
102
137
  node.data = "";
103
138
  skipToElementBoundaryEnd(walker, factories, node);
104
139
  }
105
- else if (data === "fe:b") {
140
+ else if (HydrationMarkup.isContentBindingStartMarker(data)) {
106
141
  // Content binding — consume next factory
107
- const factory = factories[factoryPointer++];
142
+ const legacyIndex = HydrationMarkup.parseLegacyContentBindingStartIndex(data);
143
+ const factoryIndex = legacyIndex === null
144
+ ? factoryPointer++
145
+ : legacyIndex + hydrationIndexOffset;
146
+ const factory = factories[factoryIndex];
147
+ factoryPointer = Math.max(factoryPointer, factoryIndex + 1);
108
148
  if (!factory) {
109
- throw new HydrationTargetElementError(`HydrationView ran out of factories while processing a content binding marker inside ${(_b = (_a = node.getRootNode().host) === null || _a === void 0 ? void 0 : _a.nodeName) !== null && _b !== void 0 ? _b : "unknown"}. This likely indicates a template mismatch between SSR rendering and hydration.`, factories, node);
149
+ const expected = formatNoMoreContentBindings(factories.length);
150
+ const result = getHydrationDiagnostic().formatStructuralError(node, getHostName(node), expected);
151
+ throw new HydrationTargetElementError(result.message, factories, node, result.expected, result.received);
110
152
  }
111
153
  targetContentBinding(node, walker, factory, factories, targets, boundaries);
112
154
  }
@@ -119,22 +161,23 @@ export function buildViewBindingTargets(firstNode, lastNode, factories) {
119
161
  return { targets, boundaries };
120
162
  }
121
163
  function targetContentBinding(node, walker, factory, factories, targets, boundaries) {
122
- var _a, _b, _c, _d;
123
164
  const nodes = [];
124
165
  let current = walker.nextSibling();
125
166
  node.data = "";
126
167
  if (current === null) {
127
- throw new HydrationTargetElementError(`Error hydrating content binding inside "${(_b = (_a = node.getRootNode().host) === null || _a === void 0 ? void 0 : _a.nodeName) !== null && _b !== void 0 ? _b : "unknown"}": no sibling found after content binding start marker.`, factories, node);
168
+ const expected = expectedContentAfterStartMarker;
169
+ const result = getHydrationDiagnostic().formatStructuralError(node, getHostName(node), expected);
170
+ throw new HydrationTargetElementError(result.message, factories, node, result.expected, result.received);
128
171
  }
129
172
  const first = current;
130
173
  // Balanced depth counting for nested content markers
131
174
  let depth = 0;
132
175
  while (current !== null) {
133
176
  if (isComment(current)) {
134
- if (current.data === "fe:b") {
177
+ if (HydrationMarkup.isContentBindingStartMarker(current.data)) {
135
178
  depth++;
136
179
  }
137
- else if (current.data === "fe:/b") {
180
+ else if (HydrationMarkup.isContentBindingEndMarker(current.data)) {
138
181
  if (depth === 0)
139
182
  break;
140
183
  depth--;
@@ -144,7 +187,9 @@ function targetContentBinding(node, walker, factory, factories, targets, boundar
144
187
  current = walker.nextSibling();
145
188
  }
146
189
  if (current === null) {
147
- throw new HydrationTargetElementError(`Error hydrating content binding inside "${(_d = (_c = node.getRootNode().host) === null || _c === void 0 ? void 0 : _c.nodeName) !== null && _d !== void 0 ? _d : "unknown"}": missing fe:/b end marker.`, factories, node);
190
+ const expected = expectedContentEndMarker;
191
+ const result = getHydrationDiagnostic().formatStructuralError(node, getHostName(node), expected);
192
+ throw new HydrationTargetElementError(result.message, factories, node, result.expected, result.received);
148
193
  }
149
194
  current.data = "";
150
195
  if (nodes.length === 1 && isText(nodes[0])) {
@@ -170,16 +215,15 @@ function targetContentBinding(node, walker, factory, factories, targets, boundar
170
215
  * depth counting to handle nested element boundaries correctly.
171
216
  */
172
217
  function skipToElementBoundaryEnd(walker, factories, startNode) {
173
- var _a, _b;
174
218
  let depth = 0;
175
219
  let current = walker.nextSibling();
176
220
  while (current !== null) {
177
221
  if (isComment(current)) {
178
- if (current.data === "fe:e") {
222
+ if (HydrationMarkup.isElementBoundaryStartMarker(current)) {
179
223
  current.data = "";
180
224
  depth++;
181
225
  }
182
- else if (current.data === "fe:/e") {
226
+ else if (HydrationMarkup.isElementBoundaryEndMarker(current)) {
183
227
  if (depth === 0) {
184
228
  current.data = "";
185
229
  return;
@@ -190,7 +234,9 @@ function skipToElementBoundaryEnd(walker, factories, startNode) {
190
234
  }
191
235
  current = walker.nextSibling();
192
236
  }
193
- throw new HydrationTargetElementError(`HydrationView could not find the end of an element boundary inside ${(_b = (_a = startNode.getRootNode().host) === null || _a === void 0 ? void 0 : _a.nodeName) !== null && _b !== void 0 ? _b : "unknown"}. This likely indicates a template mismatch between SSR rendering and hydration.`, factories, startNode);
237
+ const expected = expectedElementBoundaryEndMarker;
238
+ const result = getHydrationDiagnostic().formatStructuralError(startNode, getHostName(startNode), expected);
239
+ throw new HydrationTargetElementError(result.message, factories, startNode, result.expected, result.received);
194
240
  }
195
241
  /**
196
242
  * Counts how many factories at the start of the array are host bindings (targetNodeId='h').
@@ -1,4 +1,6 @@
1
- export { deferHydrationAttribute, enableHydration, } from "./components/enable-hydration.js";
1
+ export { deferHydrationAttribute, enableHydration, StopHydration, } from "./components/enable-hydration.js";
2
2
  export { isHydratable } from "./components/hydration.js";
3
3
  export { HydrationTracker } from "./components/hydration-tracker.js";
4
+ export { DOMPolicy, } from "./dom-policy.js";
5
+ export { hydrationDebugger } from "./hydration/hydration-debugger.js";
4
6
  export { HydrationBindingError, HydrationStage, } from "./templating/hydration-view.js";
package/dist/esm/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Core APIs for building standards-based Web Components with FAST Element.
3
+ * @packageDocumentation
4
+ */
1
5
  // Kernel
2
6
  // Components
3
7
  export { AttributeConfiguration, AttributeDefinition, attr, booleanConverter, nullableBooleanConverter, nullableNumberConverter, } from "./attr.js";
@@ -10,7 +14,7 @@ export { Signal, signal } from "./binding/signal.js";
10
14
  export { TwoWaySettings, twoWay, } from "./binding/two-way.js";
11
15
  export { ElementController, Stages, } from "./components/element-controller.js";
12
16
  export { FASTElementDefinition, fastElementRegistry, } from "./components/fast-definitions.js";
13
- export { customElement, FASTElement } from "./components/fast-element.js";
17
+ export { customElement, FASTElement, } from "./components/fast-element.js";
14
18
  export { enableDebug } from "./debug.js";
15
19
  // Directives
16
20
  export { ChildrenDirective, children, } from "./directives/children.js";
@@ -19,7 +23,7 @@ export { RefDirective, ref } from "./directives/ref.js";
19
23
  export { RepeatBehavior, RepeatDirective, repeat, } from "./directives/repeat.js";
20
24
  export { SlottedDirective, slotted, } from "./directives/slotted.js";
21
25
  export { when } from "./directives/when.js";
22
- export * from "./dom.js";
26
+ export { DOM, DOMAspect } from "./dom.js";
23
27
  export { DOMPolicy, } from "./dom-policy.js";
24
28
  export { ArrayObserver, lengthOf, Sort, Splice, SpliceStrategy, SpliceStrategySupport, sortedCount, } from "./observation/arrays.js";
25
29
  // Observation
@@ -18,6 +18,7 @@ export var Message;
18
18
  Message[Message["cannotSetTemplatePolicyAfterCompilation"] = 1208] = "cannotSetTemplatePolicyAfterCompilation";
19
19
  Message[Message["blockedByDOMPolicy"] = 1209] = "blockedByDOMPolicy";
20
20
  Message[Message["invalidHydrationAttributeMarker"] = 1210] = "invalidHydrationAttributeMarker";
21
+ Message[Message["duplicateRenderInstruction"] = 1211] = "duplicateRenderInstruction";
21
22
  // 1301 - 1400 Styles
22
23
  // 1401 - 1500 Components
23
24
  Message[Message["missingElementDefinition"] = 1401] = "missingElementDefinition";
@@ -34,19 +34,13 @@ export const Metadata = Object.freeze({
34
34
  * @param Type - The type to get the metadata for.
35
35
  * @returns The metadata array or a frozen empty array if no metadata is found.
36
36
  */
37
- getDesignParamTypes: (Type) => {
38
- var _a;
39
- return ((_a = Reflect.getOwnMetadata(designParamTypesKey, Type)) !== null && _a !== void 0 ? _a : emptyArray);
40
- },
37
+ getDesignParamTypes: (Type) => { var _a; return ((_a = Reflect.getOwnMetadata(designParamTypesKey, Type)) !== null && _a !== void 0 ? _a : emptyArray); },
41
38
  /**
42
39
  * Gets the "annotation:paramtypes" metadata for the specified type.
43
40
  * @param Type - The type to get the metadata for.
44
41
  * @returns The metadata array or a frozen empty array if no metadata is found.
45
42
  */
46
- getAnnotationParamTypes: (Type) => {
47
- var _a;
48
- return ((_a = Reflect.getOwnMetadata(annotationParamTypesKey, Type)) !== null && _a !== void 0 ? _a : emptyArray);
49
- },
43
+ getAnnotationParamTypes: (Type) => { var _a; return ((_a = Reflect.getOwnMetadata(annotationParamTypesKey, Type)) !== null && _a !== void 0 ? _a : emptyArray); },
50
44
  /**
51
45
  * Gets the "annotation:paramtypes" metadata for the specified type. If none is found,
52
46
  * an empty, mutable metadata array is created and added.
@@ -138,12 +138,10 @@ export class PropertyChangeNotifier {
138
138
  var _a, _b;
139
139
  let subscribers;
140
140
  if (propertyToWatch) {
141
- subscribers =
142
- (_a = this.subscribers[propertyToWatch]) !== null && _a !== void 0 ? _a : (this.subscribers[propertyToWatch] = new SubscriberSet(this.subject));
141
+ subscribers = (_a = this.subscribers[propertyToWatch]) !== null && _a !== void 0 ? _a : (this.subscribers[propertyToWatch] = new SubscriberSet(this.subject));
143
142
  }
144
143
  else {
145
- subscribers =
146
- (_b = this.subjectSubscribers) !== null && _b !== void 0 ? _b : (this.subjectSubscribers = new SubscriberSet(this.subject));
144
+ subscribers = (_b = this.subjectSubscribers) !== null && _b !== void 0 ? _b : (this.subjectSubscribers = new SubscriberSet(this.subject));
147
145
  }
148
146
  subscribers.subscribe(subscriber);
149
147
  }
@@ -0,0 +1 @@
1
+ export { FASTElementDefinition, fastElementRegistry, } from "./components/fast-definitions.js";
@@ -158,12 +158,12 @@ export class HTMLBindingDirective {
158
158
  * @param dataBinding - The binding configuration to apply.
159
159
  */
160
160
  constructor(dataBinding) {
161
- this.dataBinding = dataBinding;
162
161
  this.updateTarget = null;
163
162
  /**
164
163
  * The type of aspect to target.
165
164
  */
166
165
  this.aspectType = DOMAspect.content;
166
+ this.dataBinding = dataBinding;
167
167
  }
168
168
  /**
169
169
  * Creates HTML to be used within a template.
@@ -1,5 +1,6 @@
1
1
  var _a;
2
2
  import { Hydratable } from "../components/hydration.js";
3
+ import { getHostName, getHydrationDiagnostic, } from "../hydration/diagnostics.js";
3
4
  import { buildViewBindingTargets, createRangeForNodes, HydrationTargetElementError, targetFactory, } from "../hydration/target-builder.js";
4
5
  import { SourceLifetime } from "../observation/observable.js";
5
6
  import { makeSerializationNoop } from "../platform.js";
@@ -30,11 +31,23 @@ export class HydrationBindingError extends Error {
30
31
  * String representation of the HTML in the template that
31
32
  * threw the binding error.
32
33
  */
33
- templateString) {
34
+ templateString,
35
+ /**
36
+ * Structured description of the binding the hydration walk was
37
+ * attempting to apply when the mismatch was detected.
38
+ */
39
+ expected,
40
+ /**
41
+ * Structured description of the server-rendered DOM that was
42
+ * encountered at the mismatch point.
43
+ */
44
+ received) {
34
45
  super(message);
35
46
  this.factory = factory;
36
47
  this.fragment = fragment;
37
48
  this.templateString = templateString;
49
+ this.expected = expected;
50
+ this.received = received;
38
51
  }
39
52
  }
40
53
  export class HydrationView extends DefaultExecutionContext {
@@ -120,13 +133,12 @@ export class HydrationView extends DefaultExecutionContext {
120
133
  fragment.appendChild(end);
121
134
  }
122
135
  bind(source, context = this) {
123
- var _b;
136
+ if (this.source === source && this.context === context) {
137
+ return;
138
+ }
124
139
  if (this.hydrationStage !== HydrationStage.hydrated) {
125
140
  this._hydrationStage = HydrationStage.hydrating;
126
141
  }
127
- if (this.source === source) {
128
- return;
129
- }
130
142
  let behaviors = this.behaviors;
131
143
  if (behaviors === null) {
132
144
  this.source = source;
@@ -164,28 +176,9 @@ export class HydrationView extends DefaultExecutionContext {
164
176
  if (typeof templateString !== "string") {
165
177
  templateString = templateString.innerHTML;
166
178
  }
167
- const hostElement = ((_b = this.firstChild) === null || _b === void 0 ? void 0 : _b.getRootNode())
168
- .host;
169
- const hostName = (hostElement === null || hostElement === void 0 ? void 0 : hostElement.nodeName) || "unknown";
170
- const factoryInfo = factory;
171
- // Build detailed error message
172
- const details = [
173
- `HydrationView was unable to successfully target bindings inside "<${hostName.toLowerCase()}>".`,
174
- `\nMismatch Details:`,
175
- ` - Expected target node ID: "${factory.targetNodeId}"`,
176
- ` - Available target IDs: [${Object.keys(this.targets).join(", ") || "none"}]`,
177
- ];
178
- if (factory.targetTagName) {
179
- details.push(` - Expected tag name: "${factory.targetTagName}"`);
180
- }
181
- if (factoryInfo.sourceAspect) {
182
- details.push(` - Source aspect: "${factoryInfo.sourceAspect}"`);
183
- }
184
- if (factoryInfo.aspectType !== undefined) {
185
- details.push(` - Aspect type: ${factoryInfo.aspectType}`);
186
- }
187
- details.push(`\nThis usually means:`, ` 1. The server-rendered HTML doesn't match the client template`, ` 2. The hydration markers are missing or corrupted`, ` 3. The DOM structure was modified before hydration`, `\nTemplate: ${templateString.slice(0, 200)}${templateString.length > 200 ? "..." : ""}`);
188
- throw new HydrationBindingError(details.join("\n"), factory, createRangeForNodes(this.firstChild, this.lastChild).cloneContents(), templateString);
179
+ const fragment = createRangeForNodes(this.firstChild, this.lastChild).cloneContents();
180
+ const result = getHydrationDiagnostic().formatBindingMismatch(factory, this.firstChild, this.lastChild, getHostName(this.firstChild));
181
+ throw new HydrationBindingError(result.message, factory, fragment, templateString, result.expected, result.received);
189
182
  }
190
183
  }
191
184
  }
@@ -4,11 +4,12 @@ import { oneTime } from "../binding/one-time.js";
4
4
  import { oneWay } from "../binding/one-way.js";
5
5
  import { FASTElementDefinition } from "../components/fast-definitions.js";
6
6
  import { isHydratable } from "../components/hydration.js";
7
- import { isFunction, isString } from "../interfaces.js";
7
+ import { isFunction, isString, Message } from "../interfaces.js";
8
+ import { FAST } from "../platform.js";
8
9
  import { HTMLDirective, } from "./html-directive.js";
10
+ import { HydrationStage } from "./hydration-view.js";
9
11
  import { Markup } from "./markup.js";
10
12
  import { html, ViewTemplate, } from "./template.js";
11
- import { HydrationStage } from "./hydration-view.js";
12
13
  /**
13
14
  * A Behavior that enables advanced rendering.
14
15
  * @public
@@ -45,11 +46,10 @@ export class RenderBehavior {
45
46
  if (viewNodes) {
46
47
  this.view = this.template.hydrate(viewNodes.first, viewNodes.last);
47
48
  this.bindView(this.view);
49
+ return;
48
50
  }
49
51
  }
50
- else {
51
- this.refreshView();
52
- }
52
+ this.refreshView();
53
53
  }
54
54
  /**
55
55
  * Unbinds this behavior.
@@ -172,6 +172,32 @@ function instructionToTemplate(def) {
172
172
  }
173
173
  return def.template;
174
174
  }
175
+ function resolveTemplateBindingValue(result, dataBinding, source, context) {
176
+ var _a;
177
+ if (isString(result)) {
178
+ return instructionToTemplate(getForInstance(dataBinding.evaluate(source, context), result));
179
+ }
180
+ if (result instanceof Node) {
181
+ return (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
182
+ }
183
+ return result;
184
+ }
185
+ function adaptTemplateBinding(binding, dataBinding) {
186
+ const evaluateTemplate = binding.evaluate;
187
+ const adapter = Object.create(Object.getPrototypeOf(binding));
188
+ for (const propertyName of Object.getOwnPropertyNames(binding)) {
189
+ if (propertyName !== "evaluate") {
190
+ Object.defineProperty(adapter, propertyName, Object.getOwnPropertyDescriptor(binding, propertyName));
191
+ }
192
+ }
193
+ Object.defineProperty(adapter, "evaluate", {
194
+ configurable: true,
195
+ enumerable: true,
196
+ value: (source, context) => resolveTemplateBindingValue(evaluateTemplate(source, context), dataBinding, source, context),
197
+ writable: true,
198
+ });
199
+ return adapter;
200
+ }
175
201
  function createElementTemplate(tagName, options) {
176
202
  const markup = [];
177
203
  const values = [];
@@ -251,6 +277,13 @@ function register(optionsOrInstruction) {
251
277
  const instruction = instanceOf(optionsOrInstruction)
252
278
  ? optionsOrInstruction
253
279
  : create(optionsOrInstruction);
280
+ if (lookup[instruction.name] !== void 0) {
281
+ const typeName = instruction.type.name || "(anonymous)";
282
+ FAST.warn(Message.duplicateRenderInstruction, {
283
+ type: typeName,
284
+ name: instruction.name,
285
+ });
286
+ }
254
287
  return (lookup[instruction.name] = instruction);
255
288
  }
256
289
  function getByType(type, name) {
@@ -425,19 +458,7 @@ export function render(value, template) {
425
458
  });
426
459
  }
427
460
  else if (template instanceof Binding) {
428
- const evaluateTemplate = template.evaluate;
429
- template.evaluate = (s, c) => {
430
- var _a;
431
- let result = evaluateTemplate(s, c);
432
- if (isString(result)) {
433
- result = instructionToTemplate(getForInstance(dataBinding.evaluate(s, c), result));
434
- }
435
- else if (result instanceof Node) {
436
- result = (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
437
- }
438
- return result;
439
- };
440
- templateBinding = template;
461
+ templateBinding = adaptTemplateBinding(template, dataBinding);
441
462
  }
442
463
  else {
443
464
  templateBinding = oneTime((s, c) => template);