@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
@@ -18,6 +18,7 @@ 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";
@@ -163,6 +164,7 @@ const baseDebugMessages = {
163
164
  [1208 /* cannotSetTemplatePolicyAfterCompilation */]: "The DOM Policy cannot be set after a template is compiled.",
164
165
  [1209 /* blockedByDOMPolicy */]: "'${aspectName}' on '${tagName}' is blocked by the current DOMPolicy.",
165
166
  [1210 /* invalidHydrationAttributeMarker */]: "Invalid data-fe attribute value '${value}'. Expected a positive integer.",
167
+ [1211 /* duplicateRenderInstruction */]: "Replacing existing RenderInstruction for '${type}' with name '${name}'.",
166
168
  [1401 /* missingElementDefinition */]: "Missing FASTElement definition.",
167
169
  [1501 /* noRegistrationForContext */]: "No registration for Context/Interface '${name}'.",
168
170
  [1502 /* noFactoryForResolver */]: "Dependency injection resolver for '${key}' returned a null factory.",
@@ -321,10 +323,40 @@ const DOM = Object.freeze({
321
323
  },
322
324
  });
323
325
 
326
+ const surroundingWhitespaceAndControlChars = /^[\u0000-\u0020\u007F]+|[\u0000-\u0020\u007F]+$/g;
327
+ const whitespaceAndControlChars = /[\u0000-\u0020\u007F]+/g;
328
+ const unsafeURLProtocol = /^(?:javascript|vbscript|data):/;
329
+ function trimURL(value) {
330
+ return value.replace(surroundingWhitespaceAndControlChars, "");
331
+ }
332
+ function decodeURL(value) {
333
+ try {
334
+ return decodeURIComponent(value);
335
+ }
336
+ catch (_a) {
337
+ return value;
338
+ }
339
+ }
340
+ function hasUnsafeURLProtocol(value) {
341
+ let normalized = trimURL(value);
342
+ for (let i = 0; i < 3; ++i) {
343
+ const decoded = decodeURL(normalized);
344
+ if (decoded === normalized) {
345
+ break;
346
+ }
347
+ normalized = trimURL(decoded);
348
+ }
349
+ normalized = normalized.replace(whitespaceAndControlChars, "").toLowerCase();
350
+ return unsafeURLProtocol.test(normalized);
351
+ }
352
+ function sanitizeURL(value) {
353
+ const trimmed = trimURL(value);
354
+ return hasUnsafeURLProtocol(trimmed) ? "" : trimmed;
355
+ }
324
356
  function safeURL(tagName, aspect, aspectName, sink) {
325
357
  return (target, name, value, ...rest) => {
326
358
  if (isString(value)) {
327
- value = value.replace(/(javascript:|vbscript:|data:)/, "");
359
+ value = sanitizeURL(value);
328
360
  }
329
361
  sink(target, name, value, ...rest);
330
362
  };
@@ -586,7 +618,7 @@ function createElementGuards(config, defaults) {
586
618
  break;
587
619
  case undefined:
588
620
  // keep the default
589
- result[tag] = createDOMAspectGuards(overrideValue, {});
621
+ result[tag] = createDOMAspectGuards(defaultValue, {});
590
622
  break;
591
623
  default:
592
624
  // override the default aspects
@@ -1240,14 +1272,10 @@ const AttributeConfiguration = Object.freeze({
1240
1272
  */
1241
1273
  const booleanConverter = {
1242
1274
  toView(value) {
1243
- return value ? "true" : "false";
1275
+ return value ? "" : null;
1244
1276
  },
1245
1277
  fromView(value) {
1246
- return !(value === null ||
1247
- value === void 0 ||
1248
- value === "false" ||
1249
- value === false ||
1250
- value === 0);
1278
+ return !!value;
1251
1279
  },
1252
1280
  };
1253
1281
  /**
@@ -1287,7 +1315,7 @@ const nullableNumberConverter = {
1287
1315
  fromView: toNumber,
1288
1316
  };
1289
1317
  /**
1290
- * An implementation of `Accessor` that supports reactivity,
1318
+ * An implementation of {@link Accessor} that supports reactivity,
1291
1319
  * change callbacks, attribute reflection, and type conversion for
1292
1320
  * custom elements.
1293
1321
  * @public
@@ -1350,7 +1378,15 @@ class AttributeDefinition {
1350
1378
  return;
1351
1379
  }
1352
1380
  this.guards.add(element);
1353
- this.setValue(element, value);
1381
+ if (this.mode === booleanMode) {
1382
+ // Native HTML boolean attribute semantics: presence of the attribute
1383
+ // (any string value, including "") means `true`; `null` (the value
1384
+ // passed by the platform on `removeAttribute`) means `false`.
1385
+ this.setValue(element, value !== null);
1386
+ }
1387
+ else {
1388
+ this.setValue(element, value);
1389
+ }
1354
1390
  this.guards.delete(element);
1355
1391
  }
1356
1392
  tryReflectToAttribute(element) {
@@ -1899,8 +1935,11 @@ const defaultShadowOptions = { mode: "open" };
1899
1935
  const defaultElementOptions = {};
1900
1936
  const fastElementBaseTypes = new Set();
1901
1937
  /**
1902
- * The FAST custom element registry
1903
- * @internal
1938
+ * The FAST custom element registry.
1939
+ * @remarks
1940
+ * This registry stores FAST element definitions by constructor so consumers can
1941
+ * look up the `FASTElementDefinition` associated with an element type or instance.
1942
+ * @public
1904
1943
  */
1905
1944
  const fastElementRegistry = createTypeRegistry();
1906
1945
  const templateResolvers = new WeakMap();
@@ -2165,7 +2204,7 @@ FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
2165
2204
  * @param name - The name of the defined custom element.
2166
2205
  * @alpha
2167
2206
  */
2168
- FASTElementDefinition.register = (name, registry = customElements) => __awaiter(void 0, void 0, void 0, function* () {
2207
+ FASTElementDefinition.register = (name_1, ...args_1) => __awaiter(void 0, [name_1, ...args_1], void 0, function* (name, registry = customElements) {
2169
2208
  const registeredTypes = getRegisteredTypes(registry);
2170
2209
  if (!Object.prototype.hasOwnProperty.call(registeredTypes, name)) {
2171
2210
  Observable.defineProperty(registeredTypes, name);
@@ -2665,7 +2704,8 @@ class ElementController {
2665
2704
  * customElements.define() completed.
2666
2705
  */
2667
2706
  observeLateAttributes() {
2668
- if (getLateAttributeLookup(this.definition) === null) {
2707
+ const lateAttributes = getLateAttributeLookup(this.definition);
2708
+ if (lateAttributes === null) {
2669
2709
  return;
2670
2710
  }
2671
2711
  const element = this.source;
@@ -2686,7 +2726,10 @@ class ElementController {
2686
2726
  controller.onAttributeChangedCallback(attributeName, null, element.getAttribute(attributeName));
2687
2727
  }
2688
2728
  });
2689
- element[lateAttributeObserver].observe(element, { attributes: true });
2729
+ element[lateAttributeObserver].observe(element, {
2730
+ attributes: true,
2731
+ attributeFilter: Object.keys(lateAttributes),
2732
+ });
2690
2733
  }
2691
2734
  /**
2692
2735
  * Connects any existing behaviors on the associated element.
@@ -3465,7 +3508,17 @@ const ref = (propertyName) => new RefDirective(propertyName);
3465
3508
  * Attribute bindings use a single `data-fe` attribute whose value is
3466
3509
  * the count of attribute binding factories targeting the element:
3467
3510
  * <div data-fe="3"> (3 attribute bindings)
3511
+ *
3512
+ * WebUI versions that predate the data-free marker format still emit indexed
3513
+ * markers. The parser below accepts those legacy markers so existing WebUI SSR
3514
+ * output can hydrate against the newer FAST runtime.
3468
3515
  */
3516
+ const legacyBindingStartMarker = /fe-b\$\$start\$\$(\d+)\$\$(.+)\$\$fe-b/;
3517
+ const legacyBindingEndMarker = /fe-b\$\$end\$\$(\d+)\$\$(.+)\$\$fe-b/;
3518
+ const legacyRepeatViewStartMarker = /fe-repeat\$\$start\$\$(\d+)\$\$fe-repeat/;
3519
+ const legacyRepeatViewEndMarker = /fe-repeat\$\$end\$\$(\d+)\$\$fe-repeat/;
3520
+ const legacyElementBoundaryStartMarker = /^(?:.{0,1000})fe-eb\$\$start\$\$(.+?)\$\$fe-eb/;
3521
+ const legacyElementBoundaryEndMarker = /fe-eb\$\$end\$\$(.{0,1000})\$\$fe-eb(?:.{0,1000})$/;
3469
3522
  function isComment$1(node) {
3470
3523
  return node && node.nodeType === Node.COMMENT_NODE;
3471
3524
  }
@@ -3476,6 +3529,8 @@ function isComment$1(node) {
3476
3529
  const HydrationMarkup = Object.freeze({
3477
3530
  // Single attribute marker format (count only)
3478
3531
  attributeMarkerName: "data-fe",
3532
+ legacyAttributeMarkerName: "data-fe-b",
3533
+ legacyCompactAttributeMarkerName: "data-fe-c",
3479
3534
  // Content binding markers (no arguments)
3480
3535
  contentBindingStartMarker() {
3481
3536
  return "fe:b";
@@ -3499,22 +3554,24 @@ const HydrationMarkup = Object.freeze({
3499
3554
  },
3500
3555
  // Detection — simple string equality
3501
3556
  isContentBindingStartMarker(data) {
3502
- return data === "fe:b";
3557
+ return data === "fe:b" || legacyBindingStartMarker.test(data);
3503
3558
  },
3504
3559
  isContentBindingEndMarker(data) {
3505
- return data === "fe:/b";
3560
+ return data === "fe:/b" || legacyBindingEndMarker.test(data);
3506
3561
  },
3507
3562
  isRepeatViewStartMarker(data) {
3508
- return data === "fe:r";
3563
+ return data === "fe:r" || legacyRepeatViewStartMarker.test(data);
3509
3564
  },
3510
3565
  isRepeatViewEndMarker(data) {
3511
- return data === "fe:/r";
3566
+ return data === "fe:/r" || legacyRepeatViewEndMarker.test(data);
3512
3567
  },
3513
3568
  isElementBoundaryStartMarker(node) {
3514
- return isComment$1(node) && node.data === "fe:e";
3569
+ return (isComment$1(node) &&
3570
+ (node.data === "fe:e" || legacyElementBoundaryStartMarker.test(node.data)));
3515
3571
  },
3516
3572
  isElementBoundaryEndMarker(node) {
3517
- return isComment$1(node) && node.data === "fe:/e";
3573
+ return (isComment$1(node) &&
3574
+ (node.data === "fe:/e" || legacyElementBoundaryEndMarker.test(node.data)));
3518
3575
  },
3519
3576
  /**
3520
3577
  * Returns the count of attribute bindings on the element, or null
@@ -3542,7 +3599,72 @@ const HydrationMarkup = Object.freeze({
3542
3599
  }
3543
3600
  return count;
3544
3601
  },
3602
+ parseLegacyAttributeBindingIndices(node) {
3603
+ const indices = [];
3604
+ const attr = node.getAttribute(this.legacyAttributeMarkerName);
3605
+ if (attr !== null) {
3606
+ for (const value of attr.trim().split(/\s+/)) {
3607
+ if (value === "") {
3608
+ continue;
3609
+ }
3610
+ const index = Number(value);
3611
+ if (!Number.isInteger(index) || index < 0) {
3612
+ throw FAST.error(Message.invalidHydrationAttributeMarker, {
3613
+ value: attr,
3614
+ });
3615
+ }
3616
+ indices.push(index);
3617
+ }
3618
+ }
3619
+ const enumeratedPrefix = `${this.legacyAttributeMarkerName}-`;
3620
+ const compactPrefix = `${this.legacyCompactAttributeMarkerName}-`;
3621
+ for (const name of node.getAttributeNames()) {
3622
+ if (name.startsWith(enumeratedPrefix)) {
3623
+ const index = Number(name.slice(enumeratedPrefix.length));
3624
+ if (!Number.isInteger(index) || index < 0) {
3625
+ throw FAST.error(Message.invalidHydrationAttributeMarker, {
3626
+ value: name,
3627
+ });
3628
+ }
3629
+ indices.push(index);
3630
+ }
3631
+ else if (name.startsWith(compactPrefix)) {
3632
+ const [start, count] = name
3633
+ .slice(compactPrefix.length)
3634
+ .split("-")
3635
+ .map(value => Number(value));
3636
+ if (!Number.isInteger(start) ||
3637
+ !Number.isInteger(count) ||
3638
+ start < 0 ||
3639
+ count < 1) {
3640
+ throw FAST.error(Message.invalidHydrationAttributeMarker, {
3641
+ value: name,
3642
+ });
3643
+ }
3644
+ for (let i = 0; i < count; i++) {
3645
+ indices.push(start + i);
3646
+ }
3647
+ }
3648
+ }
3649
+ return indices.length === 0 ? null : indices;
3650
+ },
3651
+ removeLegacyAttributeBindingMarkers(node) {
3652
+ node.removeAttribute(this.legacyAttributeMarkerName);
3653
+ for (const name of node.getAttributeNames()) {
3654
+ if (name.startsWith(`${this.legacyAttributeMarkerName}-`) ||
3655
+ name.startsWith(`${this.legacyCompactAttributeMarkerName}-`)) {
3656
+ node.removeAttribute(name);
3657
+ }
3658
+ }
3659
+ },
3660
+ parseLegacyContentBindingStartIndex(data) {
3661
+ return parseLegacyIntMarker(legacyBindingStartMarker, data);
3662
+ },
3545
3663
  });
3664
+ function parseLegacyIntMarker(pattern, data) {
3665
+ const match = pattern.exec(data);
3666
+ return match === null ? null : Number(match[1]);
3667
+ }
3546
3668
  /**
3547
3669
  * @internal
3548
3670
  */
@@ -4209,6 +4331,94 @@ function sortedCount(array) {
4209
4331
  return array.sorted;
4210
4332
  }
4211
4333
 
4334
+ /**
4335
+ * Centralized hydration mismatch message strings used by both the default
4336
+ * minimal `HydrationDiagnostic` and the opt-in `hydrationDebugger` rich
4337
+ * formatter, and by the structural-error throw sites in
4338
+ * `target-builder.ts`.
4339
+ *
4340
+ * Static text is exported as a plain `const`; interpolated text is exported
4341
+ * as a small builder function. Plain `export const` declarations tree-shake
4342
+ * better than frozen-object property bags, so unused strings drop out of
4343
+ * bundles cleanly.
4344
+ */
4345
+ /**
4346
+ * Fallback host tag name used when a hydration mismatch is detected on a
4347
+ * node that is not inside a shadow root.
4348
+ */
4349
+ const unknownHostName = "unknown";
4350
+ /**
4351
+ * Default minimal hydration mismatch message used when the
4352
+ * `hydrationDebugger` opt-in is not installed. The optional `detail` string
4353
+ * carries the structural expectation surfaced by `target-builder.ts`.
4354
+ */
4355
+ function formatDefaultMismatchMessage(hostName, detail) {
4356
+ const suffix = detail ? `: ${detail}` : "";
4357
+ return (`Hydration mismatch in <${hostName}>${suffix}. Install ` +
4358
+ `hydrationDebugger() from "@microsoft/fast-element/hydration.js" and ` +
4359
+ `pass it as enableHydration({ debugger: hydrationDebugger() }) for an ` +
4360
+ `"Expected / Received" report including the SSR HTML snippet.`);
4361
+ }
4362
+ // -- Structural expectations (used by target-builder.ts throw sites) ---------
4363
+ const expectedContentAfterStartMarker = "content following `<!--fe:b-->` content binding marker";
4364
+ const expectedContentEndMarker = "matching `<!--fe:/b-->` content binding close marker";
4365
+ const expectedElementBoundaryEndMarker = "matching `<!--fe:/e-->` element boundary close marker";
4366
+ /**
4367
+ * Builds the "no more attribute bindings" structural expectation message
4368
+ * thrown when an element's `data-fe` count claims more attribute bindings
4369
+ * than the compiled template defines.
4370
+ */
4371
+ function formatNoMoreAttributeBindings(factoryCount) {
4372
+ return `no more attribute bindings (template defines ${factoryCount})`;
4373
+ }
4374
+ /**
4375
+ * Builds the "no more content bindings" structural expectation message
4376
+ * thrown when the SSR DOM contains more content binding markers than the
4377
+ * compiled template defines.
4378
+ */
4379
+ function formatNoMoreContentBindings(factoryCount) {
4380
+ return `no more content bindings (template defines ${factoryCount})`;
4381
+ }
4382
+
4383
+ function formatMinimalMessage(hostName, detail) {
4384
+ const host = (hostName !== null && hostName !== void 0 ? hostName : unknownHostName).toLowerCase();
4385
+ return formatDefaultMismatchMessage(host, detail);
4386
+ }
4387
+ const defaultDiagnostic = {
4388
+ formatBindingMismatch(_factory, _firstChild, _lastChild, hostName) {
4389
+ return {
4390
+ message: formatMinimalMessage(hostName, undefined),
4391
+ };
4392
+ },
4393
+ formatStructuralError(_node, hostName, expectedDescription) {
4394
+ return {
4395
+ message: formatMinimalMessage(hostName, expectedDescription),
4396
+ };
4397
+ },
4398
+ };
4399
+ let activeDiagnostic = defaultDiagnostic;
4400
+ /**
4401
+ * Returns the currently active {@link HydrationDiagnostic} — either the
4402
+ * minimal default or one installed by an opt-in debugger.
4403
+ * @internal
4404
+ */
4405
+ function getHydrationDiagnostic() {
4406
+ return activeDiagnostic;
4407
+ }
4408
+ /**
4409
+ * Reads the host element's tag name from any node inside a hydration view.
4410
+ * Returns `undefined` when the node is not inside a shadow root.
4411
+ * @internal
4412
+ */
4413
+ function getHostName(node) {
4414
+ var _a;
4415
+ if (!node) {
4416
+ return undefined;
4417
+ }
4418
+ const root = node.getRootNode();
4419
+ return (_a = root.host) === null || _a === void 0 ? void 0 : _a.nodeName;
4420
+ }
4421
+
4212
4422
  class HydrationTargetElementError extends Error {
4213
4423
  constructor(
4214
4424
  /**
@@ -4222,10 +4432,24 @@ class HydrationTargetElementError extends Error {
4222
4432
  /**
4223
4433
  * The node to target factory.
4224
4434
  */
4225
- node) {
4435
+ node,
4436
+ /**
4437
+ * Structured description of the binding the hydration walk was
4438
+ * attempting to apply when the mismatch was detected. Free-form
4439
+ * string for structural errors that do not correspond to a single
4440
+ * binding factory.
4441
+ */
4442
+ expected,
4443
+ /**
4444
+ * Structured description of the server-rendered DOM that was
4445
+ * encountered at the mismatch point.
4446
+ */
4447
+ received) {
4226
4448
  super(message);
4227
4449
  this.factories = factories;
4228
4450
  this.node = node;
4451
+ this.expected = expected;
4452
+ this.received = received;
4229
4453
  }
4230
4454
  }
4231
4455
  function isComment(node) {
@@ -4274,7 +4498,6 @@ function createRangeForNodes(first, last) {
4274
4498
  * @returns - A {@link ViewBehaviorTargets } object for the factories in the view.
4275
4499
  */
4276
4500
  function buildViewBindingTargets(firstNode, lastNode, factories) {
4277
- var _a, _b;
4278
4501
  const range = createRangeForNodes(firstNode, lastNode);
4279
4502
  const treeRoot = range.commonAncestorContainer;
4280
4503
  const walker = document.createTreeWalker(treeRoot, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_COMMENT + NodeFilter.SHOW_TEXT, {
@@ -4287,36 +4510,63 @@ function buildViewBindingTargets(firstNode, lastNode, factories) {
4287
4510
  const targets = {};
4288
4511
  const boundaries = {};
4289
4512
  // Sequential factory pointer — skip host bindings at the start
4290
- let factoryPointer = getHydrationIndexOffset(factories);
4513
+ const hydrationIndexOffset = getHydrationIndexOffset(factories);
4514
+ let factoryPointer = hydrationIndexOffset;
4291
4515
  let node = (walker.currentNode = firstNode);
4292
4516
  while (node !== null) {
4293
4517
  switch (node.nodeType) {
4294
4518
  case Node.ELEMENT_NODE: {
4295
- const count = HydrationMarkup.parseAttributeBindingCount(node);
4519
+ const element = node;
4520
+ const legacyIndices = HydrationMarkup.parseLegacyAttributeBindingIndices(element);
4521
+ if (legacyIndices !== null) {
4522
+ for (const index of legacyIndices) {
4523
+ const factoryIndex = index + hydrationIndexOffset;
4524
+ const factory = factories[factoryIndex];
4525
+ if (!factory) {
4526
+ const expected = formatNoMoreAttributeBindings(factories.length);
4527
+ const result = getHydrationDiagnostic().formatStructuralError(node, getHostName(node), expected);
4528
+ throw new HydrationTargetElementError(result.message, factories, element, result.expected, result.received);
4529
+ }
4530
+ targetFactory(factory, node, targets);
4531
+ factoryPointer = Math.max(factoryPointer, factoryIndex + 1);
4532
+ }
4533
+ HydrationMarkup.removeLegacyAttributeBindingMarkers(element);
4534
+ break;
4535
+ }
4536
+ const count = HydrationMarkup.parseAttributeBindingCount(element);
4296
4537
  if (count !== null) {
4297
4538
  for (let i = 0; i < count; i++) {
4298
4539
  const factory = factories[factoryPointer++];
4299
4540
  if (!factory) {
4300
- 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);
4541
+ const expected = formatNoMoreAttributeBindings(factories.length);
4542
+ const result = getHydrationDiagnostic().formatStructuralError(node, getHostName(node), expected);
4543
+ throw new HydrationTargetElementError(result.message, factories, node, result.expected, result.received);
4301
4544
  }
4302
4545
  targetFactory(factory, node, targets);
4303
4546
  }
4304
- node.removeAttribute(HydrationMarkup.attributeMarkerName);
4547
+ element.removeAttribute(HydrationMarkup.attributeMarkerName);
4305
4548
  }
4306
4549
  break;
4307
4550
  }
4308
4551
  case Node.COMMENT_NODE: {
4309
4552
  const data = node.data;
4310
- if (data === "fe:e") {
4553
+ if (HydrationMarkup.isElementBoundaryStartMarker(node)) {
4311
4554
  // Element boundary — clear start marker and skip subtree
4312
4555
  node.data = "";
4313
4556
  skipToElementBoundaryEnd(walker, factories, node);
4314
4557
  }
4315
- else if (data === "fe:b") {
4558
+ else if (HydrationMarkup.isContentBindingStartMarker(data)) {
4316
4559
  // Content binding — consume next factory
4317
- const factory = factories[factoryPointer++];
4560
+ const legacyIndex = HydrationMarkup.parseLegacyContentBindingStartIndex(data);
4561
+ const factoryIndex = legacyIndex === null
4562
+ ? factoryPointer++
4563
+ : legacyIndex + hydrationIndexOffset;
4564
+ const factory = factories[factoryIndex];
4565
+ factoryPointer = Math.max(factoryPointer, factoryIndex + 1);
4318
4566
  if (!factory) {
4319
- 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);
4567
+ const expected = formatNoMoreContentBindings(factories.length);
4568
+ const result = getHydrationDiagnostic().formatStructuralError(node, getHostName(node), expected);
4569
+ throw new HydrationTargetElementError(result.message, factories, node, result.expected, result.received);
4320
4570
  }
4321
4571
  targetContentBinding(node, walker, factory, factories, targets, boundaries);
4322
4572
  }
@@ -4329,22 +4579,23 @@ function buildViewBindingTargets(firstNode, lastNode, factories) {
4329
4579
  return { targets, boundaries };
4330
4580
  }
4331
4581
  function targetContentBinding(node, walker, factory, factories, targets, boundaries) {
4332
- var _a, _b, _c, _d;
4333
4582
  const nodes = [];
4334
4583
  let current = walker.nextSibling();
4335
4584
  node.data = "";
4336
4585
  if (current === null) {
4337
- 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);
4586
+ const expected = expectedContentAfterStartMarker;
4587
+ const result = getHydrationDiagnostic().formatStructuralError(node, getHostName(node), expected);
4588
+ throw new HydrationTargetElementError(result.message, factories, node, result.expected, result.received);
4338
4589
  }
4339
4590
  const first = current;
4340
4591
  // Balanced depth counting for nested content markers
4341
4592
  let depth = 0;
4342
4593
  while (current !== null) {
4343
4594
  if (isComment(current)) {
4344
- if (current.data === "fe:b") {
4595
+ if (HydrationMarkup.isContentBindingStartMarker(current.data)) {
4345
4596
  depth++;
4346
4597
  }
4347
- else if (current.data === "fe:/b") {
4598
+ else if (HydrationMarkup.isContentBindingEndMarker(current.data)) {
4348
4599
  if (depth === 0)
4349
4600
  break;
4350
4601
  depth--;
@@ -4354,7 +4605,9 @@ function targetContentBinding(node, walker, factory, factories, targets, boundar
4354
4605
  current = walker.nextSibling();
4355
4606
  }
4356
4607
  if (current === null) {
4357
- 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);
4608
+ const expected = expectedContentEndMarker;
4609
+ const result = getHydrationDiagnostic().formatStructuralError(node, getHostName(node), expected);
4610
+ throw new HydrationTargetElementError(result.message, factories, node, result.expected, result.received);
4358
4611
  }
4359
4612
  current.data = "";
4360
4613
  if (nodes.length === 1 && isText(nodes[0])) {
@@ -4380,16 +4633,15 @@ function targetContentBinding(node, walker, factory, factories, targets, boundar
4380
4633
  * depth counting to handle nested element boundaries correctly.
4381
4634
  */
4382
4635
  function skipToElementBoundaryEnd(walker, factories, startNode) {
4383
- var _a, _b;
4384
4636
  let depth = 0;
4385
4637
  let current = walker.nextSibling();
4386
4638
  while (current !== null) {
4387
4639
  if (isComment(current)) {
4388
- if (current.data === "fe:e") {
4640
+ if (HydrationMarkup.isElementBoundaryStartMarker(current)) {
4389
4641
  current.data = "";
4390
4642
  depth++;
4391
4643
  }
4392
- else if (current.data === "fe:/e") {
4644
+ else if (HydrationMarkup.isElementBoundaryEndMarker(current)) {
4393
4645
  if (depth === 0) {
4394
4646
  current.data = "";
4395
4647
  return;
@@ -4400,7 +4652,9 @@ function skipToElementBoundaryEnd(walker, factories, startNode) {
4400
4652
  }
4401
4653
  current = walker.nextSibling();
4402
4654
  }
4403
- 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);
4655
+ const expected = expectedElementBoundaryEndMarker;
4656
+ const result = getHydrationDiagnostic().formatStructuralError(startNode, getHostName(startNode), expected);
4657
+ throw new HydrationTargetElementError(result.message, factories, startNode, result.expected, result.received);
4404
4658
  }
4405
4659
  /**
4406
4660
  * Counts how many factories at the start of the array are host bindings (targetNodeId='h').
@@ -4638,7 +4892,7 @@ class HTMLView extends DefaultExecutionContext {
4638
4892
  * @param context - The execution context to run the behaviors within.
4639
4893
  */
4640
4894
  bind(source, context = this) {
4641
- if (this.source === source) {
4895
+ if (this.source === source && this.context === context) {
4642
4896
  return;
4643
4897
  }
4644
4898
  let behaviors = this.behaviors;
@@ -4732,11 +4986,23 @@ class HydrationBindingError extends Error {
4732
4986
  * String representation of the HTML in the template that
4733
4987
  * threw the binding error.
4734
4988
  */
4735
- templateString) {
4989
+ templateString,
4990
+ /**
4991
+ * Structured description of the binding the hydration walk was
4992
+ * attempting to apply when the mismatch was detected.
4993
+ */
4994
+ expected,
4995
+ /**
4996
+ * Structured description of the server-rendered DOM that was
4997
+ * encountered at the mismatch point.
4998
+ */
4999
+ received) {
4736
5000
  super(message);
4737
5001
  this.factory = factory;
4738
5002
  this.fragment = fragment;
4739
5003
  this.templateString = templateString;
5004
+ this.expected = expected;
5005
+ this.received = received;
4740
5006
  }
4741
5007
  }
4742
5008
  class HydrationView extends DefaultExecutionContext {
@@ -4822,13 +5088,12 @@ class HydrationView extends DefaultExecutionContext {
4822
5088
  fragment.appendChild(end);
4823
5089
  }
4824
5090
  bind(source, context = this) {
4825
- var _b;
5091
+ if (this.source === source && this.context === context) {
5092
+ return;
5093
+ }
4826
5094
  if (this.hydrationStage !== HydrationStage.hydrated) {
4827
5095
  this._hydrationStage = HydrationStage.hydrating;
4828
5096
  }
4829
- if (this.source === source) {
4830
- return;
4831
- }
4832
5097
  let behaviors = this.behaviors;
4833
5098
  if (behaviors === null) {
4834
5099
  this.source = source;
@@ -4866,28 +5131,9 @@ class HydrationView extends DefaultExecutionContext {
4866
5131
  if (typeof templateString !== "string") {
4867
5132
  templateString = templateString.innerHTML;
4868
5133
  }
4869
- const hostElement = ((_b = this.firstChild) === null || _b === void 0 ? void 0 : _b.getRootNode())
4870
- .host;
4871
- const hostName = (hostElement === null || hostElement === void 0 ? void 0 : hostElement.nodeName) || "unknown";
4872
- const factoryInfo = factory;
4873
- // Build detailed error message
4874
- const details = [
4875
- `HydrationView was unable to successfully target bindings inside "<${hostName.toLowerCase()}>".`,
4876
- `\nMismatch Details:`,
4877
- ` - Expected target node ID: "${factory.targetNodeId}"`,
4878
- ` - Available target IDs: [${Object.keys(this.targets).join(", ") || "none"}]`,
4879
- ];
4880
- if (factory.targetTagName) {
4881
- details.push(` - Expected tag name: "${factory.targetTagName}"`);
4882
- }
4883
- if (factoryInfo.sourceAspect) {
4884
- details.push(` - Source aspect: "${factoryInfo.sourceAspect}"`);
4885
- }
4886
- if (factoryInfo.aspectType !== undefined) {
4887
- details.push(` - Aspect type: ${factoryInfo.aspectType}`);
4888
- }
4889
- 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 ? "..." : ""}`);
4890
- throw new HydrationBindingError(details.join("\n"), factory, createRangeForNodes(this.firstChild, this.lastChild).cloneContents(), templateString);
5134
+ const fragment = createRangeForNodes(this.firstChild, this.lastChild).cloneContents();
5135
+ const result = getHydrationDiagnostic().formatBindingMismatch(factory, this.firstChild, this.lastChild, getHostName(this.firstChild));
5136
+ throw new HydrationBindingError(result.message, factory, fragment, templateString, result.expected, result.received);
4891
5137
  }
4892
5138
  }
4893
5139
  }
@@ -4955,6 +5201,21 @@ function bindWithPositioning(view, items, index, controller) {
4955
5201
  function isCommentNode(node) {
4956
5202
  return node.nodeType === Node.COMMENT_NODE;
4957
5203
  }
5204
+ function removeNodeRange(first, last) {
5205
+ const parentNode = first.parentNode;
5206
+ if (parentNode === null) {
5207
+ return;
5208
+ }
5209
+ let current = first;
5210
+ while (current !== null) {
5211
+ const next = current.nextSibling;
5212
+ parentNode.removeChild(current);
5213
+ if (current === last) {
5214
+ break;
5215
+ }
5216
+ current = next;
5217
+ }
5218
+ }
4958
5219
  class HydrationRepeatError extends Error {
4959
5220
  constructor(
4960
5221
  /**
@@ -5214,23 +5475,26 @@ class RepeatBehavior {
5214
5475
  }
5215
5476
  }
5216
5477
  hydrateViews(template) {
5478
+ var _a;
5217
5479
  if (!this.items) {
5218
5480
  return;
5219
5481
  }
5220
- const itemCount = this.items.length;
5221
- this.views = new Array(itemCount);
5482
+ const items = this.items;
5483
+ const itemCount = items.length;
5484
+ const views = (this.views = new Array(itemCount));
5222
5485
  // First pass: collect all repeat marker pairs by walking backward.
5223
- // Each entry is { start: Node, end: Node } for the item content range.
5486
+ // Each entry tracks both the item content range and its SSR markers.
5224
5487
  const itemRanges = [];
5225
5488
  let current = this.location.previousSibling;
5226
5489
  while (current !== null) {
5227
- if (!isCommentNode(current) || current.data !== "fe:/r") {
5490
+ if (!isCommentNode(current) ||
5491
+ !HydrationMarkup.isRepeatViewEndMarker(current.data)) {
5228
5492
  current = current.previousSibling;
5229
5493
  continue;
5230
5494
  }
5231
- // Found repeat end marker
5232
- current.data = "";
5233
- const end = current.previousSibling;
5495
+ const endMarker = current;
5496
+ endMarker.data = "";
5497
+ const end = endMarker.previousSibling;
5234
5498
  if (!end) {
5235
5499
  throw new Error(`Error when hydrating inside "${this.location.getRootNode().host.nodeName}": end should never be null.`);
5236
5500
  }
@@ -5239,18 +5503,23 @@ class RepeatBehavior {
5239
5503
  let depth = 0;
5240
5504
  while (start !== null) {
5241
5505
  if (isCommentNode(start)) {
5242
- if (start.data === "fe:/r") {
5506
+ if (HydrationMarkup.isRepeatViewEndMarker(start.data)) {
5243
5507
  depth++;
5244
5508
  }
5245
- else if (start.data === "fe:r") {
5509
+ else if (HydrationMarkup.isRepeatViewStartMarker(start.data)) {
5246
5510
  if (depth === 0) {
5247
5511
  const startMarker = start;
5248
5512
  startMarker.data = "";
5249
5513
  current = startMarker.previousSibling;
5250
- const itemStart = startMarker.nextSibling;
5514
+ const itemStart = (_a = startMarker.nextSibling) !== null && _a !== void 0 ? _a : endMarker;
5251
5515
  // Empty item: start and end markers are adjacent.
5252
5516
  const itemEnd = end === startMarker ? itemStart : end;
5253
- itemRanges.push({ start: itemStart, end: itemEnd });
5517
+ itemRanges.push({
5518
+ start: itemStart,
5519
+ end: itemEnd,
5520
+ startMarker,
5521
+ endMarker,
5522
+ });
5254
5523
  break;
5255
5524
  }
5256
5525
  depth--;
@@ -5266,11 +5535,22 @@ class RepeatBehavior {
5266
5535
  // Reverse so index 0 = first SSR item.
5267
5536
  itemRanges.reverse();
5268
5537
  // Hydrate each SSR item at its correct index (0-based from start).
5269
- for (let i = 0; i < itemRanges.length && i < itemCount; i++) {
5538
+ const hydrationCount = Math.min(itemRanges.length, itemCount);
5539
+ for (let i = 0; i < hydrationCount; i++) {
5270
5540
  const { start, end } = itemRanges[i];
5271
5541
  const view = template.hydrate(start, end);
5272
- this.views[i] = view;
5273
- this.bindView(view, this.items, i, this.controller);
5542
+ views[i] = view;
5543
+ this.bindView(view, items, i, this.controller);
5544
+ }
5545
+ for (let i = hydrationCount; i < itemCount; i++) {
5546
+ const view = template.create();
5547
+ views[i] = view;
5548
+ this.bindView(view, items, i, this.controller);
5549
+ view.insertBefore(this.location);
5550
+ }
5551
+ for (let i = itemCount, ii = itemRanges.length; i < ii; i++) {
5552
+ const { startMarker, endMarker } = itemRanges[i];
5553
+ removeNodeRange(startMarker, endMarker);
5274
5554
  }
5275
5555
  }
5276
5556
  }
@@ -6127,12 +6407,12 @@ class HTMLBindingDirective {
6127
6407
  * @param dataBinding - The binding configuration to apply.
6128
6408
  */
6129
6409
  constructor(dataBinding) {
6130
- this.dataBinding = dataBinding;
6131
6410
  this.updateTarget = null;
6132
6411
  /**
6133
6412
  * The type of aspect to target.
6134
6413
  */
6135
6414
  this.aspectType = DOMAspect.content;
6415
+ this.dataBinding = dataBinding;
6136
6416
  }
6137
6417
  /**
6138
6418
  * Creates HTML to be used within a template.
@@ -6780,11 +7060,10 @@ class RenderBehavior {
6780
7060
  if (viewNodes) {
6781
7061
  this.view = this.template.hydrate(viewNodes.first, viewNodes.last);
6782
7062
  this.bindView(this.view);
7063
+ return;
6783
7064
  }
6784
7065
  }
6785
- else {
6786
- this.refreshView();
6787
- }
7066
+ this.refreshView();
6788
7067
  }
6789
7068
  /**
6790
7069
  * Unbinds this behavior.
@@ -6901,6 +7180,32 @@ function instructionToTemplate(def) {
6901
7180
  }
6902
7181
  return def.template;
6903
7182
  }
7183
+ function resolveTemplateBindingValue(result, dataBinding, source, context) {
7184
+ var _a;
7185
+ if (isString(result)) {
7186
+ return instructionToTemplate(getForInstance(dataBinding.evaluate(source, context), result));
7187
+ }
7188
+ if (result instanceof Node) {
7189
+ return (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
7190
+ }
7191
+ return result;
7192
+ }
7193
+ function adaptTemplateBinding(binding, dataBinding) {
7194
+ const evaluateTemplate = binding.evaluate;
7195
+ const adapter = Object.create(Object.getPrototypeOf(binding));
7196
+ for (const propertyName of Object.getOwnPropertyNames(binding)) {
7197
+ if (propertyName !== "evaluate") {
7198
+ Object.defineProperty(adapter, propertyName, Object.getOwnPropertyDescriptor(binding, propertyName));
7199
+ }
7200
+ }
7201
+ Object.defineProperty(adapter, "evaluate", {
7202
+ configurable: true,
7203
+ enumerable: true,
7204
+ value: (source, context) => resolveTemplateBindingValue(evaluateTemplate(source, context), dataBinding, source, context),
7205
+ writable: true,
7206
+ });
7207
+ return adapter;
7208
+ }
6904
7209
  function getByType(type, name) {
6905
7210
  const entries = typeToInstructionLookup.get(type);
6906
7211
  if (entries === void 0) {
@@ -7003,19 +7308,7 @@ function render(value, template) {
7003
7308
  });
7004
7309
  }
7005
7310
  else if (template instanceof Binding) {
7006
- const evaluateTemplate = template.evaluate;
7007
- template.evaluate = (s, c) => {
7008
- var _a;
7009
- let result = evaluateTemplate(s, c);
7010
- if (isString(result)) {
7011
- result = instructionToTemplate(getForInstance(dataBinding.evaluate(s, c), result));
7012
- }
7013
- else if (result instanceof Node) {
7014
- result = (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
7015
- }
7016
- return result;
7017
- };
7018
- templateBinding = template;
7311
+ templateBinding = adaptTemplateBinding(template, dataBinding);
7019
7312
  }
7020
7313
  else {
7021
7314
  templateBinding = oneTime((s, c) => template);