@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
@@ -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) {
@@ -39,26 +55,24 @@ export function createRangeForNodes(first, last) {
39
55
  range.setEnd(last, isComment(last) || isText(last) ? last.data.length : last.childNodes.length);
40
56
  return range;
41
57
  }
42
- function isShadowRoot(node) {
43
- return node instanceof DocumentFragment && "mode" in node;
44
- }
45
58
  /**
46
59
  * Maps compiled ViewBehaviorFactory IDs to their corresponding DOM nodes in the
47
60
  * server-rendered shadow root. Uses a TreeWalker to scan the existing DOM between
48
- * firstNode and lastNode, parsing hydration markers to build the targets map.
61
+ * firstNode and lastNode, processing data-free sequential hydration markers.
49
62
  *
50
- * For element nodes: parses `data-fe-b` (or variant) attributes to identify which
51
- * factories target each element, then removes the marker attribute.
63
+ * A sequential factory pointer advances through the factories array in DFS order.
64
+ * Since the template compiler and hydration walker both traverse the DOM in
65
+ * identical depth-first order, no embedded indices are needed in markers.
52
66
  *
53
- * For comment nodes: parses content binding markers (`fe-b$$start/end$$`) to find
54
- * the DOM range controlled by each content binding. Single text nodes become the
55
- * direct target; multi-node ranges are stored in boundaries for structural directives.
56
- * Element boundary markers (`fe-eb$$start/end$$`) cause the walker to skip over
57
- * nested custom elements that handle their own hydration.
67
+ * For element nodes: parses `data-fe="N"` to determine the count of attribute
68
+ * binding factories, then consumes N factories sequentially.
69
+ *
70
+ * For comment nodes: `fe:b` markers consume the next factory for content bindings,
71
+ * using balanced depth counting for nested marker pairing. `fe:e` markers cause
72
+ * the walker to skip nested custom element subtrees.
58
73
  *
59
74
  * Host bindings (targetNodeId='h') appear at the start of the factories array but
60
- * have no SSR markers — getHydrationIndexOffset() computes how many to skip so that
61
- * marker indices align with the correct non-host factories.
75
+ * have no SSR markers — getHydrationIndexOffset() computes the initial pointer value.
62
76
  *
63
77
  * @param firstNode - The first node of the view.
64
78
  * @param lastNode - The last node of the view.
@@ -68,7 +82,6 @@ function isShadowRoot(node) {
68
82
  export function buildViewBindingTargets(firstNode, lastNode, factories) {
69
83
  const range = createRangeForNodes(firstNode, lastNode);
70
84
  const treeRoot = range.commonAncestorContainer;
71
- const hydrationIndexOffset = getHydrationIndexOffset(factories);
72
85
  const walker = document.createTreeWalker(treeRoot, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_COMMENT + NodeFilter.SHOW_TEXT, {
73
86
  acceptNode(node) {
74
87
  return range.comparePoint(node, 0) === 0
@@ -78,15 +91,67 @@ export function buildViewBindingTargets(firstNode, lastNode, factories) {
78
91
  });
79
92
  const targets = {};
80
93
  const boundaries = {};
94
+ // Sequential factory pointer — skip host bindings at the start
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
- targetElement(node, factories, targets, hydrationIndexOffset);
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);
119
+ if (count !== null) {
120
+ for (let i = 0; i < count; i++) {
121
+ const factory = factories[factoryPointer++];
122
+ if (!factory) {
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);
126
+ }
127
+ targetFactory(factory, node, targets);
128
+ }
129
+ element.removeAttribute(HydrationMarkup.attributeMarkerName);
130
+ }
86
131
  break;
87
132
  }
88
133
  case Node.COMMENT_NODE: {
89
- targetComment(node, walker, factories, targets, boundaries, hydrationIndexOffset);
134
+ const data = node.data;
135
+ if (HydrationMarkup.isElementBoundaryStartMarker(node)) {
136
+ // Element boundary — clear start marker and skip subtree
137
+ node.data = "";
138
+ skipToElementBoundaryEnd(walker, factories, node);
139
+ }
140
+ else if (HydrationMarkup.isContentBindingStartMarker(data)) {
141
+ // Content binding — consume next factory
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);
148
+ if (!factory) {
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);
152
+ }
153
+ targetContentBinding(node, walker, factory, factories, targets, boundaries);
154
+ }
90
155
  break;
91
156
  }
92
157
  }
@@ -95,100 +160,88 @@ export function buildViewBindingTargets(firstNode, lastNode, factories) {
95
160
  range.detach();
96
161
  return { targets, boundaries };
97
162
  }
98
- function targetElement(node, factories, targets, hydrationIndexOffset) {
99
- var _a, _b;
100
- // Check for attributes and map any factories.
101
- const attrFactoryIds = (_b = (_a = HydrationMarkup.parseAttributeBinding(node)) !== null && _a !== void 0 ? _a : HydrationMarkup.parseEnumeratedAttributeBinding(node)) !== null && _b !== void 0 ? _b : HydrationMarkup.parseCompactAttributeBinding(node);
102
- if (attrFactoryIds !== null) {
103
- for (const id of attrFactoryIds) {
104
- const factory = factories[id + hydrationIndexOffset];
105
- if (!factory) {
106
- 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);
107
- }
108
- targetFactory(factory, node, targets);
109
- }
110
- node.removeAttribute(HydrationMarkup.attributeMarkerName);
111
- }
112
- }
113
- function targetComment(node, walker, factories, targets, boundaries, hydrationIndexOffset) {
114
- if (HydrationMarkup.isElementBoundaryStartMarker(node)) {
115
- skipToElementBoundaryEndMarker(node, walker);
116
- return;
163
+ function targetContentBinding(node, walker, factory, factories, targets, boundaries) {
164
+ const nodes = [];
165
+ let current = walker.nextSibling();
166
+ node.data = "";
167
+ if (current === null) {
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);
117
171
  }
118
- if (HydrationMarkup.isContentBindingStartMarker(node.data)) {
119
- const parsed = HydrationMarkup.parseContentBindingStartMarker(node.data);
120
- if (parsed === null) {
121
- return;
122
- }
123
- const [index, id] = parsed;
124
- const factory = factories[index + hydrationIndexOffset];
125
- const nodes = [];
126
- let current = walker.nextSibling();
127
- node.data = "";
128
- const first = current;
129
- // Search for the binding end marker that closes the binding.
130
- while (current !== null) {
131
- if (isComment(current)) {
132
- const parsed = HydrationMarkup.parseContentBindingEndMarker(current.data);
133
- if (parsed && parsed[1] === id) {
172
+ const first = current;
173
+ // Balanced depth counting for nested content markers
174
+ let depth = 0;
175
+ while (current !== null) {
176
+ if (isComment(current)) {
177
+ if (HydrationMarkup.isContentBindingStartMarker(current.data)) {
178
+ depth++;
179
+ }
180
+ else if (HydrationMarkup.isContentBindingEndMarker(current.data)) {
181
+ if (depth === 0)
134
182
  break;
135
- }
183
+ depth--;
136
184
  }
137
- nodes.push(current);
138
- current = walker.nextSibling();
139
- }
140
- if (current === null) {
141
- const root = node.getRootNode();
142
- throw new Error(`Error hydrating Comment node inside "${isShadowRoot(root) ? root.host.nodeName : root.nodeName}".`);
143
185
  }
144
- current.data = "";
145
- if (nodes.length === 1 && isText(nodes[0])) {
146
- targetFactory(factory, nodes[0], targets);
147
- }
148
- else {
149
- // If current === first, it means there is no content in
150
- // the view. This happens when a `when` directive evaluates false,
151
- // or whenever a content binding returns null or undefined.
152
- // In that case, there will never be any content
153
- // to hydrate and Binding can simply create a HTMLView
154
- // whenever it needs to.
155
- if (current !== first && current.previousSibling !== null) {
156
- boundaries[factory.targetNodeId] = {
157
- first,
158
- last: current.previousSibling,
159
- };
160
- }
161
- // Binding evaluates to null / undefined or a template.
162
- // If binding revaluates to string, it will replace content in target
163
- // So we always insert a text node to ensure that
164
- // text content binding will be written to this text node instead of comment
165
- const dummyTextNode = current.parentNode.insertBefore(document.createTextNode(""), current);
166
- targetFactory(factory, dummyTextNode, targets);
186
+ nodes.push(current);
187
+ current = walker.nextSibling();
188
+ }
189
+ if (current === null) {
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);
193
+ }
194
+ current.data = "";
195
+ if (nodes.length === 1 && isText(nodes[0])) {
196
+ targetFactory(factory, nodes[0], targets);
197
+ }
198
+ else {
199
+ // If current === first, it means there is no content in
200
+ // the view. This happens when a `when` directive evaluates false,
201
+ // or whenever a content binding returns null or undefined.
202
+ if (current !== first && current.previousSibling !== null) {
203
+ boundaries[factory.targetNodeId] = {
204
+ first,
205
+ last: current.previousSibling,
206
+ };
167
207
  }
208
+ // Insert a text node so text content binding targets it
209
+ const dummyTextNode = current.parentNode.insertBefore(document.createTextNode(""), current);
210
+ targetFactory(factory, dummyTextNode, targets);
168
211
  }
169
212
  }
170
213
  /**
171
- * Moves TreeWalker to element boundary end marker
172
- * @param node - element boundary start marker node
173
- * @param walker - tree walker
214
+ * Skips past a nested custom element's shadow content using balanced
215
+ * depth counting to handle nested element boundaries correctly.
174
216
  */
175
- function skipToElementBoundaryEndMarker(node, walker) {
176
- const id = HydrationMarkup.parseElementBoundaryStartMarker(node.data);
217
+ function skipToElementBoundaryEnd(walker, factories, startNode) {
218
+ let depth = 0;
177
219
  let current = walker.nextSibling();
178
220
  while (current !== null) {
179
221
  if (isComment(current)) {
180
- const parsed = HydrationMarkup.parseElementBoundaryEndMarker(current.data);
181
- if (parsed && parsed === id) {
182
- break;
222
+ if (HydrationMarkup.isElementBoundaryStartMarker(current)) {
223
+ current.data = "";
224
+ depth++;
225
+ }
226
+ else if (HydrationMarkup.isElementBoundaryEndMarker(current)) {
227
+ if (depth === 0) {
228
+ current.data = "";
229
+ return;
230
+ }
231
+ current.data = "";
232
+ depth--;
183
233
  }
184
234
  }
185
235
  current = walker.nextSibling();
186
236
  }
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);
187
240
  }
188
241
  /**
189
242
  * Counts how many factories at the start of the array are host bindings (targetNodeId='h').
190
243
  * Host bindings target the custom element itself and are not represented by SSR markers,
191
- * so the marker indices must be offset by this count to align with the correct factory.
244
+ * so the factory pointer must start past them.
192
245
  */
193
246
  function getHydrationIndexOffset(factories) {
194
247
  let offset = 0;
@@ -0,0 +1,6 @@
1
+ export { deferHydrationAttribute, enableHydration, StopHydration, } from "./components/enable-hydration.js";
2
+ export { isHydratable } from "./components/hydration.js";
3
+ export { HydrationTracker } from "./components/hydration-tracker.js";
4
+ export { DOMPolicy, } from "./dom-policy.js";
5
+ export { hydrationDebugger } from "./hydration/hydration-debugger.js";
6
+ export { HydrationBindingError, HydrationStage, } from "./templating/hydration-view.js";
@@ -1,2 +1,3 @@
1
- import "./debug.js";
1
+ import { enableDebug } from "./debug.js";
2
+ enableDebug();
2
3
  export * from "./index.js";
package/dist/esm/index.js CHANGED
@@ -1,39 +1,48 @@
1
- export { emptyArray, FAST } from "./platform.js";
2
- // DOM
3
- export { DOM, DOMAspect } from "./dom.js";
4
- // Observation
5
- export { ArrayObserver, lengthOf, Sort, sortedCount, Splice, SpliceStrategy, SpliceStrategySupport, } from "./observation/arrays.js";
6
- export { PropertyChangeNotifier, SubscriberSet, } from "./observation/notifier.js";
7
- export { ExecutionContext, Observable, observable, SourceLifetime, volatile, } from "./observation/observable.js";
8
- export { Updates } from "./observation/update-queue.js";
1
+ /**
2
+ * Core APIs for building standards-based Web Components with FAST Element.
3
+ * @packageDocumentation
4
+ */
5
+ // Kernel
6
+ // Components
7
+ export { AttributeConfiguration, AttributeDefinition, attr, booleanConverter, nullableBooleanConverter, nullableNumberConverter, } from "./attr.js";
9
8
  // Binding
10
9
  export { Binding } from "./binding/binding.js";
11
10
  export { normalizeBinding } from "./binding/normalize.js";
12
11
  export { oneTime } from "./binding/one-time.js";
13
12
  export { listener, oneWay } from "./binding/one-way.js";
13
+ export { Signal, signal } from "./binding/signal.js";
14
+ export { TwoWaySettings, twoWay, } from "./binding/two-way.js";
15
+ export { ElementController, Stages, } from "./components/element-controller.js";
16
+ export { FASTElementDefinition, fastElementRegistry, } from "./components/fast-definitions.js";
17
+ export { customElement, FASTElement, } from "./components/fast-element.js";
18
+ export { enableDebug } from "./debug.js";
19
+ // Directives
20
+ export { ChildrenDirective, children, } from "./directives/children.js";
21
+ export { elements, NodeObservationDirective, } from "./directives/node-observation.js";
22
+ export { RefDirective, ref } from "./directives/ref.js";
23
+ export { RepeatBehavior, RepeatDirective, repeat, } from "./directives/repeat.js";
24
+ export { SlottedDirective, slotted, } from "./directives/slotted.js";
25
+ export { when } from "./directives/when.js";
26
+ export { DOM, DOMAspect } from "./dom.js";
27
+ export { DOMPolicy, } from "./dom-policy.js";
28
+ export { ArrayObserver, lengthOf, Sort, Splice, SpliceStrategy, SpliceStrategySupport, sortedCount, } from "./observation/arrays.js";
29
+ // Observation
30
+ export { PropertyChangeNotifier, SubscriberSet, } from "./observation/notifier.js";
31
+ export { ExecutionContext, Observable, observable, SourceLifetime, volatile, } from "./observation/observable.js";
32
+ export { Updates } from "./observation/update-queue.js";
33
+ export { emptyArray, FAST } from "./platform.js";
34
+ export { Schema, schemaRegistry, } from "./schema.js";
35
+ export * from "./state/exports.js";
14
36
  // Styles
15
- export { CSSBindingDirective } from "./styles/css-binding-directive.js";
16
- export { cssDirective, CSSDirective, } from "./styles/css-directive.js";
17
37
  export { css } from "./styles/css.js";
18
- export { ElementStyles, } from "./styles/element-styles.js";
19
- export {} from "./styles/host.js";
38
+ export { CSSDirective, cssDirective, } from "./styles/css-directive.js";
39
+ export { ElementStyles } from "./styles/element-styles.js";
20
40
  // Templating
21
- export { children, ChildrenDirective } from "./templating/children.js";
22
41
  export { Compiler } from "./templating/compiler.js";
23
- export { HTMLBindingDirective } from "./templating/html-binding-directive.js";
24
- export { htmlDirective, HTMLDirective, StatelessAttachedAttributeDirective, } from "./templating/html-directive.js";
42
+ export { HTMLBindingDirective, } from "./templating/html-binding-directive.js";
43
+ export { HTMLDirective, htmlDirective, StatelessAttachedAttributeDirective, } from "./templating/html-directive.js";
25
44
  export { Markup, Parser } from "./templating/markup.js";
26
- export { elements, NodeObservationDirective } from "./templating/node-observation.js";
27
- export { ref, RefDirective } from "./templating/ref.js";
28
- export { render, RenderBehavior, RenderDirective } from "./templating/render.js";
29
- export { repeat, RepeatBehavior, RepeatDirective } from "./templating/repeat.js";
30
- export { slotted, SlottedDirective } from "./templating/slotted.js";
31
- export { html, InlineTemplateDirective, ViewTemplate } from "./templating/template.js";
32
- export { HTMLView, HydrationBindingError } from "./templating/view.js";
33
- export { when } from "./templating/when.js";
34
- // Components
35
- export { attr, AttributeConfiguration, AttributeDefinition, booleanConverter, nullableBooleanConverter, nullableNumberConverter, } from "./components/attributes.js";
36
- export { ElementController, HydratableElementController, needsHydrationAttribute, Stages, } from "./components/element-controller.js";
37
- export { FASTElementDefinition, fastElementRegistry, TemplateOptions, } from "./components/fast-definitions.js";
38
- export { customElement, FASTElement } from "./components/fast-element.js";
39
- export { deferHydrationAttribute, isHydratable } from "./components/hydration.js";
45
+ export { RenderBehavior, RenderDirective, render } from "./templating/render.js";
46
+ export { html, InlineTemplateDirective, ViewTemplate, } from "./templating/template.js";
47
+ export { DefaultExecutionContext, HTMLView, } from "./templating/view.js";
48
+ export * from "./utilities.js";
@@ -1,5 +1,6 @@
1
- import "./debug.js";
2
- import { DOMPolicy } from "./dom-policy.js";
1
+ import { enableDebug } from "./debug.js";
3
2
  import { DOM } from "./dom.js";
3
+ import { DOMPolicy } from "./dom-policy.js";
4
+ enableDebug();
4
5
  export * from "./index.js";
5
6
  DOM.setPolicy(DOMPolicy.create());
@@ -1,4 +1,4 @@
1
- import { DOMPolicy } from "./dom-policy.js";
2
1
  import { DOM } from "./dom.js";
2
+ import { DOMPolicy } from "./dom-policy.js";
3
3
  export * from "./index.js";
4
4
  DOM.setPolicy(DOMPolicy.create());
@@ -1,48 +1,3 @@
1
- let kernelMode;
2
- const kernelAttr = "fast-kernel";
3
- try {
4
- if (document.currentScript) {
5
- kernelMode = document.currentScript.getAttribute(kernelAttr);
6
- }
7
- else {
8
- const scripts = document.getElementsByTagName("script");
9
- const currentScript = scripts[scripts.length - 1];
10
- kernelMode = currentScript.getAttribute(kernelAttr);
11
- }
12
- }
13
- catch (e) {
14
- kernelMode = "isolate";
15
- }
16
- let KernelServiceId;
17
- switch (kernelMode) {
18
- case "share": // share the kernel across major versions
19
- KernelServiceId = Object.freeze({
20
- updateQueue: 1,
21
- observable: 2,
22
- contextEvent: 3,
23
- elementRegistry: 4,
24
- });
25
- break;
26
- case "share-v2": // only share the kernel with other v2 instances
27
- KernelServiceId = Object.freeze({
28
- updateQueue: 1.2,
29
- observable: 2.2,
30
- contextEvent: 3.2,
31
- elementRegistry: 4.2,
32
- });
33
- break;
34
- default:
35
- // fully isolate the kernel from all other FAST instances
36
- const postfix = `-${Math.random().toString(36).substring(2, 8)}`;
37
- KernelServiceId = Object.freeze({
38
- updateQueue: `1.2${postfix}`,
39
- observable: `2.2${postfix}`,
40
- contextEvent: `3.2${postfix}`,
41
- elementRegistry: `4.2${postfix}`,
42
- });
43
- break;
44
- }
45
- export { KernelServiceId };
46
1
  /**
47
2
  * Warning and error messages.
48
3
  * @internal
@@ -62,6 +17,8 @@ export var Message;
62
17
  Message[Message["onlySetTemplatePolicyOnce"] = 1207] = "onlySetTemplatePolicyOnce";
63
18
  Message[Message["cannotSetTemplatePolicyAfterCompilation"] = 1208] = "cannotSetTemplatePolicyAfterCompilation";
64
19
  Message[Message["blockedByDOMPolicy"] = 1209] = "blockedByDOMPolicy";
20
+ Message[Message["invalidHydrationAttributeMarker"] = 1210] = "invalidHydrationAttributeMarker";
21
+ Message[Message["duplicateRenderInstruction"] = 1211] = "duplicateRenderInstruction";
65
22
  // 1301 - 1400 Styles
66
23
  // 1401 - 1500 Components
67
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.
@@ -1,5 +1,2 @@
1
- /**
2
- * Observable exports for easy access to the Observable API
3
- */
4
- export { ExecutionContext, Observable, observable, SourceLifetime, volatile, } from "./observation/observable.js";
5
1
  export { PropertyChangeNotifier, SubscriberSet } from "./observation/notifier.js";
2
+ export { ExecutionContext, Observable, observable, SourceLifetime, } from "./observation/observable.js";
@@ -589,7 +589,7 @@ export const ArrayObserver = Object.freeze({
589
589
  * Enables the array observation mechanism.
590
590
  * @remarks
591
591
  * Array observation is enabled automatically when using the
592
- * {@link RepeatDirective}, so calling this API manually is
592
+ * `RepeatDirective`, so calling this API manually is
593
593
  * not typically necessary.
594
594
  */
595
595
  enable() {
@@ -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
  }
@@ -1,4 +1,4 @@
1
- import { isFunction, isString, KernelServiceId, Message, } from "../interfaces.js";
1
+ import { isFunction, isString, Message, } from "../interfaces.js";
2
2
  import { createMetadataLocator, FAST, makeSerializationNoop } from "../platform.js";
3
3
  import { PropertyChangeNotifier, SubscriberSet } from "./notifier.js";
4
4
  import { Updates } from "./update-queue.js";
@@ -21,7 +21,7 @@ export const SourceLifetime = Object.freeze({
21
21
  * Common Observable APIs.
22
22
  * @public
23
23
  */
24
- export const Observable = FAST.getById(KernelServiceId.observable, () => {
24
+ export const Observable = (() => {
25
25
  const queueUpdate = Updates.enqueue;
26
26
  const volatileRegex = /(:|&&|\|\||if|\?\.)/;
27
27
  const notifierLookup = new WeakMap();
@@ -264,7 +264,7 @@ export const Observable = FAST.getById(KernelServiceId.observable, () => {
264
264
  return volatileRegex.test(expression.toString());
265
265
  },
266
266
  });
267
- });
267
+ })();
268
268
  /**
269
269
  * Decorator: Defines an observable property on the target.
270
270
  * @param target - The target to define the observable on.
@@ -289,7 +289,7 @@ export function volatile(target, name, descriptor) {
289
289
  },
290
290
  });
291
291
  }
292
- const contextEvent = FAST.getById(KernelServiceId.contextEvent, () => {
292
+ const contextEvent = (() => {
293
293
  let current = null;
294
294
  return {
295
295
  get() {
@@ -299,7 +299,7 @@ const contextEvent = FAST.getById(KernelServiceId.contextEvent, () => {
299
299
  current = event;
300
300
  },
301
301
  };
302
- });
302
+ })();
303
303
  /**
304
304
  * Provides additional contextual information available to behaviors and expressions.
305
305
  * @public