@microsoft/fast-element 2.10.4 → 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 (211) hide show
  1. package/CHANGELOG.md +52 -2
  2. package/README.md +244 -1
  3. package/dist/arrays/arrays.api.json +2621 -0
  4. package/dist/context/context.api.json +13 -13
  5. package/dist/declarative/declarative.api.json +8483 -0
  6. package/dist/di/di.api.json +16 -16
  7. package/dist/dts/__test__/helpers.d.ts +6 -0
  8. package/dist/dts/array-observer.d.ts +2 -0
  9. package/dist/dts/arrays.d.ts +2 -0
  10. package/dist/dts/attr.d.ts +1 -0
  11. package/dist/dts/binding/binding.d.ts +15 -5
  12. package/dist/dts/binding/one-time.d.ts +1 -1
  13. package/dist/dts/binding/one-way.d.ts +1 -1
  14. package/dist/dts/binding/signal.d.ts +6 -6
  15. package/dist/dts/binding/two-way.d.ts +2 -1
  16. package/dist/dts/binding.d.ts +7 -0
  17. package/dist/dts/components/attributes.d.ts +1 -4
  18. package/dist/dts/components/definition-schema-transforms.d.ts +9 -0
  19. package/dist/dts/components/element-controller.d.ts +80 -114
  20. package/dist/dts/components/element-hydration.d.ts +1 -1
  21. package/dist/dts/components/enable-hydration.d.ts +54 -0
  22. package/dist/dts/components/fast-definitions.d.ts +98 -46
  23. package/dist/dts/components/fast-element.d.ts +43 -16
  24. package/dist/dts/components/hydration-tracker.d.ts +83 -0
  25. package/dist/dts/components/hydration.d.ts +23 -53
  26. package/dist/dts/components/schema.d.ts +205 -0
  27. package/dist/dts/context.d.ts +13 -13
  28. package/dist/dts/css.d.ts +3 -0
  29. package/dist/dts/debug.d.ts +5 -1
  30. package/dist/dts/declarative/attribute-map.d.ts +58 -0
  31. package/dist/dts/declarative/debug.d.ts +4 -0
  32. package/dist/dts/declarative/index.d.ts +14 -0
  33. package/dist/dts/declarative/interfaces.d.ts +8 -0
  34. package/dist/dts/declarative/observer-map-utilities.d.ts +58 -0
  35. package/dist/dts/declarative/observer-map.d.ts +89 -0
  36. package/dist/dts/declarative/runtime.d.ts +5 -0
  37. package/dist/dts/declarative/syntax.d.ts +21 -0
  38. package/dist/dts/declarative/template-bridge.d.ts +33 -0
  39. package/dist/dts/declarative/template-parser.d.ts +98 -0
  40. package/dist/dts/declarative/template.d.ts +10 -0
  41. package/dist/dts/declarative/utilities.d.ts +358 -0
  42. package/dist/dts/di/di.d.ts +7 -7
  43. package/dist/dts/directives/children.d.ts +2 -0
  44. package/dist/dts/directives/node-observation.d.ts +2 -0
  45. package/dist/dts/directives/ref.d.ts +2 -0
  46. package/dist/dts/directives/repeat.d.ts +4 -0
  47. package/dist/dts/directives/slotted.d.ts +2 -0
  48. package/dist/dts/directives/when.d.ts +3 -0
  49. package/dist/dts/dom-policy.d.ts +23 -5
  50. package/dist/dts/dom.d.ts +4 -16
  51. package/dist/dts/html.d.ts +5 -0
  52. package/dist/dts/hydration/diagnostics.d.ts +93 -0
  53. package/dist/dts/hydration/hydration-debugger.d.ts +35 -0
  54. package/dist/dts/hydration/messages.d.ts +62 -0
  55. package/dist/dts/hydration/runtime.d.ts +7 -0
  56. package/dist/dts/hydration/target-builder.d.ts +40 -12
  57. package/dist/dts/hydration.d.ts +18 -0
  58. package/dist/dts/index.d.ts +42 -42
  59. package/dist/dts/index.debug.d.ts +0 -1
  60. package/dist/dts/index.rollup.debug.d.ts +0 -1
  61. package/dist/dts/interfaces.d.ts +2 -49
  62. package/dist/dts/observable.d.ts +3 -6
  63. package/dist/dts/observation/arrays.d.ts +1 -1
  64. package/dist/dts/observation/observable.d.ts +3 -3
  65. package/dist/dts/observation/update-queue.d.ts +1 -1
  66. package/dist/dts/platform.d.ts +45 -8
  67. package/dist/dts/registry.d.ts +1 -0
  68. package/dist/dts/render.d.ts +7 -0
  69. package/dist/dts/schema.d.ts +1 -0
  70. package/dist/dts/state/exports.d.ts +1 -1
  71. package/dist/dts/state/state.d.ts +2 -2
  72. package/dist/dts/styles/css-directive.d.ts +5 -12
  73. package/dist/dts/styles/css.d.ts +5 -7
  74. package/dist/dts/styles/element-styles.d.ts +0 -10
  75. package/dist/dts/styles.d.ts +6 -0
  76. package/dist/dts/templating/compiler.d.ts +1 -1
  77. package/dist/dts/templating/html-binding-directive.d.ts +10 -2
  78. package/dist/dts/templating/html-directive.d.ts +19 -1
  79. package/dist/dts/templating/hydration-view.d.ts +130 -0
  80. package/dist/dts/templating/render.d.ts +1 -1
  81. package/dist/dts/templating/repeat.d.ts +1 -1
  82. package/dist/dts/templating/template.d.ts +15 -7
  83. package/dist/dts/templating/view.d.ts +25 -102
  84. package/dist/dts/templating.d.ts +10 -0
  85. package/dist/dts/testing/exports.d.ts +2 -2
  86. package/dist/dts/testing/fakes.d.ts +4 -4
  87. package/dist/dts/updates.d.ts +1 -0
  88. package/dist/dts/volatile.d.ts +2 -0
  89. package/dist/esm/__test__/helpers.js +22 -0
  90. package/dist/esm/__test__/setup-node.js +18 -0
  91. package/dist/esm/array-observer.js +1 -0
  92. package/dist/esm/arrays.js +1 -0
  93. package/dist/esm/attr.js +1 -0
  94. package/dist/esm/binding/normalize.js +1 -1
  95. package/dist/esm/binding/signal.js +4 -4
  96. package/dist/esm/binding/two-way.js +3 -3
  97. package/dist/esm/binding.js +4 -0
  98. package/dist/esm/components/attributes.js +18 -11
  99. package/dist/esm/components/definition-schema-transforms.js +23 -0
  100. package/dist/esm/components/element-controller.js +206 -270
  101. package/dist/esm/components/element-hydration.js +1 -1
  102. package/dist/esm/components/enable-hydration.js +124 -0
  103. package/dist/esm/components/fast-definitions.js +219 -56
  104. package/dist/esm/components/fast-element.js +18 -27
  105. package/dist/esm/components/hydration-tracker.js +122 -0
  106. package/dist/esm/components/hydration.js +137 -140
  107. package/dist/esm/components/schema.js +253 -0
  108. package/dist/esm/context.js +6 -6
  109. package/dist/esm/css.js +3 -0
  110. package/dist/esm/debug.js +27 -26
  111. package/dist/esm/declarative/attribute-map.js +122 -0
  112. package/dist/esm/declarative/debug.js +4 -0
  113. package/dist/esm/declarative/index.js +4 -0
  114. package/dist/esm/declarative/interfaces.js +9 -0
  115. package/dist/esm/declarative/observer-map-utilities.js +565 -0
  116. package/dist/esm/declarative/observer-map.js +216 -0
  117. package/dist/esm/declarative/runtime.js +14 -0
  118. package/dist/esm/declarative/syntax.js +36 -0
  119. package/dist/esm/declarative/template-bridge.js +160 -0
  120. package/dist/esm/declarative/template-parser.js +306 -0
  121. package/dist/esm/declarative/template.js +143 -0
  122. package/dist/esm/declarative/utilities.js +1069 -0
  123. package/dist/esm/di/di.js +8 -9
  124. package/dist/esm/directives/children.js +1 -0
  125. package/dist/esm/directives/node-observation.js +1 -0
  126. package/dist/esm/directives/ref.js +1 -0
  127. package/dist/esm/directives/repeat.js +1 -0
  128. package/dist/esm/directives/slotted.js +1 -0
  129. package/dist/esm/directives/when.js +1 -0
  130. package/dist/esm/dom-policy.js +35 -6
  131. package/dist/esm/dom.js +1 -1
  132. package/dist/esm/html.js +2 -0
  133. package/dist/esm/hydration/diagnostics.js +50 -0
  134. package/dist/esm/hydration/hydration-debugger.js +112 -0
  135. package/dist/esm/hydration/messages.js +84 -0
  136. package/dist/esm/hydration/runtime.js +33 -0
  137. package/dist/esm/hydration/target-builder.js +144 -91
  138. package/dist/esm/hydration.js +6 -0
  139. package/dist/esm/index.debug.js +2 -1
  140. package/dist/esm/index.js +38 -29
  141. package/dist/esm/index.rollup.debug.js +3 -2
  142. package/dist/esm/index.rollup.js +1 -1
  143. package/dist/esm/interfaces.js +2 -45
  144. package/dist/esm/metadata.js +2 -8
  145. package/dist/esm/observable.js +1 -4
  146. package/dist/esm/observation/arrays.js +1 -1
  147. package/dist/esm/observation/notifier.js +2 -4
  148. package/dist/esm/observation/observable.js +5 -5
  149. package/dist/esm/observation/update-queue.js +47 -58
  150. package/dist/esm/platform.js +31 -30
  151. package/dist/esm/registry.js +1 -0
  152. package/dist/esm/render.js +1 -0
  153. package/dist/esm/schema.js +1 -0
  154. package/dist/esm/state/exports.js +1 -1
  155. package/dist/esm/styles/css-directive.js +1 -2
  156. package/dist/esm/styles/css.js +15 -56
  157. package/dist/esm/styles/element-styles.js +69 -15
  158. package/dist/esm/styles.js +2 -0
  159. package/dist/esm/templating/html-binding-directive.js +11 -9
  160. package/dist/esm/templating/hydration-view.js +228 -0
  161. package/dist/esm/templating/render.js +39 -18
  162. package/dist/esm/templating/repeat.js +69 -33
  163. package/dist/esm/templating/template.js +7 -7
  164. package/dist/esm/templating/view.js +25 -234
  165. package/dist/esm/templating.js +7 -0
  166. package/dist/esm/testing/exports.js +2 -2
  167. package/dist/esm/testing/fixture.js +2 -2
  168. package/dist/esm/testing/timeout.js +2 -2
  169. package/dist/esm/updates.js +1 -0
  170. package/dist/esm/volatile.js +1 -0
  171. package/dist/fast-element.api.json +14389 -11138
  172. package/dist/fast-element.d.ts +3651 -809
  173. package/dist/fast-element.debug.js +5666 -4722
  174. package/dist/fast-element.debug.min.js +2 -2
  175. package/dist/fast-element.js +5394 -4381
  176. package/dist/fast-element.min.js +2 -2
  177. package/dist/fast-element.untrimmed.d.ts +923 -472
  178. package/dist/hydration/hydration.api.json +6460 -0
  179. package/dist/styles/styles.api.json +2672 -0
  180. package/package.json +165 -45
  181. package/ARCHITECTURE_FASTELEMENT.md +0 -63
  182. package/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md +0 -36
  183. package/ARCHITECTURE_INTRO.md +0 -10
  184. package/ARCHITECTURE_OVERVIEW.md +0 -52
  185. package/ARCHITECTURE_UPDATES.md +0 -11
  186. package/CHANGELOG.json +0 -2275
  187. package/DESIGN.md +0 -510
  188. package/api-extractor.context.json +0 -14
  189. package/api-extractor.di.json +0 -14
  190. package/biome.json +0 -4
  191. package/dist/dts/components/install-hydration.d.ts +0 -1
  192. package/dist/dts/pending-task.d.ts +0 -32
  193. package/dist/dts/styles/css-binding-directive.d.ts +0 -60
  194. package/dist/dts/templating/install-hydratable-view-templates.d.ts +0 -1
  195. package/dist/esm/components/install-hydration.js +0 -3
  196. package/dist/esm/pending-task.js +0 -28
  197. package/dist/esm/polyfills.js +0 -60
  198. package/dist/esm/styles/css-binding-directive.js +0 -76
  199. package/dist/esm/templating/install-hydratable-view-templates.js +0 -23
  200. package/docs/ACKNOWLEDGEMENTS.md +0 -12
  201. package/docs/api-report.api.md +0 -1122
  202. package/docs/context/api-report.api.md +0 -69
  203. package/docs/di/api-report.api.md +0 -315
  204. package/docs/fast-element-2-changes.md +0 -15
  205. package/playwright.config.ts +0 -26
  206. package/scripts/run-api-extractor.js +0 -51
  207. package/test/index.html +0 -11
  208. package/test/main.ts +0 -104
  209. package/test/vite.config.ts +0 -19
  210. package/tsconfig.api-extractor.json +0 -6
  211. /package/dist/dts/{polyfills.d.ts → __test__/setup-node.d.ts} +0 -0
package/dist/esm/di/di.js CHANGED
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  * Big thanks to https://github.com/fkleuver and the https://github.com/aurelia/aurelia project
12
12
  * for the bulk of this code and many of the associated tests.
13
13
  */
14
- import { Context, ContextEvent, } from "../context.js";
14
+ import { Context, } from "../context.js";
15
15
  import { Message } from "../interfaces.js";
16
16
  import { Metadata } from "../metadata.js";
17
17
  import { emptyArray, FAST } from "../platform.js";
@@ -181,7 +181,7 @@ function createContext(nameConfigOrCallback, configuror) {
181
181
  return configure(new ResolverBuilder(container, key !== null && key !== void 0 ? key : Interface));
182
182
  };
183
183
  }
184
- Interface.toString = function toString() {
184
+ Interface.toString = function interfaceToString() {
185
185
  return `DIContext<${Interface.name}>`;
186
186
  };
187
187
  return Interface;
@@ -800,7 +800,8 @@ export class ResolverImpl {
800
800
  }
801
801
  }
802
802
  getFactory(container) {
803
- var _a, _b, _c;
803
+ var _a, _b;
804
+ var _c;
804
805
  switch (this.strategy) {
805
806
  case ResolverStrategy.singleton:
806
807
  case ResolverStrategy.transient:
@@ -1132,9 +1133,7 @@ export class ContainerImpl {
1132
1133
  }
1133
1134
  getAll(key, searchAncestors = false) {
1134
1135
  validateKey(key);
1135
- /* eslint-disable-next-line @typescript-eslint/no-this-alias */
1136
- const requestor = this;
1137
- let current = requestor;
1136
+ let current = this;
1138
1137
  let resolver;
1139
1138
  if (searchAncestors) {
1140
1139
  let resolutions = emptyArray;
@@ -1143,7 +1142,7 @@ export class ContainerImpl {
1143
1142
  if (resolver != null) {
1144
1143
  resolutions = resolutions.concat(
1145
1144
  /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
1146
- buildAllResponse(resolver, current, requestor));
1145
+ buildAllResponse(resolver, current, this));
1147
1146
  }
1148
1147
  current = current.parent;
1149
1148
  }
@@ -1159,7 +1158,7 @@ export class ContainerImpl {
1159
1158
  }
1160
1159
  }
1161
1160
  else {
1162
- return buildAllResponse(resolver, current, requestor);
1161
+ return buildAllResponse(resolver, current, this);
1163
1162
  }
1164
1163
  }
1165
1164
  }
@@ -1199,7 +1198,7 @@ export class ContainerImpl {
1199
1198
  if (!(registrationResolver instanceof Object) ||
1200
1199
  registrationResolver.resolve == null) {
1201
1200
  const newResolver = handler.resolvers.get(keyAsValue);
1202
- if (newResolver != void 0) {
1201
+ if (newResolver !== void 0) {
1203
1202
  return newResolver;
1204
1203
  }
1205
1204
  throw FAST.error(Message.invalidResolver);
@@ -0,0 +1 @@
1
+ export { ChildrenDirective, children, } from "../templating/children.js";
@@ -0,0 +1 @@
1
+ export { elements, NodeObservationDirective, } from "../templating/node-observation.js";
@@ -0,0 +1 @@
1
+ export { RefDirective, ref } from "../templating/ref.js";
@@ -0,0 +1 @@
1
+ export { RepeatBehavior, RepeatDirective, repeat, } from "../templating/repeat.js";
@@ -0,0 +1 @@
1
+ export { SlottedDirective, slotted, } from "../templating/slotted.js";
@@ -0,0 +1 @@
1
+ export { when } from "../templating/when.js";
@@ -1,10 +1,40 @@
1
1
  import { DOMAspect } from "./dom.js";
2
2
  import { isString, Message } from "./interfaces.js";
3
3
  import { FAST } from "./platform.js";
4
+ const surroundingWhitespaceAndControlChars = /^[\u0000-\u0020\u007F]+|[\u0000-\u0020\u007F]+$/g;
5
+ const whitespaceAndControlChars = /[\u0000-\u0020\u007F]+/g;
6
+ const unsafeURLProtocol = /^(?:javascript|vbscript|data):/;
7
+ function trimURL(value) {
8
+ return value.replace(surroundingWhitespaceAndControlChars, "");
9
+ }
10
+ function decodeURL(value) {
11
+ try {
12
+ return decodeURIComponent(value);
13
+ }
14
+ catch (_a) {
15
+ return value;
16
+ }
17
+ }
18
+ function hasUnsafeURLProtocol(value) {
19
+ let normalized = trimURL(value);
20
+ for (let i = 0; i < 3; ++i) {
21
+ const decoded = decodeURL(normalized);
22
+ if (decoded === normalized) {
23
+ break;
24
+ }
25
+ normalized = trimURL(decoded);
26
+ }
27
+ normalized = normalized.replace(whitespaceAndControlChars, "").toLowerCase();
28
+ return unsafeURLProtocol.test(normalized);
29
+ }
30
+ function sanitizeURL(value) {
31
+ const trimmed = trimURL(value);
32
+ return hasUnsafeURLProtocol(trimmed) ? "" : trimmed;
33
+ }
4
34
  function safeURL(tagName, aspect, aspectName, sink) {
5
35
  return (target, name, value, ...rest) => {
6
36
  if (isString(value)) {
7
- value = value.replace(/(javascript:|vbscript:|data:)/, "");
37
+ value = sanitizeURL(value);
8
38
  }
9
39
  sink(target, name, value, ...rest);
10
40
  };
@@ -266,7 +296,7 @@ function createElementGuards(config, defaults) {
266
296
  break;
267
297
  case undefined:
268
298
  // keep the default
269
- result[tag] = createDOMAspectGuards(overrideValue, {});
299
+ result[tag] = createDOMAspectGuards(defaultValue, {});
270
300
  break;
271
301
  default:
272
302
  // override the default aspects
@@ -295,7 +325,7 @@ function createDOMGuards(config, defaults) {
295
325
  function createTrustedType() {
296
326
  const createHTML = html => html;
297
327
  return globalThis.trustedTypes
298
- ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML })
328
+ ? globalThis.trustedTypes.createPolicy("fast-element", { createHTML })
299
329
  : { createHTML };
300
330
  }
301
331
  function tryGuard(aspectGuards, tagName, aspect, aspectName, sink) {
@@ -311,10 +341,10 @@ function tryGuard(aspectGuards, tagName, aspect, aspectName, sink) {
311
341
  * A helper for creating DOM policies.
312
342
  * @public
313
343
  */
314
- const DOMPolicy = Object.freeze({
344
+ export const DOMPolicy = Object.freeze({
315
345
  /**
316
346
  * Creates a new DOM Policy object.
317
- * @param options The options to use in creating the policy.
347
+ * @param options - The options to use in creating the policy.
318
348
  * @returns The newly created DOMPolicy.
319
349
  */
320
350
  create(options = {}) {
@@ -342,4 +372,3 @@ const DOMPolicy = Object.freeze({
342
372
  });
343
373
  },
344
374
  });
345
- export { DOMPolicy };
package/dist/esm/dom.js CHANGED
@@ -36,7 +36,7 @@ export const DOMAspect = Object.freeze({
36
36
  });
37
37
  const createHTML = html => html;
38
38
  const fastTrustedType = globalThis.trustedTypes
39
- ? globalThis.trustedTypes.createPolicy("fast-html", { createHTML })
39
+ ? globalThis.trustedTypes.createPolicy("fast-element", { createHTML })
40
40
  : { createHTML };
41
41
  let defaultPolicy = Object.freeze({
42
42
  createHTML(value) {
@@ -0,0 +1,2 @@
1
+ export { html, InlineTemplateDirective, ViewTemplate, } from "./templating/template.js";
2
+ export { HTMLView } from "./templating/view.js";
@@ -0,0 +1,50 @@
1
+ import { formatDefaultMismatchMessage, unknownHostName } from "./messages.js";
2
+ function formatMinimalMessage(hostName, detail) {
3
+ const host = (hostName !== null && hostName !== void 0 ? hostName : unknownHostName).toLowerCase();
4
+ return formatDefaultMismatchMessage(host, detail);
5
+ }
6
+ const defaultDiagnostic = {
7
+ formatBindingMismatch(_factory, _firstChild, _lastChild, hostName) {
8
+ return {
9
+ message: formatMinimalMessage(hostName, undefined),
10
+ };
11
+ },
12
+ formatStructuralError(_node, hostName, expectedDescription) {
13
+ return {
14
+ message: formatMinimalMessage(hostName, expectedDescription),
15
+ };
16
+ },
17
+ };
18
+ let activeDiagnostic = defaultDiagnostic;
19
+ /**
20
+ * Installs a {@link HydrationDiagnostic} as the active formatter for
21
+ * hydration mismatch errors. Called by `enableHydration()` when an opt-in
22
+ * debugger configuration is supplied; not exposed as `@public` because
23
+ * library consumers should always go through `enableHydration` to install
24
+ * a debugger.
25
+ * @internal
26
+ */
27
+ export function installHydrationDiagnostic(diagnostic) {
28
+ activeDiagnostic = diagnostic;
29
+ }
30
+ /**
31
+ * Returns the currently active {@link HydrationDiagnostic} — either the
32
+ * minimal default or one installed by an opt-in debugger.
33
+ * @internal
34
+ */
35
+ export function getHydrationDiagnostic() {
36
+ return activeDiagnostic;
37
+ }
38
+ /**
39
+ * Reads the host element's tag name from any node inside a hydration view.
40
+ * Returns `undefined` when the node is not inside a shadow root.
41
+ * @internal
42
+ */
43
+ export function getHostName(node) {
44
+ var _a;
45
+ if (!node) {
46
+ return undefined;
47
+ }
48
+ const root = node.getRootNode();
49
+ return (_a = root.host) === null || _a === void 0 ? void 0 : _a.nodeName;
50
+ }
@@ -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
+ }
@@ -0,0 +1,33 @@
1
+ import { Hydratable } from "../components/hydration.js";
2
+ import { ViewTemplate } from "../templating/template.js";
3
+ import { HydrationView } from "../templating/hydration-view.js";
4
+ let installed = false;
5
+ /**
6
+ * Installs the hydration runtime on `ViewTemplate.prototype`,
7
+ * making all templates hydratable. Call this before any hydration
8
+ * occurs. Safe to call multiple times — subsequent calls are no-ops.
9
+ * @internal
10
+ */
11
+ export function ensureHydrationRuntime() {
12
+ if (installed) {
13
+ return;
14
+ }
15
+ const prototype = ViewTemplate.prototype;
16
+ if (prototype[Hydratable] !== Hydratable) {
17
+ Object.defineProperties(prototype, {
18
+ [Hydratable]: {
19
+ value: Hydratable,
20
+ enumerable: false,
21
+ configurable: false,
22
+ },
23
+ hydrate: {
24
+ value(firstChild, lastChild, hostBindingTarget) {
25
+ return new HydrationView(firstChild, lastChild, this, hostBindingTarget);
26
+ },
27
+ enumerable: true,
28
+ configurable: false,
29
+ },
30
+ });
31
+ }
32
+ installed = true;
33
+ }