@jay-framework/compiler-jay-html 0.24.6 → 0.24.7

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.
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { camelCase as camelCase$1, JayImportedType, JayString, isObjectType as isObjectType$1, isImportedType, isRecursiveType, JayUnknown, Imports, Import, RenderFragment, WithValidations, isArrayType as isArrayType$1, isAtomicType, isPromiseType, isEnumType, GenerateTarget, hasRefs, JayTypeAlias, JayUnionType, mkRefsTree, equalJayTypes, mkRef, JayComponentType, JayHTMLType, getModeFileExtension, isHtmlStringType, RuntimeMode, nestRefs, mergeRefsTrees, SourceFileFormat, JayEnumType, JayObjectType, JayArrayType, JayPromiseType, resolvePrimitiveType, JayRecursiveType, JAY_CONTRACT_EXTENSION as JAY_CONTRACT_EXTENSION$1, ImportsFor, JAY_FULLSTACK_COMPONENTS, JayErrorType, JayOptionalType, JayRecordType, isOptionalType, isRecordType, resolvePluginManifest, resolvePluginComponent } from "@jay-framework/compiler-shared";
1
+ import { camelCase as camelCase$1, JayImportedType, JayString, JayUnknown, isObjectType as isObjectType$1, isImportedType, isRecursiveType, Imports, Import, RenderFragment, WithValidations, isArrayType as isArrayType$1, isAtomicType, isPromiseType, isEnumType, GenerateTarget, hasRefs, JayTypeAlias, JayUnionType, mkRefsTree, equalJayTypes, mkRef, JayComponentType, JayHTMLType, getModeFileExtension, isHtmlStringType, RuntimeMode, nestRefs, mergeRefsTrees, SourceFileFormat, JayEnumType, JayObjectType, JayArrayType, JayPromiseType, resolvePrimitiveType, JayRecursiveType, JAY_CONTRACT_EXTENSION as JAY_CONTRACT_EXTENSION$1, ImportsFor, JAY_FULLSTACK_COMPONENTS, hoistFreeRefs, freeRefsAugmentedRefType, isComponentType as isComponentType$1, JayErrorType, JayOptionalType, JayRecordType, isOptionalType, isRecordType, resolvePluginManifest, resolvePluginComponent } from "@jay-framework/compiler-shared";
2
2
  import path from "path";
3
3
  import { getLogger } from "@jay-framework/logger";
4
- import fs$1 from "fs/promises";
5
- import fs from "fs";
4
+ import fs from "fs/promises";
5
+ import fs$1 from "fs";
6
6
  import { analyzeExportedTypes } from "@jay-framework/compiler-analyze-exported-types";
7
7
  import { createRequire } from "module";
8
8
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
@@ -5820,15 +5820,7 @@ function walkChildren(parent, parentCoord, scopeId, options, counter) {
5820
5820
  ref = `AR${idx}`;
5821
5821
  element.setAttribute("ref", ref);
5822
5822
  }
5823
- assignHeadlessInstance(
5824
- element,
5825
- contractName,
5826
- ref,
5827
- parentCoord,
5828
- scopeId,
5829
- options,
5830
- counter
5831
- );
5823
+ assignHeadlessInstance(element, contractName, ref, parentCoord, options, counter);
5832
5824
  continue;
5833
5825
  }
5834
5826
  }
@@ -5851,18 +5843,18 @@ function walkChildren(parent, parentCoord, scopeId, options, counter) {
5851
5843
  walkChildren(element, coord, scopeId, options, counter);
5852
5844
  }
5853
5845
  }
5854
- function assignHeadlessInstance(element, contractName, ref, parentCoord, parentScopeId, options, counter) {
5846
+ function assignHeadlessInstance(element, contractName, ref, parentCoord, options, counter) {
5855
5847
  const instanceCoord = `${parentCoord}/${contractName}:${ref}`;
5856
5848
  element.setAttribute(COORD_ATTR$1, instanceCoord);
5857
5849
  const childScopeId = nextScopeId(counter);
5858
5850
  element.setAttribute(SCOPE_ATTR, childScopeId);
5859
- const significantChildren = element.childNodes.filter(
5851
+ const significantTemplateChildren = element.childNodes.filter(
5860
5852
  (n) => n.nodeType === NodeType.ELEMENT_NODE || n.nodeType === NodeType.TEXT_NODE && (n.innerText || "").trim() !== ""
5861
5853
  );
5862
- if (significantChildren.length > 1) {
5854
+ if (significantTemplateChildren.length > 1) {
5863
5855
  const wrapper = parse_2("<div></div>").querySelector("div");
5864
5856
  wrapper.setAttribute("style", "display: contents");
5865
- const children = [...element.childNodes];
5857
+ const children = element.childNodes;
5866
5858
  element.innerHTML = "";
5867
5859
  children.forEach((child) => wrapper.appendChild(child));
5868
5860
  element.appendChild(wrapper);
@@ -5886,15 +5878,7 @@ function walkForEachChildren(parent, itemScopeId, options, counter) {
5886
5878
  ref = `AR${idx}`;
5887
5879
  element.setAttribute("ref", ref);
5888
5880
  }
5889
- assignHeadlessInstance(
5890
- element,
5891
- contractName,
5892
- ref,
5893
- itemScopeId,
5894
- itemScopeId,
5895
- options,
5896
- counter
5897
- );
5881
+ assignHeadlessInstance(element, contractName, ref, itemScopeId, options, counter);
5898
5882
  continue;
5899
5883
  }
5900
5884
  }
@@ -6108,6 +6092,18 @@ function capitalCase(input, options) {
6108
6092
  }
6109
6093
  return noCase(input, __assign({ delimiter: " ", transform: capitalCaseTransform }, options));
6110
6094
  }
6095
+ function dotCase(input, options) {
6096
+ if (options === void 0) {
6097
+ options = {};
6098
+ }
6099
+ return noCase(input, __assign({ delimiter: "." }, options));
6100
+ }
6101
+ function paramCase(input, options) {
6102
+ if (options === void 0) {
6103
+ options = {};
6104
+ }
6105
+ return dotCase(input, __assign({ delimiter: "-" }, options));
6106
+ }
6111
6107
  function shouldIncludeInPhase(propertyPhase, targetPhase) {
6112
6108
  if (targetPhase === "slow") {
6113
6109
  return propertyPhase === "slow";
@@ -6588,11 +6584,11 @@ function peg$parse(input, options) {
6588
6584
  }, peg$c1 = function(comment2) {
6589
6585
  return null;
6590
6586
  }, peg$c2 = ":", peg$c3 = peg$literalExpectation(":", false), peg$c4 = ";", peg$c5 = peg$literalExpectation(";", false), peg$c6 = function(prop, value) {
6591
- const camelProp = prop.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
6587
+ const camelProp = prop.startsWith("--") ? prop : prop.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
6592
6588
  const [valueFragment, isDynamic] = value;
6593
6589
  return {
6594
6590
  property: camelProp,
6595
- valueFragment: isDynamic ? valueFragment.map((_) => `dp(${vars.currentVar} => ${_})`).plusImport(dp) : valueFragment,
6591
+ valueFragment: isDynamic ? valueFragment.map((_) => `dp(${jayParams(valueFragment)} => ${_})`).plusImport(dp) : valueFragment,
6596
6592
  isDynamic
6597
6593
  };
6598
6594
  }, peg$c7 = function() {
@@ -6630,31 +6626,32 @@ function peg$parse(input, options) {
6630
6626
  if (isDynamic) {
6631
6627
  if (tail.length === 0)
6632
6628
  return head.map((_) => `{${_}}`);
6633
- const mappedClasses = classes.map((cls) => {
6634
- return cls instanceof RenderFragment2 ? cls.map((_) => `\${${_}}`) : new RenderFragment2(cls, none);
6635
- });
6636
- const reduced = mappedClasses.reduce(
6637
- (result, fragment) => RenderFragment2.merge(result, fragment, " "),
6629
+ const elements = classes.map((cls) => cls instanceof RenderFragment2 ? cls : new RenderFragment2(`'${cls}'`));
6630
+ const reduced = elements.reduce(
6631
+ (result, fragment) => RenderFragment2.merge(result, fragment, ", "),
6638
6632
  RenderFragment2.empty()
6639
6633
  );
6640
- return reduced.map((_) => `{\`${_}\`}`);
6634
+ return reduced.map((_) => `{cx(${_})}`).plusImport(cx);
6641
6635
  } else
6642
6636
  return new RenderFragment2(classes.join(" "), none).map((_) => `"${_}"`);
6643
6637
  }, peg$c43 = function(head, tail) {
6644
- let isDynamic = false;
6645
- const renderClass = (cls) => {
6646
- isDynamic = isDynamic || cls instanceof RenderFragment2;
6647
- return cls instanceof RenderFragment2 ? cls.map((_) => "${" + _ + "}") : new RenderFragment2(cls, none);
6648
- };
6649
- let classString = tail.reduce((result, tuple) => {
6650
- const classExp = tuple[1];
6651
- return RenderFragment2.merge(result, renderClass(classExp), " ");
6652
- }, renderClass(head));
6653
- return isDynamic ? classString.map((_) => `da(${vars.currentVar} => \`${_}\`)`) : classString.map((_) => `'${_}'`);
6638
+ const classes = [head, ...tail.map((tuple) => tuple[1])];
6639
+ const isDynamic = classes.some((cls) => cls instanceof RenderFragment2);
6640
+ if (isDynamic) {
6641
+ if (tail.length === 0)
6642
+ return head.map((_) => `da(${jayParams(head)} => ${_})`);
6643
+ const elements = classes.map((cls) => cls instanceof RenderFragment2 ? cls : new RenderFragment2(`'${cls}'`));
6644
+ const arrayContents = elements.reduce(
6645
+ (result, fragment) => RenderFragment2.merge(result, fragment, ", "),
6646
+ RenderFragment2.empty()
6647
+ );
6648
+ return arrayContents.map((_) => `da(${jayParams(arrayContents)} => cx(${_}))`).plusImport(cx);
6649
+ }
6650
+ return new RenderFragment2(classes.join(" "), none).map((_) => `'${_}'`);
6654
6651
  }, peg$c44 = function(cls) {
6655
6652
  return cls.render().plusImport(da);
6656
6653
  }, peg$c45 = /^[{]/, peg$c46 = peg$classExpectation(["{"], false, false), peg$c47 = /^[?]/, peg$c48 = peg$classExpectation(["?"], false, false), peg$c49 = /^[:]/, peg$c50 = peg$classExpectation([":"], false, false), peg$c51 = /^[}]/, peg$c52 = peg$classExpectation(["}"], false, false), peg$c53 = function(acc, classY, classN) {
6657
- return new RenderFragment2(`${acc.rendered}?'${classY}':'${classN ? classN[3] : ""}'`, da, acc.validations);
6654
+ return new RenderFragment2(`${acc.rendered}?'${classY}':'${classN ? classN[3] : ""}'`, da, acc.validations, void 0, void 0, acc.parentDepth);
6658
6655
  }, peg$c54 = /^[\-]/, peg$c55 = peg$classExpectation(["-"], false, false), peg$c56 = /^[_a-zA-Z]/, peg$c57 = peg$classExpectation(["_", ["a", "z"], ["A", "Z"]], false, false), peg$c58 = /^[_a-zA-Z0-9\-]/, peg$c59 = peg$classExpectation(["_", ["a", "z"], ["A", "Z"], ["0", "9"], "-"], false, false), peg$c60 = function() {
6659
6656
  return text2();
6660
6657
  }, peg$c61 = "as", peg$c62 = peg$literalExpectation("as", false), peg$c63 = ",", peg$c64 = peg$literalExpectation(",", false), peg$c65 = function(head, rename, tail) {
@@ -6669,10 +6666,10 @@ function peg$parse(input, options) {
6669
6666
  return [...acc, val[3]];
6670
6667
  }, [first]);
6671
6668
  }, peg$c78 = function(cond) {
6672
- return cond.map((_) => `ba(${vars.currentVar} => ${_})`).plusImport(ba);
6669
+ return cond.map((_) => `ba(${jayParams(cond)} => ${_})`).plusImport(ba);
6673
6670
  }, peg$c79 = function(template) {
6674
6671
  let [renderFragment, isDynamic] = template;
6675
- return isDynamic ? renderFragment.map((_) => `da(${vars.currentVar} => ${_})`).plusImport(da) : renderFragment;
6672
+ return isDynamic ? renderFragment.map((_) => `da(${jayParams(renderFragment)} => ${_})`).plusImport(da) : renderFragment;
6676
6673
  }, peg$c80 = function(num) {
6677
6674
  return new RenderFragment2(num);
6678
6675
  }, peg$c81 = function(template) {
@@ -6680,7 +6677,7 @@ function peg$parse(input, options) {
6680
6677
  return renderFragment;
6681
6678
  }, peg$c82 = function(template) {
6682
6679
  let [renderFragment, isDynamic] = template;
6683
- return isDynamic ? renderFragment.map((_) => `dp(${vars.currentVar} => ${_})`).plusImport(dp) : renderFragment;
6680
+ return isDynamic ? renderFragment.map((_) => `dp(${jayParams(renderFragment)} => ${_})`).plusImport(dp) : renderFragment;
6684
6681
  }, peg$c83 = function(template) {
6685
6682
  let [renderFragment, isDynamic] = template;
6686
6683
  return isDynamic ? renderFragment.map((_) => `{${_}}`) : renderFragment;
@@ -6705,7 +6702,7 @@ function peg$parse(input, options) {
6705
6702
  }
6706
6703
  }, peg$c86 = function(template) {
6707
6704
  let [renderFragment, isDynamic] = template;
6708
- return isDynamic ? renderFragment.map((_) => `dt(${vars.currentVar} => ${_})`).plusImport(dt) : renderFragment;
6705
+ return isDynamic ? renderFragment.map((_) => `dt(${jayParams(renderFragment)} => ${_})`).plusImport(dt) : renderFragment;
6709
6706
  }, peg$c87 = function(a, head, tail) {
6710
6707
  const renderText2 = (w2, h) => {
6711
6708
  return h ? h.reduce((acc, str2) => acc + str2[0] + (str2[1].length ? " " : ""), w2.length ? " " : "") : w2.length ? " " : "";
@@ -6730,7 +6727,7 @@ function peg$parse(input, options) {
6730
6727
  const raw = text2();
6731
6728
  return { kind: "binding", value: raw.slice(1, -1).trim() };
6732
6729
  }, peg$c93 = function(cond) {
6733
- return cond.map((_) => `${vars.currentVar} => ${_}`);
6730
+ return cond.map((_) => `${jayParams(cond)} => ${_}`);
6734
6731
  }, peg$c94 = "||", peg$c95 = peg$literalExpectation("||", false), peg$c96 = function(head, tail) {
6735
6732
  if (tail.length === 0) return head;
6736
6733
  return tail.reduce((acc, curr) => combineOr(acc, curr[3]), head);
@@ -13302,6 +13299,7 @@ function peg$parse(input, options) {
13302
13299
  let da = options.da;
13303
13300
  let dp = options.dp;
13304
13301
  let ba = options.ba;
13302
+ let cx = options.cx;
13305
13303
  let slowContext = options.slowContext;
13306
13304
  function isSlowPhase2(path2) {
13307
13305
  if (!slowContext) return false;
@@ -13420,6 +13418,13 @@ function peg$parse(input, options) {
13420
13418
  expr: `${leftExpr} ${jsOp} ${rightExpr}`
13421
13419
  };
13422
13420
  }
13421
+ function jayParams(renderFragment) {
13422
+ const depth = renderFragment && renderFragment.parentDepth || 0;
13423
+ if (depth === 0) return vars.currentVar;
13424
+ let params = vars.currentVar;
13425
+ for (let i = 1; i <= depth; i++) params += `, _p${i}`;
13426
+ return `(${params})`;
13427
+ }
13423
13428
  function applyStartsWith(left, right) {
13424
13429
  if (left.type === "resolved" && right.type === "resolved") {
13425
13430
  return { type: "resolved", value: String(left.value).startsWith(String(right.value)) };
@@ -13450,18 +13455,37 @@ function peg$parse(input, options) {
13450
13455
  var expressionParser = {
13451
13456
  parse: peg$parse
13452
13457
  };
13458
+ const PARENT_TOKEN = "$parent";
13459
+ function parentParamName(level) {
13460
+ return `_p${level}`;
13461
+ }
13453
13462
  class Accessor {
13454
13463
  rootVar;
13455
13464
  terms;
13456
13465
  validations;
13457
13466
  resolvedType;
13458
- constructor(rootVar, terms, validations, resolvedType) {
13467
+ /** DL#193: how many scopes a `$parent` access climbs (0 = current scope). */
13468
+ parentLevel;
13469
+ constructor(rootVar, terms, validations, resolvedType, parentLevel = 0) {
13459
13470
  this.rootVar = rootVar;
13460
13471
  this.terms = terms;
13461
13472
  this.validations = validations;
13462
13473
  this.resolvedType = resolvedType;
13474
+ this.parentLevel = parentLevel;
13463
13475
  }
13464
13476
  render() {
13477
+ if (this.parentLevel > 0) {
13478
+ const param = parentParamName(this.parentLevel);
13479
+ const rendered = this.terms.length === 0 ? param : param + "." + this.terms.join("?.");
13480
+ return new RenderFragment(
13481
+ rendered,
13482
+ Imports.none(),
13483
+ this.validations,
13484
+ void 0,
13485
+ void 0,
13486
+ this.parentLevel
13487
+ );
13488
+ }
13465
13489
  let renderedAccessor = this.terms.length === 1 && this.terms[0] === "." ? this.rootVar : this.rootVar + "." + this.terms.join("?.");
13466
13490
  return new RenderFragment(`${renderedAccessor}`, Imports.none(), this.validations);
13467
13491
  }
@@ -13471,20 +13495,76 @@ class Variables {
13471
13495
  currentType;
13472
13496
  currentContext;
13473
13497
  parent;
13474
- children = {};
13498
+ children;
13475
13499
  depth;
13476
- constructor(currentTypes, parent = void 0, depth = 0, customVarName) {
13500
+ /**
13501
+ * DL#193 Phase 2c: when true, this scope's `currentVar` is a real variable that is lexically
13502
+ * in scope at every descendant binding site (the server target inlines the whole tree into one
13503
+ * `renderToStream`, so `vs`, forEach item vars, and instance vars are all reachable by name).
13504
+ * In that world a `$parent` climb that lands on this scope must emit `currentVar` directly, not
13505
+ * a `_pN` closure param (which only exists in the client/hydrate per-binding callbacks). The
13506
+ * flag is inherited by every child scope so setting it once on the server root covers the tree.
13507
+ */
13508
+ lexicallyInScope;
13509
+ constructor(currentTypes, parent = void 0, depth = 0, customVarName, children = {}, lexicallyInScope = false) {
13477
13510
  this.currentVar = customVarName || (depth === 0 ? "vs" : "vs" + depth);
13478
13511
  this.currentContext = depth === 0 ? "context" : "cx" + depth;
13479
13512
  this.depth = depth;
13480
13513
  this.parent = parent;
13481
13514
  this.currentType = currentTypes instanceof JayImportedType ? currentTypes.type : currentTypes;
13515
+ this.children = children;
13516
+ this.lexicallyInScope = lexicallyInScope;
13517
+ }
13518
+ /**
13519
+ * DL#193 Phase 2c: return a view of this scope marked lexically-in-scope (see
13520
+ * {@link lexicallyInScope}). Used by the server target on its root scope; child scopes
13521
+ * inherit the flag automatically through {@link childVariableFor} / {@link childVariableForWithData}.
13522
+ */
13523
+ asLexical() {
13524
+ if (this.lexicallyInScope) return this;
13525
+ return new Variables(
13526
+ this.currentType,
13527
+ this.parent,
13528
+ this.depth,
13529
+ this.currentVar,
13530
+ this.children,
13531
+ true
13532
+ );
13482
13533
  }
13483
13534
  resolveAccessor(accessor) {
13484
13535
  if (accessor[0] === "jay") {
13485
13536
  const jayPath = ["__jay", ...accessor.slice(1)];
13486
13537
  return new Accessor(this.currentVar, jayPath, [], JayString);
13487
13538
  }
13539
+ if (accessor[0] === PARENT_TOKEN) {
13540
+ let parentLevel = 0;
13541
+ let scope = this;
13542
+ let terms = accessor;
13543
+ while (terms[0] === PARENT_TOKEN) {
13544
+ parentLevel++;
13545
+ if (!scope.parent) {
13546
+ const message = `${"$parent".repeat(parentLevel)} used but there is no parent scope ${parentLevel} level(s) up`;
13547
+ return new Accessor(
13548
+ this.currentVar,
13549
+ terms.slice(1),
13550
+ [message],
13551
+ JayUnknown,
13552
+ parentLevel
13553
+ );
13554
+ }
13555
+ scope = scope.parent;
13556
+ terms = terms.slice(1);
13557
+ }
13558
+ const resolved = scope.resolveAccessor(terms);
13559
+ const effectiveParentLevel = scope.lexicallyInScope ? resolved.parentLevel : parentLevel + resolved.parentLevel;
13560
+ return new Accessor(
13561
+ scope.currentVar,
13562
+ resolved.terms,
13563
+ resolved.validations,
13564
+ resolved.resolvedType,
13565
+ effectiveParentLevel
13566
+ );
13567
+ }
13488
13568
  let curr = this.currentType;
13489
13569
  let validations = [];
13490
13570
  accessor.forEach((member) => {
@@ -13506,7 +13586,14 @@ class Variables {
13506
13586
  if (this.children[path2]) return this.children[path2];
13507
13587
  else {
13508
13588
  const resolvedForEachType = accessor.resolvedType.itemType;
13509
- const variables = new Variables(resolvedForEachType, this, this.depth + 1);
13589
+ const variables = new Variables(
13590
+ resolvedForEachType,
13591
+ this,
13592
+ this.depth + 1,
13593
+ void 0,
13594
+ {},
13595
+ this.lexicallyInScope
13596
+ );
13510
13597
  this.children[path2] = variables;
13511
13598
  return variables;
13512
13599
  }
@@ -13522,7 +13609,14 @@ class Variables {
13522
13609
  depth++;
13523
13610
  parent = parent.parent;
13524
13611
  }
13525
- const variables = new Variables(accessor.resolvedType, this, depth);
13612
+ const variables = new Variables(
13613
+ accessor.resolvedType,
13614
+ this,
13615
+ depth,
13616
+ void 0,
13617
+ {},
13618
+ this.lexicallyInScope
13619
+ );
13526
13620
  this.children[path2] = variables;
13527
13621
  return variables;
13528
13622
  }
@@ -13677,6 +13771,7 @@ function doParse(expression, startRule, vars, throwOnError = false) {
13677
13771
  da: Imports.for(Import.dynamicAttribute),
13678
13772
  dp: Imports.for(Import.dynamicProperty),
13679
13773
  ba: Imports.for(Import.booleanAttribute),
13774
+ cx: Imports.for(Import.classNames),
13680
13775
  startRule
13681
13776
  });
13682
13777
  } catch (e2) {
@@ -13697,8 +13792,19 @@ function parseAccessor(expression, vars) {
13697
13792
  function parseCondition(expression, vars) {
13698
13793
  return doParse(expression, "conditionFunc", vars);
13699
13794
  }
13795
+ function guardReactParentBinding(fragment) {
13796
+ if (fragment.parentDepth === 0) return fragment;
13797
+ return new RenderFragment(
13798
+ fragment.rendered,
13799
+ fragment.imports,
13800
+ [...fragment.validations, "$parent bindings are not supported in the React target"],
13801
+ fragment.refs,
13802
+ fragment.recursiveRegions,
13803
+ fragment.parentDepth
13804
+ );
13805
+ }
13700
13806
  function parseReactCondition(expression, vars) {
13701
- return doParse(expression, "condition", vars);
13807
+ return guardReactParentBinding(doParse(expression, "condition", vars));
13702
13808
  }
13703
13809
  function parseTextExpression(expression, vars) {
13704
13810
  return doParse(expression, "dynamicText", vars);
@@ -13721,7 +13827,9 @@ function unescapeBackslash(jsCode) {
13721
13827
  });
13722
13828
  }
13723
13829
  function parseReactTextExpression(expression, vars) {
13724
- return doParse(expression, "reactDynamicText", vars).map((_) => unescapeBackslash(_));
13830
+ return guardReactParentBinding(
13831
+ doParse(expression, "reactDynamicText", vars).map((_) => unescapeBackslash(_))
13832
+ );
13725
13833
  }
13726
13834
  function parseBooleanAttributeExpression(expression, vars) {
13727
13835
  return doParse(expression, "booleanAttribute", vars);
@@ -13733,7 +13841,7 @@ function parsePropertyExpression(expression, vars) {
13733
13841
  return doParse(expression, "dynamicProperty", vars);
13734
13842
  }
13735
13843
  function parseReactPropertyExpression(expression, vars) {
13736
- return doParse(expression, "reactDynamicProperty", vars);
13844
+ return guardReactParentBinding(doParse(expression, "reactDynamicProperty", vars));
13737
13845
  }
13738
13846
  function parseComponentPropExpression(expression, vars) {
13739
13847
  return doParse(expression, "dynamicComponentProp", vars);
@@ -13741,23 +13849,34 @@ function parseComponentPropExpression(expression, vars) {
13741
13849
  function parseClassExpression(expression, vars) {
13742
13850
  return doParse(expression, "classExpression", vars);
13743
13851
  }
13852
+ const UNSUPPORTED_TARGET_PARENT_MESSAGE = "$parent bindings are not yet supported in the server target";
13853
+ class UnsupportedServerParentBindingError extends Error {
13854
+ constructor() {
13855
+ super(UNSUPPORTED_TARGET_PARENT_MESSAGE);
13856
+ this.name = "UnsupportedServerParentBindingError";
13857
+ }
13858
+ }
13859
+ function guardServerParentBinding(fragment) {
13860
+ if (fragment.parentDepth === 0) return fragment;
13861
+ throw new UnsupportedServerParentBindingError();
13862
+ }
13744
13863
  function parseServerTemplateExpression(expression, vars) {
13745
- return doParse(expression, "template", vars);
13864
+ const [fragment, isDynamic] = doParse(expression, "template", vars);
13865
+ return [guardServerParentBinding(fragment), isDynamic];
13746
13866
  }
13747
13867
  function parseTemplateParts(value) {
13748
13868
  if (!value) return [];
13749
13869
  return doParse(value, "templateParts");
13750
13870
  }
13751
13871
  function parseServerCondition(expression, vars) {
13752
- return doParse(expression, "condition", vars);
13872
+ return guardServerParentBinding(doParse(expression, "condition", vars));
13753
13873
  }
13754
13874
  function parseReactClassExpression(expression, vars) {
13755
- const { rendered, validations, refs } = doParse(
13756
- expression,
13757
- "reactClassExpression",
13758
- vars
13875
+ const parsed = doParse(expression, "reactClassExpression", vars);
13876
+ const { rendered, validations, refs } = parsed;
13877
+ return guardReactParentBinding(
13878
+ new RenderFragment(rendered, Imports.none(), validations, refs, [], parsed.parentDepth)
13759
13879
  );
13760
- return new RenderFragment(rendered, Imports.none(), validations, refs);
13761
13880
  }
13762
13881
  function parseImportNames(expression) {
13763
13882
  return doParse(expression, "importNames", void 0, true);
@@ -14136,7 +14255,7 @@ const isComponentType = (type2) => type2 instanceof JayComponentType || type2 in
14136
14255
  const isComponentRef = (ref) => isComponentType(ref.elementType);
14137
14256
  const isCollectionRef = (ref) => ref.repeated;
14138
14257
  const isComponentCollectionRef = (ref) => isCollectionRef(ref) && isComponentRef(ref);
14139
- function renderRefsType(refs, refsType, generateTarget = GenerateTarget.jay) {
14258
+ function renderRefsType(refs, refsType, generateTarget = GenerateTarget.jay, emitComponentRefHelpers = true, emittedHelpers) {
14140
14259
  let renderedRefs;
14141
14260
  let imports = Imports.none();
14142
14261
  const componentRefs = /* @__PURE__ */ new Map();
@@ -14193,7 +14312,7 @@ function renderRefsType(refs, refsType, generateTarget = GenerateTarget.jay) {
14193
14312
  }
14194
14313
  return `${indent.curr}${ref.ref}: ${referenceType}`;
14195
14314
  }).join(",\n");
14196
- const childTypes = Object.entries(refsTree.children).filter(([_, childRefNode]) => {
14315
+ const childTypes = Object.entries(refsTree.children).filter(([, childRefNode]) => {
14197
14316
  return childRefNode.imported || hasRefs(childRefNode, false);
14198
14317
  }).map(([childName, childRefNode]) => {
14199
14318
  if (childRefNode.imported) {
@@ -14213,20 +14332,28 @@ ${allTypes}
14213
14332
  ${indent.lastLine}}`;
14214
14333
  };
14215
14334
  const mainType = generateTypeForPath(refs, new Indent("", true, true));
14216
- const renderedComponentRefs = [...componentRefs].map(([componentName, refsNeeded]) => {
14335
+ const renderedComponentRefs = emitComponentRefHelpers ? [...componentRefs].map(([componentName, refsNeeded]) => {
14217
14336
  const elementType = generateTarget === GenerateTarget.jay ? `ReturnType<typeof ${componentName}>` : "any";
14218
- let refTypes = `export type ${componentName}Ref<ParentVS> = MapEventEmitterViewState<ParentVS, ${elementType}>;`;
14219
- imports = imports.plus(Import.MapEventEmitterViewState);
14220
- if (refsNeeded === 1) {
14221
- refTypes += `
14222
- export type ${componentName}Refs<ParentVS> =
14223
- ComponentCollectionProxy<ParentVS, ${componentName}Ref<ParentVS>> &
14224
- OnlyEventEmitters<${componentName}Ref<ParentVS>>
14225
- `;
14337
+ const refId = `${componentName}Ref`;
14338
+ const refsId = `${componentName}Refs`;
14339
+ const parts = [];
14340
+ if (!emittedHelpers?.has(refId)) {
14341
+ parts.push(
14342
+ `export type ${refId}<ParentVS> = MapEventEmitterViewState<ParentVS, ${elementType}>;`
14343
+ );
14344
+ imports = imports.plus(Import.MapEventEmitterViewState);
14345
+ emittedHelpers?.add(refId);
14346
+ }
14347
+ if (refsNeeded === 1 && !emittedHelpers?.has(refsId)) {
14348
+ parts.push(`export type ${refsId}<ParentVS> =
14349
+ ComponentCollectionProxy<ParentVS, ${refId}<ParentVS>> &
14350
+ OnlyEventEmitters<${refId}<ParentVS>>
14351
+ `);
14226
14352
  imports = imports.plus(Import.ComponentCollectionProxy).plus(Import.OnlyEventEmitters);
14353
+ emittedHelpers?.add(refsId);
14227
14354
  }
14228
- return refTypes;
14229
- });
14355
+ return parts.join("\n");
14356
+ }).filter(Boolean) : [];
14230
14357
  renderedRefs = `${renderedComponentRefs.join("\n")}
14231
14358
  export interface ${refsType} ${mainType}`;
14232
14359
  } else renderedRefs = `export interface ${refsType} {}`;
@@ -14241,16 +14368,6 @@ class RefNameGenerator {
14241
14368
  newAutoRefNameGenerator() {
14242
14369
  return "aR" + this.nextId++;
14243
14370
  }
14244
- /**
14245
- * Register a constName as used with the given Variables context.
14246
- * This is used when an imported ref is used to ensure later refs
14247
- * with the same name get a different suffix.
14248
- */
14249
- registerUsedConstName(constName, variables) {
14250
- if (!this.constNamesToVariables.has(constName)) {
14251
- this.constNamesToVariables.set(constName, variables);
14252
- }
14253
- }
14254
14371
  newConstantName(refName, variables) {
14255
14372
  let suffix = 2;
14256
14373
  let constName = camelCase$1(`ref ${refName}`);
@@ -14528,6 +14645,8 @@ const REFERENCE_MANAGER_TYPES = {
14528
14645
  function renderReferenceManager(refs, target) {
14529
14646
  const { referenceManagerInit, imports } = REFERENCE_MANAGER_TYPES[target];
14530
14647
  const options = target === 0 ? "options, " : "";
14648
+ let usesComponentRefsImpl = false;
14649
+ let usesComponentCollectionRefsImpl = false;
14531
14650
  const usedRefManagerNames = /* @__PURE__ */ new Set();
14532
14651
  const usedRefConstNames = /* @__PURE__ */ new Set();
14533
14652
  const collectReservedConstNames = (refsTree) => {
@@ -14587,11 +14706,15 @@ function renderReferenceManager(refs, target) {
14587
14706
  const elemCollectionRefsDeclarations = elemCollectionRefs.map((ref) => `'${ref.ref}'`).join(", ");
14588
14707
  const compRefsDeclarations = compRefs.map((ref) => `'${ref.ref}'`).join(", ");
14589
14708
  const compCollectionRefsDeclarations = compCollectionRefs.map((ref) => `'${ref.ref}'`).join(", ");
14709
+ const elemRefVars = elemRefs.map((ref) => getUniqueRefConstName(ref));
14710
+ const elemCollectionRefVars = elemCollectionRefs.map((ref) => getUniqueRefConstName(ref));
14711
+ const compRefVars = compRefs.map((ref) => getUniqueRefConstName(ref));
14712
+ const compCollectionRefVars = compCollectionRefs.map((ref) => getUniqueRefConstName(ref));
14590
14713
  const refVariables = [
14591
- ...elemRefs.map((ref) => getUniqueRefConstName(ref)),
14592
- ...elemCollectionRefs.map((ref) => getUniqueRefConstName(ref)),
14593
- ...compRefs.map((ref) => getUniqueRefConstName(ref)),
14594
- ...compCollectionRefs.map((ref) => getUniqueRefConstName(ref))
14714
+ ...elemRefVars,
14715
+ ...elemCollectionRefVars,
14716
+ ...compRefVars,
14717
+ ...compCollectionRefVars
14595
14718
  ].join(", ");
14596
14719
  const childRenderedRefManagers = [];
14597
14720
  const childRefManagerMembers = [];
@@ -14606,11 +14729,38 @@ function renderReferenceManager(refs, target) {
14606
14729
  }` : "";
14607
14730
  const renderedRefManager = ` const [${name}, [${refVariables}]] =
14608
14731
  ${referenceManagerInit}(${options}[${elemRefsDeclarations}], [${elemCollectionRefsDeclarations}], [${compRefsDeclarations}], [${compCollectionRefsDeclarations}]${childRefManager});`;
14609
- return [...childRenderedRefManagers, renderedRefManager].join("\n");
14732
+ const emitFreeRefManager = (ref, constName, regionIsCollection) => {
14733
+ const freeMgrName = `${constName}FreeRefManager`;
14734
+ const asCollection = (free) => regionIsCollection || free.repeated;
14735
+ const elementNames = ref.freeRefs.filter((free) => !asCollection(free)).map((free) => `'${free.name}'`).join(", ");
14736
+ const collectionNames = ref.freeRefs.filter((free) => asCollection(free)).map((free) => `'${free.name}'`).join(", ");
14737
+ const forCall = `${referenceManagerInit}(${options}[${elementNames}], [${collectionNames}], [], [])`;
14738
+ if (regionIsCollection) {
14739
+ usesComponentCollectionRefsImpl = true;
14740
+ return ` const [${freeMgrName}] = ${forCall};
14741
+ (${name}.get('${ref.ref}') as ComponentCollectionRefImpl<any, any>).setFreeRefManager(${freeMgrName});`;
14742
+ } else {
14743
+ usesComponentRefsImpl = true;
14744
+ return ` const [${freeMgrName}] = ${forCall};
14745
+ (${name}.get('${ref.ref}') as ComponentRefsImpl<any, any>).setFreeRefManager(${freeMgrName});`;
14746
+ }
14747
+ };
14748
+ const freeRefManagerLines = target === 0 ? [
14749
+ ...compRefs.map((ref, i) => ({ ref, constName: compRefVars[i] })).filter(({ ref }) => ref.freeRefs && ref.freeRefs.length > 0).map(({ ref, constName }) => emitFreeRefManager(ref, constName, false)),
14750
+ ...compCollectionRefs.map((ref, i) => ({ ref, constName: compCollectionRefVars[i] })).filter(({ ref }) => ref.freeRefs && ref.freeRefs.length > 0).map(({ ref, constName }) => emitFreeRefManager(ref, constName, true))
14751
+ ] : [];
14752
+ return [...childRenderedRefManagers, renderedRefManager, ...freeRefManagerLines].join("\n");
14610
14753
  };
14611
14754
  if (hasRefs(refs, true)) {
14612
14755
  const renderedRefsManager = renderRefManagerNode("refManager", refs);
14613
- return { renderedRefsManager, refsManagerImport: imports };
14756
+ let refsManagerImport = imports;
14757
+ if (usesComponentRefsImpl)
14758
+ refsManagerImport = refsManagerImport.plus(Imports.for(Import.ComponentRefsImpl));
14759
+ if (usesComponentCollectionRefsImpl)
14760
+ refsManagerImport = refsManagerImport.plus(
14761
+ Imports.for(Import.ComponentCollectionRefImpl)
14762
+ );
14763
+ return { renderedRefsManager, refsManagerImport };
14614
14764
  } else {
14615
14765
  const renderedRefsManager = `const [refManager, []] = ${referenceManagerInit}(${options}[], [], [], []);`;
14616
14766
  return { renderedRefsManager, refsManagerImport: imports };
@@ -14618,16 +14768,27 @@ function renderReferenceManager(refs, target) {
14618
14768
  }
14619
14769
  function renderImports$1(imports, importsFor, componentImports, importerMode) {
14620
14770
  const runtimeImport = imports.render(importsFor);
14621
- let renderedComponentImports = componentImports.map((importStatement) => {
14622
- let symbols = importStatement.names.map((symbol) => symbol.as ? `${symbol.name} as ${symbol.as}` : symbol.name).join(", ");
14623
- let imports2 = [];
14624
- imports2.push(
14625
- `import {${symbols}} from "${importStatement.module}${getModeFileExtension(
14626
- importStatement.sandbox,
14627
- importerMode
14628
- )}";`
14629
- );
14630
- return imports2.join("\n");
14771
+ const mergedByModule = /* @__PURE__ */ new Map();
14772
+ for (const importStatement of componentImports) {
14773
+ const key = `${importStatement.sandbox ? "sandbox:" : ""}${importStatement.module}`;
14774
+ let entry = mergedByModule.get(key);
14775
+ if (!entry) {
14776
+ entry = { link: { ...importStatement, names: [] }, symbols: /* @__PURE__ */ new Set() };
14777
+ mergedByModule.set(key, entry);
14778
+ }
14779
+ for (const symbol of importStatement.names) {
14780
+ const rendered = symbol.as ? `${symbol.name} as ${symbol.as}` : symbol.name;
14781
+ if (entry.symbols.has(rendered)) continue;
14782
+ entry.symbols.add(rendered);
14783
+ entry.link.names.push(symbol);
14784
+ }
14785
+ }
14786
+ let renderedComponentImports = [...mergedByModule.values()].map(({ link }) => {
14787
+ let symbols = link.names.map((symbol) => symbol.as ? `${symbol.name} as ${symbol.as}` : symbol.name).join(", ");
14788
+ return `import {${symbols}} from "${link.module}${getModeFileExtension(
14789
+ link.sandbox,
14790
+ importerMode
14791
+ )}";`;
14631
14792
  });
14632
14793
  return [runtimeImport, ...renderedComponentImports].join("\n");
14633
14794
  }
@@ -14837,6 +14998,12 @@ const DIRECTIVE_ATTRIBUTES = /* @__PURE__ */ new Set([
14837
14998
  "slow",
14838
14999
  "jay-coordinate-base",
14839
15000
  "jay-scope",
15001
+ // Component provenance marker stamped by the parser on `<jay:Name>` usage tags (DL#123 discovery).
15002
+ // Read by the coordinate/discovery pipeline, never a component prop or a rendered attribute.
15003
+ "jc",
15004
+ // DL#202 — template-side content-slot marker. A pure build-time authoring directive read by the
15005
+ // drift/sync differ; it rides into the flattened page copy (verbatim flatten) but must never render.
15006
+ "jay-content",
14840
15007
  AsyncDirectiveTypes.loading.directive,
14841
15008
  AsyncDirectiveTypes.resolved.directive,
14842
15009
  AsyncDirectiveTypes.rejected.directive
@@ -18960,7 +19127,7 @@ function dedentYaml(text2) {
18960
19127
  const minIndent = Math.min(...lines.map((l) => l.match(/^\s*/)?.[0].length ?? 0));
18961
19128
  return lines.map((l) => l.slice(minIndent)).join("\n");
18962
19129
  }
18963
- async function parseHeadlessImports(elements, validations, filePath, importResolver, projectRoot) {
19130
+ async function parseHeadlessImports(elements, validations, filePath, importResolver, projectRoot, options) {
18964
19131
  const result = [];
18965
19132
  for await (const element of elements) {
18966
19133
  const pluginAttr = element.getAttribute("plugin");
@@ -18972,10 +19139,16 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
18972
19139
  );
18973
19140
  continue;
18974
19141
  }
18975
- if (!pluginAttr) {
18976
- validations.push("headless import must specify plugin attribute");
19142
+ const asAttr = element.getAttribute("as") ?? void 0;
19143
+ if (asAttr && !/^[a-z][a-z0-9-]*$/.test(asAttr)) {
19144
+ validations.push(
19145
+ `Headless component as="${asAttr}" is not a valid region tag name. Use kebab-case (e.g., as="feature-card").`
19146
+ );
18977
19147
  continue;
18978
19148
  }
19149
+ const srcAttr = element.getAttribute("src") ?? void 0;
19150
+ const templateAttr = element.getAttribute("template") ?? void 0;
19151
+ const suppressedValidations = (element.getAttribute("jay-validations") ?? "").split(/[\s,]+/).map((s) => s.trim().toUpperCase()).filter(Boolean);
18979
19152
  if (!contractAttr) {
18980
19153
  validations.push("headless import must specify contract attribute");
18981
19154
  continue;
@@ -18997,41 +19170,75 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
18997
19170
  );
18998
19171
  }
18999
19172
  }
19000
- const resolveResult = importResolver.resolvePluginComponent(
19001
- pluginAttr,
19002
- contractAttr,
19003
- projectRoot
19004
- );
19005
- validations.push(...resolveResult.validations);
19006
- if (!resolveResult.val) {
19007
- continue;
19008
- }
19009
- const absoluteComponentPath = resolveResult.val.componentPath;
19010
- const name = resolveResult.val.componentName;
19011
- const isNpmPackage = resolveResult.val.isNpmPackage;
19012
- const packageName = resolveResult.val.packageName;
19173
+ let loadedContract;
19174
+ let contractFile;
19175
+ let contractMetadata = void 0;
19013
19176
  let module;
19014
- if (isNpmPackage && packageName) {
19015
- module = packageName;
19016
- } else {
19017
- module = path.relative(filePath, absoluteComponentPath);
19018
- if (!module.startsWith(".")) {
19019
- module = "./" + module;
19020
- }
19021
- }
19177
+ let name;
19178
+ let structural = false;
19179
+ let contractTagName = contractAttr;
19022
19180
  try {
19023
- const contractResult = importResolver.loadPluginContract(
19024
- pluginAttr,
19025
- contractAttr,
19026
- projectRoot
19027
- );
19028
- validations.push(...contractResult.validations);
19029
- if (!contractResult.val) {
19030
- continue;
19181
+ if (pluginAttr) {
19182
+ const resolveResult = importResolver.resolvePluginComponent(
19183
+ pluginAttr,
19184
+ contractAttr,
19185
+ projectRoot
19186
+ );
19187
+ validations.push(...resolveResult.validations);
19188
+ if (!resolveResult.val) {
19189
+ continue;
19190
+ }
19191
+ const absoluteComponentPath = resolveResult.val.componentPath;
19192
+ name = resolveResult.val.componentName;
19193
+ const isNpmPackage = resolveResult.val.isNpmPackage;
19194
+ const packageName = resolveResult.val.packageName;
19195
+ if (isNpmPackage && packageName) {
19196
+ module = packageName;
19197
+ } else {
19198
+ module = path.relative(filePath, absoluteComponentPath);
19199
+ if (!module.startsWith(".")) module = "./" + module;
19200
+ }
19201
+ const contractResult = importResolver.loadPluginContract(
19202
+ pluginAttr,
19203
+ contractAttr,
19204
+ projectRoot
19205
+ );
19206
+ validations.push(...contractResult.validations);
19207
+ if (!contractResult.val) {
19208
+ continue;
19209
+ }
19210
+ loadedContract = contractResult.val.contract;
19211
+ contractFile = contractResult.val.contractPath;
19212
+ contractMetadata = contractResult.val.metadata;
19213
+ } else {
19214
+ const contractPath = path.resolve(filePath, contractAttr);
19215
+ const contractResult = importResolver.loadContract(contractPath);
19216
+ validations.push(...contractResult.validations);
19217
+ if (!contractResult.val) {
19218
+ continue;
19219
+ }
19220
+ loadedContract = contractResult.val;
19221
+ contractFile = contractPath;
19222
+ if (srcAttr) {
19223
+ const importedFile = importResolver.resolveLink(filePath, srcAttr);
19224
+ const exportedComponents = importResolver.analyzeExportedTypes(importedFile, options).filter((t) => t instanceof JayComponentType);
19225
+ if (exportedComponents.length !== 1) {
19226
+ validations.push(
19227
+ `headless import src="${srcAttr}" must export exactly one Jay component (found ${exportedComponents.length})`
19228
+ );
19229
+ continue;
19230
+ }
19231
+ name = exportedComponents[0].name;
19232
+ module = path.relative(filePath, importedFile);
19233
+ if (!module.startsWith(".")) module = "./" + module;
19234
+ } else {
19235
+ structural = true;
19236
+ name = pascalCase(loadedContract.name);
19237
+ module = "";
19238
+ }
19239
+ contractTagName = paramCase(loadedContract.name);
19031
19240
  }
19032
- const loadedContract = contractResult.val.contract;
19033
- const contractFile = contractResult.val.contractPath;
19034
- const contractMetadata = contractResult.val.metadata;
19241
+ if (asAttr) contractTagName = asAttr;
19035
19242
  const contractTypes = await contractToImportsViewStateAndRefs(
19036
19243
  loadedContract,
19037
19244
  contractFile,
@@ -19048,13 +19255,17 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
19048
19255
  refsTypeName,
19049
19256
  repeatedRefsTypeName
19050
19257
  );
19258
+ const toRelativeModule = (to) => {
19259
+ const relative = path.relative(filePath, to);
19260
+ return relative.startsWith(".") ? relative : "./" + relative;
19261
+ };
19051
19262
  const enumsToImportRelativeToJayHtml = enumsToImport.map(
19052
19263
  (enumsToImport2) => ({
19053
19264
  type: enumsToImport2.type,
19054
- declaringModule: path.relative(filePath, enumsToImport2.declaringModule)
19265
+ declaringModule: toRelativeModule(enumsToImport2.declaringModule)
19055
19266
  })
19056
19267
  );
19057
- const relativeContractPath = path.relative(filePath, contractFile);
19268
+ const relativeContractPath = toRelativeModule(contractFile);
19058
19269
  const enumsFromContract = enumsToImportRelativeToJayHtml.filter((_) => _.declaringModule === relativeContractPath).map((_) => _.type);
19059
19270
  const nestedTypeNames = collectNestedTypeNames(type2);
19060
19271
  const nestedTypeImports = nestedTypeNames.filter((name2) => name2 !== type2.name).map((name2) => ({ name: name2, type: JayUnknown }));
@@ -19098,7 +19309,7 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
19098
19309
  };
19099
19310
  result.push({
19100
19311
  ...key && { key },
19101
- contractName: contractAttr,
19312
+ contractName: contractTagName,
19102
19313
  refs,
19103
19314
  rootType: type2,
19104
19315
  contractLinks,
@@ -19106,355 +19317,28 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
19106
19317
  contract: loadedContract,
19107
19318
  contractPath: contractFile,
19108
19319
  metadata: contractMetadata,
19109
- headlessProps
19320
+ headlessProps,
19321
+ ...structural && { structural },
19322
+ ...templateAttr && { template: templateAttr },
19323
+ ...suppressedValidations.length && { suppressedValidations }
19110
19324
  });
19111
19325
  });
19112
19326
  } catch (e2) {
19113
19327
  validations.push(`failed to parse linked contract - ${e2.message}${e2.stack}`);
19114
19328
  }
19115
19329
  }
19116
- return result;
19117
- }
19118
- function injectHeadfullFSTemplates(html2, sourceDir, importResolver) {
19119
- const root = parse_2(html2);
19120
- const allHeadfullElements = root.querySelectorAll('script[type="application/jay-headfull"]');
19121
- const fsElements = allHeadfullElements.filter((el) => el.getAttribute("contract"));
19122
- if (fsElements.length === 0) return html2;
19123
- const body = root.querySelector("body");
19124
- if (!body) return html2;
19125
- injectHeadfullFSTemplatesRecursive(fsElements, body, sourceDir, importResolver, /* @__PURE__ */ new Set());
19126
- return root.toString();
19127
- }
19128
- function injectHeadfullFSTemplatesRecursive(fsElements, body, sourceDir, importResolver, visited) {
19129
- for (const element of fsElements) {
19130
- const src = element.getAttribute("src");
19131
- const rawNames = element.getAttribute("names");
19132
- if (!src || !rawNames) continue;
19133
- const names = parseImportNames(rawNames);
19134
- if (names.length === 0) continue;
19135
- const contractName = (names[0].as || names[0].name).toLowerCase();
19136
- const resolvedSrc = path.resolve(sourceDir, src);
19137
- if (visited.has(resolvedSrc)) continue;
19138
- visited.add(resolvedSrc);
19139
- const jayHtmlResult = importResolver.readJayHtml(sourceDir, src);
19140
- if (!jayHtmlResult) continue;
19141
- const jayHtmlRoot = parse_2(jayHtmlResult.content);
19142
- const jayHtmlBody = jayHtmlRoot.querySelector("body");
19143
- if (!jayHtmlBody) continue;
19144
- const nestedHeadfullElements = jayHtmlRoot.querySelectorAll('script[type="application/jay-headfull"]').filter((el) => el.getAttribute("contract"));
19145
- if (nestedHeadfullElements.length > 0) {
19146
- const childDir = jayHtmlResult.componentDir;
19147
- injectHeadfullFSTemplatesRecursive(
19148
- nestedHeadfullElements,
19149
- jayHtmlBody,
19150
- childDir,
19151
- importResolver,
19152
- visited
19153
- );
19154
- }
19155
- const jayTags = body.querySelectorAll("*").filter((el) => el.tagName?.toLowerCase() === `jay:${contractName}`);
19156
- for (const jayTag of jayTags) {
19157
- if (!jayTag.innerHTML.trim()) {
19158
- jayTag.set_content(jayHtmlBody.innerHTML);
19159
- jayTag.setAttribute("style", "display: contents");
19160
- jayTag.setAttribute("jc", contractName);
19161
- }
19162
- }
19330
+ const tagCounts = /* @__PURE__ */ new Map();
19331
+ for (const imp of result) {
19332
+ tagCounts.set(imp.contractName, (tagCounts.get(imp.contractName) ?? 0) + 1);
19163
19333
  }
19164
- }
19165
- async function parseHeadfullFSImports(elements, validations, filePath, importResolver, body, projectRoot, visited = /* @__PURE__ */ new Set(), sourceDir, pageLinkedCssPaths) {
19166
- const headlessImports = [];
19167
- const cssParts = [];
19168
- const linkedCssFiles = [];
19169
- const linkedComponentFiles = [];
19170
- for (const element of elements) {
19171
- const src = element.getAttribute("src");
19172
- const contractAttr = element.getAttribute("contract");
19173
- const rawNames = element.getAttribute("names");
19174
- if (!src) {
19175
- validations.push("headfull FS import must specify src attribute");
19176
- continue;
19177
- }
19178
- if (!contractAttr) {
19179
- continue;
19180
- }
19181
- if (!rawNames) {
19182
- validations.push(`headfull FS import for module ${src} must specify names attribute`);
19183
- continue;
19184
- }
19185
- const names = parseImportNames(rawNames);
19186
- if (names.length === 0) {
19187
- validations.push(
19188
- `headfull FS import for module ${src} does not specify what to import`
19189
- );
19190
- continue;
19191
- }
19192
- const componentExportName = names[0].name;
19193
- const contractName = (names[0].as || componentExportName).toLowerCase();
19194
- const resolvedSrc = path.resolve(filePath, src);
19195
- if (visited.has(resolvedSrc)) {
19196
- validations.push(
19197
- `Circular headfull FS import detected: ${src} has already been processed`
19198
- );
19199
- continue;
19200
- }
19201
- visited.add(resolvedSrc);
19202
- const resolveDir = sourceDir || filePath;
19203
- let jayHtmlResult = importResolver.readJayHtml(resolveDir, src);
19204
- let moduleResolveDir = resolveDir;
19205
- if (jayHtmlResult === null && resolveDir !== filePath) {
19206
- jayHtmlResult = importResolver.readJayHtml(filePath, src);
19207
- if (jayHtmlResult !== null) {
19208
- moduleResolveDir = filePath;
19209
- }
19210
- }
19211
- if (jayHtmlResult === null && projectRoot && projectRoot !== filePath && projectRoot !== resolveDir) {
19212
- jayHtmlResult = importResolver.readJayHtml(projectRoot, src);
19213
- if (jayHtmlResult !== null) {
19214
- moduleResolveDir = projectRoot;
19215
- }
19216
- }
19217
- if (jayHtmlResult === null) {
19218
- validations.push(
19219
- `jay-html file not found for headfull FS component ${src} (expected ${src}.jay-html)`
19220
- );
19221
- continue;
19222
- }
19223
- linkedComponentFiles.push(jayHtmlResult.filePath);
19224
- let contractPath = path.resolve(resolveDir, contractAttr);
19225
- let loadedContract;
19226
- try {
19227
- const contractResult = importResolver.loadContract(contractPath);
19228
- validations.push(...contractResult.validations);
19229
- if (!contractResult.val) {
19230
- continue;
19231
- }
19232
- loadedContract = contractResult.val;
19233
- } catch (e2) {
19234
- let resolved = false;
19235
- if (projectRoot && projectRoot !== filePath) {
19236
- try {
19237
- contractPath = path.resolve(projectRoot, contractAttr);
19238
- const fallbackResult = importResolver.loadContract(contractPath);
19239
- validations.push(...fallbackResult.validations);
19240
- if (!fallbackResult.val) {
19241
- continue;
19242
- }
19243
- loadedContract = fallbackResult.val;
19244
- resolved = true;
19245
- } catch {
19246
- }
19247
- }
19248
- if (!resolved) {
19249
- try {
19250
- contractPath = path.resolve(
19251
- jayHtmlResult.componentDir,
19252
- path.basename(contractAttr)
19253
- );
19254
- const fallbackResult = importResolver.loadContract(contractPath);
19255
- validations.push(...fallbackResult.validations);
19256
- if (!fallbackResult.val) {
19257
- continue;
19258
- }
19259
- loadedContract = fallbackResult.val;
19260
- } catch (e3) {
19261
- validations.push(
19262
- `Failed to load contract for headfull FS component ${src}: ${e3.message}`
19263
- );
19264
- continue;
19265
- }
19266
- }
19267
- }
19268
- const jayHtmlRoot = parse_2(jayHtmlResult.content);
19269
- const jayHtmlBody = jayHtmlRoot.querySelector("body");
19270
- if (!jayHtmlBody) {
19271
- validations.push(`headfull FS component ${src} jay-html must have a body tag`);
19272
- continue;
19273
- }
19274
- const componentDir = jayHtmlResult.componentDir;
19275
- const componentCssResult = await extractCss(jayHtmlRoot, componentDir, pageLinkedCssPaths);
19276
- validations.push(...componentCssResult.validations);
19277
- if (componentCssResult.val?.css) {
19278
- cssParts.push(`/* Component: ${contractName} */
19279
- ${componentCssResult.val.css}`);
19280
- }
19281
- if (componentCssResult.val?.linkedCssFiles) {
19282
- linkedCssFiles.push(...componentCssResult.val.linkedCssFiles);
19283
- }
19284
- const nestedHeadlessElements = jayHtmlRoot.querySelectorAll(
19285
- 'script[type="application/jay-headless"]'
19286
- );
19287
- if (nestedHeadlessElements.length > 0) {
19288
- const nestedHeadless = await parseHeadlessImports(
19289
- nestedHeadlessElements,
19290
- validations,
19291
- filePath,
19292
- importResolver,
19293
- projectRoot
19294
- );
19295
- headlessImports.push(...nestedHeadless);
19296
- }
19297
- const nestedHeadfullFSElements = jayHtmlRoot.querySelectorAll('script[type="application/jay-headfull"]').filter((el) => el.getAttribute("contract"));
19298
- if (nestedHeadfullFSElements.length > 0) {
19299
- for (const nestedEl of nestedHeadfullFSElements) {
19300
- const nestedSrc = nestedEl.getAttribute("src");
19301
- if (nestedSrc) {
19302
- const absoluteSrc = path.resolve(componentDir, nestedSrc);
19303
- let relativeSrc = path.relative(filePath, absoluteSrc);
19304
- if (!relativeSrc.startsWith(".")) relativeSrc = "./" + relativeSrc;
19305
- nestedEl.setAttribute("src", relativeSrc);
19306
- }
19307
- const nestedContract = nestedEl.getAttribute("contract");
19308
- if (nestedContract) {
19309
- const absoluteContract = path.resolve(componentDir, nestedContract);
19310
- let relativeContract = path.relative(filePath, absoluteContract);
19311
- if (!relativeContract.startsWith("."))
19312
- relativeContract = "./" + relativeContract;
19313
- nestedEl.setAttribute("contract", relativeContract);
19314
- }
19315
- }
19316
- const nestedResult = await parseHeadfullFSImports(
19317
- nestedHeadfullFSElements,
19318
- validations,
19319
- filePath,
19320
- importResolver,
19321
- jayHtmlBody,
19322
- projectRoot,
19323
- visited,
19324
- void 0,
19325
- pageLinkedCssPaths
19326
- );
19327
- headlessImports.push(...nestedResult.headlessImports);
19328
- if (nestedResult.css) {
19329
- cssParts.push(nestedResult.css);
19330
- }
19331
- linkedCssFiles.push(...nestedResult.linkedCssFiles);
19332
- linkedComponentFiles.push(...nestedResult.linkedComponentFiles);
19333
- }
19334
- const jayTags = body.querySelectorAll("*").filter((el) => el.tagName?.toLowerCase() === `jay:${contractName}`);
19335
- for (const jayTag of jayTags) {
19336
- const existingContent = jayTag.innerHTML.trim();
19337
- if (existingContent) {
19338
- continue;
19339
- }
19340
- jayTag.set_content(jayHtmlBody.innerHTML);
19341
- jayTag.setAttribute("jc", contractName);
19342
- }
19343
- let resolvedSrcPath;
19344
- try {
19345
- resolvedSrcPath = importResolver.resolveLink(moduleResolveDir, src);
19346
- } catch {
19347
- resolvedSrcPath = path.resolve(moduleResolveDir, src);
19348
- }
19349
- const hasCodeFile = fs.existsSync(resolvedSrcPath + ".ts") || fs.existsSync(resolvedSrcPath + ".js") || fs.existsSync(path.join(resolvedSrcPath, "index.ts")) || fs.existsSync(path.join(resolvedSrcPath, "index.js"));
19350
- if (!hasCodeFile) {
19351
- for (const jayTag of jayTags) {
19352
- jayTag.replaceWith(jayTag.innerHTML);
19353
- }
19354
- continue;
19355
- }
19356
- try {
19357
- const contractTypes = await contractToImportsViewStateAndRefs(
19358
- loadedContract,
19359
- contractPath,
19360
- importResolver
19361
- );
19362
- contractTypes.map(({ type: type2, refs: subContractRefsTree, enumsToImport }) => {
19363
- const contractInternalName = loadedContract.name;
19364
- const refsTypeName = `${pascalCase(contractInternalName)}Refs`;
19365
- const repeatedRefsTypeName = `${pascalCase(contractInternalName)}RepeatedRefs`;
19366
- const refs = mkRefsTree(
19367
- subContractRefsTree.refs,
19368
- subContractRefsTree.children,
19369
- subContractRefsTree.repeated,
19370
- refsTypeName,
19371
- repeatedRefsTypeName
19372
- );
19373
- const relativeContractPath = path.relative(filePath, contractPath);
19374
- const enumsToImportRelativeToJayHtml = enumsToImport.map(
19375
- (enumToImport) => ({
19376
- type: enumToImport.type,
19377
- declaringModule: path.relative(filePath, enumToImport.declaringModule)
19378
- })
19379
- );
19380
- const enumsFromContract = enumsToImportRelativeToJayHtml.filter((_) => _.declaringModule === relativeContractPath).map((_) => _.type);
19381
- const nestedTypeNames = collectNestedTypeNames(type2);
19382
- const nestedTypeImports = nestedTypeNames.filter((name) => name !== type2.name).map((name) => ({ name, type: JayUnknown }));
19383
- const contractLink = {
19384
- module: relativeContractPath,
19385
- names: [
19386
- { name: type2.name, type: type2 },
19387
- { name: refsTypeName, type: JayUnknown },
19388
- ...nestedTypeImports,
19389
- ...enumsFromContract.map((_) => ({ name: _.name, type: _ }))
19390
- ]
19391
- };
19392
- const enumsFromOtherContracts = deduplicateEnums(
19393
- enumsToImportRelativeToJayHtml.filter(
19394
- (_) => _.declaringModule !== relativeContractPath
19395
- )
19396
- );
19397
- const enumImportLinks = Object.entries(
19398
- enumsFromOtherContracts.reduce(
19399
- (acc, enumToImport) => {
19400
- const module = enumToImport.declaringModule;
19401
- if (!acc[module]) {
19402
- acc[module] = [];
19403
- }
19404
- acc[module].push(enumToImport);
19405
- return acc;
19406
- },
19407
- {}
19408
- )
19409
- ).map(([module, enums]) => ({
19410
- module,
19411
- names: enums.map((enumToImport) => ({
19412
- name: enumToImport.type.name,
19413
- type: enumToImport.type
19414
- }))
19415
- }));
19416
- const contractLinks = [contractLink, ...enumImportLinks];
19417
- let resolvedModulePath;
19418
- try {
19419
- resolvedModulePath = importResolver.resolveLink(moduleResolveDir, src);
19420
- } catch {
19421
- resolvedModulePath = path.resolve(moduleResolveDir, src);
19422
- }
19423
- let relativeModule = path.relative(filePath, resolvedModulePath);
19424
- if (!relativeModule.startsWith(".")) {
19425
- relativeModule = "./" + relativeModule;
19426
- }
19427
- const codeLink = {
19428
- module: relativeModule,
19429
- names: [
19430
- {
19431
- name: componentExportName,
19432
- type: new JayComponentType(componentExportName, [])
19433
- }
19434
- ]
19435
- };
19436
- headlessImports.push({
19437
- contractName,
19438
- refs,
19439
- rootType: type2,
19440
- contractLinks,
19441
- codeLink,
19442
- contract: loadedContract,
19443
- contractPath
19444
- });
19445
- });
19446
- } catch (e2) {
19334
+ for (const [tag, count] of tagCounts) {
19335
+ if (count > 1) {
19447
19336
  validations.push(
19448
- `failed to parse contract for headfull FS component ${src} - ${e2.message}${e2.stack}`
19337
+ `Two headless imports back the same <jay:${tag}> region tag. Add as="<alias>" to one of them to flatten them as distinct design variants (e.g. as="feature-${tag}" → <jay:feature-${tag}>).`
19449
19338
  );
19450
19339
  }
19451
19340
  }
19452
- return {
19453
- headlessImports,
19454
- css: cssParts.length > 0 ? cssParts.join("\n\n") : void 0,
19455
- linkedCssFiles,
19456
- linkedComponentFiles
19457
- };
19341
+ return result;
19458
19342
  }
19459
19343
  function normalizeFilename(filename) {
19460
19344
  return filename.replace(".jay-html", "");
@@ -19588,7 +19472,7 @@ function resolveNestedCssImports(css, cssDir, visited) {
19588
19472
  if (seen.has(resolved)) return `/* circular import: ${importPath} */`;
19589
19473
  seen.add(resolved);
19590
19474
  try {
19591
- const imported = fs.readFileSync(resolved, "utf-8");
19475
+ const imported = fs$1.readFileSync(resolved, "utf-8");
19592
19476
  return `/* @import ${importPath} */
19593
19477
  ${resolveNestedCssImports(imported, path.dirname(resolved), seen)}`;
19594
19478
  } catch {
@@ -19610,9 +19494,8 @@ async function extractCss(root, filePath, skipPaths) {
19610
19494
  if (filePath) {
19611
19495
  const cssFilePath = path.resolve(filePath, href);
19612
19496
  linkedCssFiles.push(cssFilePath);
19613
- if (skipPaths?.has(cssFilePath)) continue;
19614
19497
  try {
19615
- const cssContent = await fs$1.readFile(cssFilePath, "utf-8");
19498
+ const cssContent = await fs.readFile(cssFilePath, "utf-8");
19616
19499
  const resolvedCss = resolveNestedCssImports(
19617
19500
  cssContent,
19618
19501
  path.dirname(cssFilePath)
@@ -19707,10 +19590,16 @@ async function parseJayFile(html2, filename, filePath, options, linkedContractRe
19707
19590
  const { val: jayYaml, validations } = parseYaml(root);
19708
19591
  if (validations.length > 0) return new WithValidations(void 0, validations);
19709
19592
  const allHeadfullElements = root.querySelectorAll('script[type="application/jay-headfull"]');
19593
+ for (const el of allHeadfullElements) {
19594
+ if (el.getAttribute("contract")) {
19595
+ validations.push(
19596
+ `application/jay-headfull with a contract attribute is no longer supported (DL#196). Declare the component with application/jay-headless and flatten it into a <jay:X> region (run \`jay-stack sync\`).`
19597
+ );
19598
+ }
19599
+ }
19710
19600
  const regularHeadfullElements = allHeadfullElements.filter(
19711
19601
  (el) => !el.getAttribute("contract")
19712
19602
  );
19713
- const fsHeadfullElements = allHeadfullElements.filter((el) => el.getAttribute("contract"));
19714
19603
  const headfullImports = parseHeadfullImports(
19715
19604
  regularHeadfullElements,
19716
19605
  validations,
@@ -19723,33 +19612,15 @@ async function parseJayFile(html2, filename, filePath, options, linkedContractRe
19723
19612
  validations.push(`jay file must have exactly a body tag`);
19724
19613
  return new WithValidations(void 0, validations);
19725
19614
  }
19726
- const pageLinkedCssPaths = /* @__PURE__ */ new Set();
19727
- for (const link of root.querySelectorAll('head link[rel="stylesheet"]')) {
19728
- const href = link.getAttribute("href");
19729
- if (href && !href.startsWith("http://") && !href.startsWith("https://") && !href.startsWith("//")) {
19730
- pageLinkedCssPaths.add(path.resolve(filePath, href));
19731
- }
19732
- }
19733
- const headfullFSResult = await parseHeadfullFSImports(
19734
- fsHeadfullElements,
19735
- validations,
19736
- filePath,
19737
- linkedContractResolver,
19738
- body,
19739
- projectRoot,
19740
- void 0,
19741
- // visited
19742
- sourceDir,
19743
- pageLinkedCssPaths
19744
- );
19745
19615
  const headlessImports = await parseHeadlessImports(
19746
19616
  root.querySelectorAll('script[type="application/jay-headless"]'),
19747
19617
  validations,
19748
19618
  filePath,
19749
19619
  linkedContractResolver,
19750
- projectRoot
19620
+ projectRoot,
19621
+ options
19751
19622
  );
19752
- const allHeadlessImports = [...headlessImports, ...headfullFSResult.headlessImports];
19623
+ const allHeadlessImports = [...headlessImports];
19753
19624
  resolveEnumCollisions(allHeadlessImports);
19754
19625
  const importNames = headfullImports.flatMap((_) => _.names);
19755
19626
  const types2 = await parseTypes(
@@ -19780,17 +19651,11 @@ async function parseJayFile(html2, filename, filePath, options, linkedContractRe
19780
19651
  validations.push(...cssResult.validations);
19781
19652
  if (validations.length > 0) return new WithValidations(void 0, validations);
19782
19653
  let css = cssResult.val?.css;
19783
- if (headfullFSResult.css) {
19784
- css = css ? css + "\n\n" + headfullFSResult.css : headfullFSResult.css;
19785
- }
19786
19654
  if (css) {
19787
19655
  detectCssNameCollisions(css, validations);
19788
19656
  }
19789
- let allLinkedCssFiles = cssResult.val?.linkedCssFiles || [];
19790
- if (headfullFSResult.linkedCssFiles.length > 0) {
19791
- allLinkedCssFiles = [...allLinkedCssFiles, ...headfullFSResult.linkedCssFiles];
19792
- }
19793
- const allLinkedComponentFiles = headfullFSResult.linkedComponentFiles;
19657
+ const allLinkedCssFiles = cssResult.val?.linkedCssFiles || [];
19658
+ const allLinkedComponentFiles = [];
19794
19659
  const { serverTrackByMap, clientTrackByMap } = extractTrackByMaps(
19795
19660
  jayYaml.parsedContract,
19796
19661
  allHeadlessImports
@@ -20117,7 +19982,15 @@ function generateRefsInterface(contract, refs) {
20117
19982
  ${repeatedRefsRendered}`;
20118
19983
  return { imports: imports.plus(imports2), renderedRefs };
20119
19984
  }
20120
- function generatePropsInterface(contractName, props) {
19985
+ function collectViewStateEnumNames(type2, acc = /* @__PURE__ */ new Set()) {
19986
+ if (isEnumType(type2)) acc.add(type2.name);
19987
+ else if (isObjectType$1(type2))
19988
+ for (const key of Object.keys(type2.props)) collectViewStateEnumNames(type2.props[key], acc);
19989
+ else if (isArrayType$1(type2) || isPromiseType(type2))
19990
+ collectViewStateEnumNames(type2.itemType, acc);
19991
+ return acc;
19992
+ }
19993
+ function generatePropsInterface(contractName, props, existingEnumNames = /* @__PURE__ */ new Set()) {
20121
19994
  const propsTypeName = `${contractName}Props`;
20122
19995
  const enums = [];
20123
19996
  if (props.length === 0) {
@@ -20129,10 +20002,13 @@ function generatePropsInterface(contractName, props) {
20129
20002
  let typeName;
20130
20003
  if (isEnumType(prop.dataType)) {
20131
20004
  typeName = prop.dataType.name;
20132
- const genEnum = `export enum ${prop.dataType.name} {
20005
+ if (!existingEnumNames.has(prop.dataType.name)) {
20006
+ const genEnum = `export enum ${prop.dataType.name} {
20133
20007
  ${prop.dataType.values.map((_) => " " + _).join(",\n")}
20134
20008
  }`;
20135
- enums.push(genEnum);
20009
+ enums.push(genEnum);
20010
+ existingEnumNames.add(prop.dataType.name);
20011
+ }
20136
20012
  } else if (isAtomicType(prop.dataType)) {
20137
20013
  typeName = prop.dataType.name;
20138
20014
  } else {
@@ -20198,9 +20074,11 @@ async function compileContract(contractWithValidations, contractFilePath, jayImp
20198
20074
  const propsTypeName = `${contractName}Props`;
20199
20075
  let renderedProps = "";
20200
20076
  if (hasProps) {
20077
+ const existingEnumNames = collectViewStateEnumNames(type2);
20201
20078
  const { propsInterface, propsEnums } = generatePropsInterface(
20202
20079
  contractName,
20203
- contract.props
20080
+ contract.props,
20081
+ existingEnumNames
20204
20082
  );
20205
20083
  renderedProps = propsEnums ? `${propsEnums}
20206
20084
 
@@ -20876,10 +20754,13 @@ ${indent.firstLine} }`;
20876
20754
  allImports = allImports.plus(Import.adoptElement);
20877
20755
  }
20878
20756
  const itemRootCoordForEach = element.getAttribute(COORD_ATTR) || "0";
20757
+ const bodyParentDepth = Math.max(itemContent.parentDepth, createChildren.parentDepth);
20758
+ const dependsOnParent = bodyParentDepth > 0;
20879
20759
  const hydrateForEachFragment = new RenderFragment(
20880
20760
  `${indent.firstLine}hydrateForEach(${forEachFragment.rendered}, '${trackBy}', '${itemRootCoordForEach}',
20881
20761
  ${indent.firstLine} ${adoptBody},
20882
- ${indent.firstLine} ${createBody},
20762
+ ${indent.firstLine} ${createBody},${dependsOnParent ? `
20763
+ ${indent.firstLine} true,` : ""}
20883
20764
  ${indent.firstLine})`,
20884
20765
  allImports,
20885
20766
  [
@@ -20887,7 +20768,9 @@ ${indent.firstLine})`,
20887
20768
  ...itemContent.validations,
20888
20769
  ...createChildren.validations
20889
20770
  ],
20890
- mergeRefsTrees(itemContent.refs, itemRefFragment.refs)
20771
+ mergeRefsTrees(itemContent.refs, itemRefFragment.refs),
20772
+ [],
20773
+ Math.max(forEachFragment.parentDepth, Math.max(0, bodyParentDepth - 1))
20891
20774
  );
20892
20775
  return nestRefs(forEachAccessor.terms, hydrateForEachFragment);
20893
20776
  }
@@ -20937,7 +20820,7 @@ function renderHydrateHeadlessInstance(element, context, renderContext, contract
20937
20820
  } else {
20938
20821
  coordinateKey2 = instanceCoord;
20939
20822
  }
20940
- const componentVariables = new Variables(headlessImport.rootType);
20823
+ const componentVariables = new Variables(headlessImport.rootType, renderContext.variables);
20941
20824
  const childNodes = filterContentNodes(element.childNodes);
20942
20825
  if (childNodes.length === 0) {
20943
20826
  return new RenderFragment("", Imports.none(), [
@@ -20987,14 +20870,50 @@ ${adoptChildren.rendered}
20987
20870
  ${adoptChildIndent.firstLine}])`,
20988
20871
  adoptChildren.imports.plus(Import.adoptElement),
20989
20872
  adoptChildren.validations,
20990
- adoptChildren.refs
20873
+ adoptChildren.refs,
20874
+ adoptChildren.recursiveRegions,
20875
+ // DL#193 Capability A: preserve the deepest `$parent` climb so the usage site
20876
+ // still weakens gates when instance content spans multiple root children.
20877
+ adoptChildren.parentDepth
20991
20878
  );
20992
20879
  }
20993
- const adoptMergedRefs = mergeContractStubRefs(adoptInlineBody.refs, headlessImport.refs);
20880
+ const contractRefNames = /* @__PURE__ */ new Set();
20881
+ const collectContractRefNames = (tree) => {
20882
+ for (const r of tree.refs) contractRefNames.add(camelCase$1(r.ref));
20883
+ for (const child of Object.values(tree.children)) collectContractRefNames(child);
20884
+ };
20885
+ collectContractRefNames(headlessImport.refs);
20886
+ const freeRefDecls = [];
20887
+ const seenFreeRefNames = /* @__PURE__ */ new Set();
20888
+ const collectFreeRefs = (tree) => {
20889
+ for (const r of tree.refs) {
20890
+ if (isComponentType$1(r.elementType)) continue;
20891
+ if (contractRefNames.has(camelCase$1(r.ref))) continue;
20892
+ if (seenFreeRefNames.has(r.ref)) continue;
20893
+ seenFreeRefNames.add(r.ref);
20894
+ freeRefDecls.push({
20895
+ name: r.ref,
20896
+ repeated: r.repeated,
20897
+ viewStateType: r.viewStateType.name,
20898
+ elementType: r.elementType.name
20899
+ });
20900
+ }
20901
+ for (const child of Object.values(tree.children)) collectFreeRefs(child);
20902
+ };
20903
+ collectFreeRefs(adoptInlineBody.refs);
20904
+ const freeRefNames = freeRefDecls.map((f) => f.name);
20905
+ const freeRefsArg = freeRefNames.length > 0 ? `
20906
+ [${freeRefNames.map((r) => `'${r}'`).join(", ")}],` : "";
20907
+ const adoptMergedRefs = hoistFreeRefs(
20908
+ mergeContractStubRefs(adoptInlineBody.refs, headlessImport.refs),
20909
+ new Set(freeRefNames)
20910
+ );
20994
20911
  const { renderedRefsManager, refsManagerImport } = renderReferenceManager(
20995
20912
  adoptMergedRefs,
20996
20913
  ReferenceManagerTarget.element
20997
20914
  );
20915
+ const refOriginalName = element.attributes.ref || context.refNameGenerator.newAutoRefNameGenerator();
20916
+ const refRefName = camelCase$1(refOriginalName);
20998
20917
  const adoptRenderFnCode = `
20999
20918
  // Hydrate inline template for headless component: ${contractName} #${idx}
21000
20919
  type ${elementType} = JayElement<${interactiveViewStateType}, ${refsTypeName}>;
@@ -21011,22 +20930,31 @@ ${adoptInlineBody.rendered}
21011
20930
  }`;
21012
20931
  let adoptComponentSymbol;
21013
20932
  let adoptComponentDef;
21014
- if (isInsideForEach) {
20933
+ const coordinateArg = isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`;
20934
+ if (headlessImport.structural) {
20935
+ adoptComponentSymbol = `_Headless${pascal}${idx}`;
20936
+ adoptComponentDef = `const ${adoptComponentSymbol} = makePassthroughHeadlessInstanceComponent(
20937
+ ${renderFnName},
20938
+ ${coordinateArg},${freeRefsArg}
20939
+ );`;
20940
+ } else if (isInsideForEach) {
21015
20941
  adoptComponentSymbol = `_Headless${pascal}${idx}Adopt`;
21016
20942
  adoptComponentDef = `const ${adoptComponentSymbol} = makeHeadlessInstanceComponent(
21017
20943
  ${renderFnName},
21018
20944
  ${pluginComponentName},
21019
- (dataIds) => [...dataIds, '${coordinateSuffix}'].toString(),
20945
+ (dataIds) => [...dataIds, '${coordinateSuffix}'].toString(),${freeRefsArg}
21020
20946
  );`;
21021
20947
  } else {
21022
20948
  adoptComponentSymbol = `_Headless${pascal}${idx}`;
21023
20949
  adoptComponentDef = `const ${adoptComponentSymbol} = makeHeadlessInstanceComponent(
21024
20950
  ${renderFnName},
21025
20951
  ${pluginComponentName},
21026
- '${coordinateKey2}',
20952
+ '${coordinateKey2}',${freeRefsArg}
21027
20953
  );`;
21028
20954
  }
21029
- let adoptImports = adoptInlineBody.imports.plus(refsManagerImport).plus(Import.ConstructContext).plus(Import.makeHeadlessInstanceComponent);
20955
+ let adoptImports = adoptInlineBody.imports.plus(refsManagerImport).plus(Import.ConstructContext).plus(
20956
+ headlessImport.structural ? Import.makePassthroughHeadlessInstanceComponent : Import.makeHeadlessInstanceComponent
20957
+ );
21030
20958
  const ifCondition = element.attributes.if;
21031
20959
  const needsCreateVersion = !!ifCondition;
21032
20960
  let createComponentSymbol;
@@ -21067,7 +20995,10 @@ ${createRenderedChildren.rendered}
21067
20995
  } else {
21068
20996
  createInlineBody = createRenderedChildren;
21069
20997
  }
21070
- const createMergedRefs = mergeContractStubRefs(createInlineBody.refs, headlessImport.refs);
20998
+ const createMergedRefs = hoistFreeRefs(
20999
+ mergeContractStubRefs(createInlineBody.refs, headlessImport.refs),
21000
+ new Set(freeRefNames)
21001
+ );
21071
21002
  const { renderedRefsManager: createRefsManager, refsManagerImport: createRefsImport } = renderReferenceManager(createMergedRefs, ReferenceManagerTarget.element);
21072
21003
  createRenderFnCode = `
21073
21004
  function ${createRenderFnName}(options?: RenderElementOptions): ${preRenderType} {
@@ -21079,10 +21010,10 @@ ${createInlineBody.rendered}
21079
21010
  return [refManager.getPublicAPI() as ${refsTypeName}, render];
21080
21011
  }
21081
21012
 
21082
- const ${createComponentSymbol} = makeHeadlessInstanceComponent(
21083
- ${createRenderFnName},
21084
- ${pluginComponentName},
21085
- ${isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`},
21013
+ const ${createComponentSymbol} = ${headlessImport.structural ? "makePassthroughHeadlessInstanceComponent" : "makeHeadlessInstanceComponent"}(
21014
+ ${createRenderFnName},${headlessImport.structural ? "" : `
21015
+ ${pluginComponentName},`}
21016
+ ${isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`},${freeRefsArg}
21086
21017
  );`;
21087
21018
  createImports = createInlineBody.imports.plus(createRefsImport);
21088
21019
  }
@@ -21090,7 +21021,7 @@ const ${createComponentSymbol} = makeHeadlessInstanceComponent(
21090
21021
  componentSymbol: adoptComponentSymbol,
21091
21022
  renderFnName,
21092
21023
  renderFnCode: adoptRenderFnCode + "\n" + adoptComponentDef + (createRenderFnCode ? "\n" + createRenderFnCode : ""),
21093
- pluginComponentName,
21024
+ pluginComponentName: headlessImport.structural ? void 0 : pluginComponentName,
21094
21025
  imports: adoptImports.plus(createImports)
21095
21026
  });
21096
21027
  const propsGetterAndRefs = renderChildCompProps$1(
@@ -21099,8 +21030,6 @@ const ${createComponentSymbol} = makeHeadlessInstanceComponent(
21099
21030
  headlessImport.contract?.props
21100
21031
  );
21101
21032
  const getProps = `(${context.variables.currentVar}: ${context.variables.currentType.name}) => ${propsGetterAndRefs.rendered}`;
21102
- const refOriginalName = element.attributes.ref || context.refNameGenerator.newAutoRefNameGenerator();
21103
- const refRefName = camelCase$1(refOriginalName);
21104
21033
  const refConstName = context.refNameGenerator.newConstantName(refRefName, context.variables);
21105
21034
  const isRepeated = context.dynamicRef;
21106
21035
  const contractRefType = isRepeated ? `${pascal}RepeatedRefs` : `${pascal}Refs`;
@@ -21109,6 +21038,22 @@ const ${createComponentSymbol} = makeHeadlessInstanceComponent(
21109
21038
  link.names.push({ name: contractRefType, type: JayUnknown });
21110
21039
  }
21111
21040
  }
21041
+ const augmented = freeRefsAugmentedRefType(contractRefType, freeRefDecls, isRepeated);
21042
+ if (freeRefDecls.length > 0) {
21043
+ for (const f of freeRefDecls) {
21044
+ if (!f.viewStateType) continue;
21045
+ for (const link of headlessImport.contractLinks) {
21046
+ if (!link.names.some((n) => n.name === f.viewStateType)) {
21047
+ link.names.push({ name: f.viewStateType, type: JayUnknown });
21048
+ }
21049
+ }
21050
+ }
21051
+ }
21052
+ let freeRefProxyImports = Imports.for();
21053
+ if (augmented.needsElementProxy)
21054
+ freeRefProxyImports = freeRefProxyImports.plus(Import.HTMLElementProxy);
21055
+ if (augmented.needsCollectionProxy)
21056
+ freeRefProxyImports = freeRefProxyImports.plus(Import.HTMLElementCollectionProxy);
21112
21057
  const instanceRef = mkRef(
21113
21058
  refRefName,
21114
21059
  refOriginalName,
@@ -21116,25 +21061,25 @@ const ${createComponentSymbol} = makeHeadlessInstanceComponent(
21116
21061
  isRepeated,
21117
21062
  !element.attributes.ref,
21118
21063
  context.variables.currentType,
21119
- new JayTypeAlias(contractRefType)
21120
- );
21121
- let renderedRef = new RenderFragment(
21122
- `${refConstName}()`,
21123
- Imports.for(),
21124
- [],
21125
- mkRefsTree([instanceRef], {})
21064
+ new JayTypeAlias(augmented.type),
21065
+ // DL#198 — declare free refs on the page-side region ref so the page-level renderReferenceManager
21066
+ // emits the per-region FreeReferenceManager (`${refConstName}FreeRefManager`) + setFreeRefManager.
21067
+ freeRefDecls.length > 0 ? freeRefDecls : void 0
21126
21068
  );
21069
+ const instanceRefsTree = mkRefsTree([instanceRef], {});
21070
+ let renderedRef = new RenderFragment(`${refConstName}()`, Imports.for(), [], instanceRefsTree);
21127
21071
  if (renderedRef.rendered !== "") renderedRef = renderedRef.map((_) => ", " + _);
21072
+ const freeRefManagerArg = freeRefNames.length > 0 ? `, ${refConstName}FreeRefManager` : "";
21128
21073
  const scopeRootCoord = childScopeId ? `'${childScopeId}/0'` : "undefined";
21129
21074
  if (ifCondition) {
21130
21075
  const renderedCondition = parseCondition(ifCondition, context.variables);
21131
- const adoptCall = `() => childCompHydrate(${adoptComponentSymbol}, ${getProps}, ${scopeRootCoord}${renderedRef.rendered})`;
21132
- const createCall = `() => childComp(${createComponentSymbol}, ${getProps}${renderedRef.rendered})`;
21076
+ const adoptCall = `() => childCompHydrate(${adoptComponentSymbol}, ${getProps}, ${scopeRootCoord}${renderedRef.rendered}${freeRefManagerArg})`;
21077
+ const createCall = `() => childComp(${createComponentSymbol}, ${getProps}${renderedRef.rendered}${freeRefManagerArg})`;
21133
21078
  const callExpr = `${context.indent.firstLine}hydrateConditional(${renderedCondition.rendered}, ${adoptCall},
21134
21079
  ${context.indent.firstLine} ${createCall})`;
21135
21080
  return new RenderFragment(
21136
21081
  callExpr,
21137
- Imports.for(Import.childCompHydrate, Import.hydrateConditional, Import.childComp).plus(propsGetterAndRefs.imports).plus(renderedRef.imports).plus(renderedCondition.imports),
21082
+ Imports.for(Import.childCompHydrate, Import.hydrateConditional, Import.childComp).plus(propsGetterAndRefs.imports).plus(renderedRef.imports).plus(renderedCondition.imports).plus(freeRefProxyImports),
21138
21083
  [
21139
21084
  ...propsGetterAndRefs.validations,
21140
21085
  ...adoptInlineBody.validations,
@@ -21144,8 +21089,8 @@ ${context.indent.firstLine} ${createCall})`;
21144
21089
  );
21145
21090
  }
21146
21091
  return new RenderFragment(
21147
- `${context.indent.firstLine}childCompHydrate(${adoptComponentSymbol}, ${getProps}, ${scopeRootCoord}${renderedRef.rendered})`,
21148
- Imports.for(Import.childCompHydrate).plus(propsGetterAndRefs.imports).plus(renderedRef.imports),
21092
+ `${context.indent.firstLine}childCompHydrate(${adoptComponentSymbol}, ${getProps}, ${scopeRootCoord}${renderedRef.rendered}${freeRefManagerArg})`,
21093
+ Imports.for(Import.childCompHydrate).plus(propsGetterAndRefs.imports).plus(renderedRef.imports).plus(freeRefProxyImports),
21149
21094
  [
21150
21095
  ...propsGetterAndRefs.validations,
21151
21096
  ...adoptInlineBody.validations,
@@ -21221,6 +21166,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21221
21166
  let childImports = Imports.none();
21222
21167
  const childValidations = [];
21223
21168
  const childRefs = [];
21169
+ let childParentDepth = 0;
21224
21170
  for (const child of childNodes) {
21225
21171
  if (child.nodeType !== NodeType.ELEMENT_NODE) continue;
21226
21172
  const htmlChild = child;
@@ -21234,6 +21180,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21234
21180
  childImports = childImports.plus(frag.imports);
21235
21181
  childValidations.push(...frag.validations);
21236
21182
  if (frag.refs) childRefs.push(frag.refs);
21183
+ childParentDepth = Math.max(childParentDepth, frag.parentDepth);
21237
21184
  }
21238
21185
  } else {
21239
21186
  const htmlChild2 = child;
@@ -21256,6 +21203,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21256
21203
  childImports = childImports.plus(frag.imports);
21257
21204
  childValidations.push(...frag.validations);
21258
21205
  if (frag.refs) childRefs.push(frag.refs);
21206
+ childParentDepth = Math.max(childParentDepth, frag.parentDepth);
21259
21207
  } else {
21260
21208
  childParts.push(`${indent.firstLine}STATIC`);
21261
21209
  childImports = childImports.plus(Import.STATIC);
@@ -21263,12 +21211,16 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21263
21211
  }
21264
21212
  }
21265
21213
  const refSuffix2 = renderedRef.rendered ? `, ${renderedRef.rendered}` : "";
21266
- const childrenArr2 = childParts.length ? `[${childParts.join(",\n")}]` : "[]";
21214
+ const childrenArr2 = childParts.length ? `[
21215
+ ${childParts.join(",\n")}
21216
+ ${indent.firstLine}]` : "[]";
21267
21217
  return new RenderFragment(
21268
21218
  `${indent.firstLine}adoptDynamicElement("${coordinate}", ${attributes2.rendered}, ${childrenArr2}${refSuffix2})`,
21269
21219
  Imports.for(Import.adoptDynamicElement).plus(attributes2.imports).plus(childImports).plus(renderedRef.imports),
21270
21220
  [...attributes2.validations, ...childValidations, ...renderedRef.validations],
21271
- mergeRefsTrees(...childRefs, renderedRef.refs)
21221
+ mergeRefsTrees(...childRefs, renderedRef.refs),
21222
+ [],
21223
+ Math.max(childParentDepth, attributes2.parentDepth)
21272
21224
  );
21273
21225
  }
21274
21226
  if (hasMixedContentDynamicText2 && !hasDynamicAttrs) {
@@ -21276,6 +21228,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21276
21228
  let childImports = Imports.none();
21277
21229
  const childValidations = [];
21278
21230
  const childRefs = [];
21231
+ let childParentDepth = 0;
21279
21232
  childNodes.forEach((child) => {
21280
21233
  if (child.nodeType === NodeType.ELEMENT_NODE) {
21281
21234
  const frag = renderHydrateNode(child, context);
@@ -21284,6 +21237,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21284
21237
  childImports = childImports.plus(frag.imports);
21285
21238
  childValidations.push(...frag.validations);
21286
21239
  if (frag.refs) childRefs.push(frag.refs);
21240
+ childParentDepth = Math.max(childParentDepth, frag.parentDepth);
21287
21241
  }
21288
21242
  }
21289
21243
  });
@@ -21299,6 +21253,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21299
21253
  );
21300
21254
  childImports = childImports.plus(Import.adoptText).plus(rendered.imports.minus(Import.dynamicText));
21301
21255
  childValidations.push(...rendered.validations);
21256
+ childParentDepth = Math.max(childParentDepth, rendered.parentDepth);
21302
21257
  }
21303
21258
  }
21304
21259
  }
@@ -21309,7 +21264,9 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21309
21264
  `${indent.firstLine}adoptElement("${coordinate}", ${attributes2.rendered}, ${childrenArr2}${refSuffix2})`,
21310
21265
  Imports.for(Import.adoptElement).plus(attributes2.imports).plus(childImports).plus(renderedRef.imports),
21311
21266
  [...attributes2.validations, ...childValidations, ...renderedRef.validations],
21312
- mergeRefsTrees(...childRefs, renderedRef.refs)
21267
+ mergeRefsTrees(...childRefs, renderedRef.refs),
21268
+ [],
21269
+ childParentDepth
21313
21270
  );
21314
21271
  }
21315
21272
  if (isHtmlStringChild && textFragment && !hasDynamicAttrs) {
@@ -21319,7 +21276,9 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21319
21276
  `${indent.firstLine}adoptElement("${coordinate}", ${attributes2.rendered}, [dh(${accessor})]${refSuffix2})`,
21320
21277
  Imports.for(Import.adoptElement).plus(Import.dynamicHtml).plus(textFragment.imports.minus(Import.dynamicText)).plus(renderedRef.imports).plus(attributes2.imports),
21321
21278
  [...textFragment.validations, ...renderedRef.validations, ...attributes2.validations],
21322
- renderedRef.refs
21279
+ renderedRef.refs,
21280
+ [],
21281
+ textFragment.parentDepth
21323
21282
  );
21324
21283
  }
21325
21284
  if (textFragment && !hasDynamicAttrs) {
@@ -21334,13 +21293,19 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21334
21293
  ...renderedRef.validations,
21335
21294
  ...attributes2.validations
21336
21295
  ],
21337
- renderedRef.refs
21296
+ renderedRef.refs,
21297
+ [],
21298
+ // DL#193 Capability A: carry the `$parent` climb up so enclosing forEach gates weaken.
21299
+ textFragment.parentDepth
21338
21300
  );
21339
21301
  }
21340
21302
  return new RenderFragment(
21341
21303
  `${indent.firstLine}adoptText("${coordinate}", ${accessor})`,
21342
21304
  Imports.for(Import.adoptText).plus(textFragment.imports.minus(Import.dynamicText)),
21343
- [...textFragment.validations]
21305
+ [...textFragment.validations],
21306
+ mkRefsTree([], {}),
21307
+ [],
21308
+ textFragment.parentDepth
21344
21309
  );
21345
21310
  }
21346
21311
  if (hasDynamicAttrs) {
@@ -21348,6 +21313,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21348
21313
  let childImports = Imports.none();
21349
21314
  let childValidations = [];
21350
21315
  let childRefs;
21316
+ let childParentDepth = 0;
21351
21317
  if (textFragment) {
21352
21318
  const accessor = textFragment.rendered.replace(/^dt\(/, "").replace(/\)$/, "");
21353
21319
  childrenRendered = `[adoptText("${coordinate}", ${accessor})]`;
@@ -21355,6 +21321,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21355
21321
  textFragment.imports.minus(Import.dynamicText)
21356
21322
  );
21357
21323
  childValidations = textFragment.validations;
21324
+ childParentDepth = textFragment.parentDepth;
21358
21325
  } else {
21359
21326
  const children = mergeHydrateFragments(
21360
21327
  childNodes.map((child) => renderHydrateNode(child, context)),
@@ -21365,6 +21332,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21365
21332
  childImports = children.imports;
21366
21333
  childValidations = children.validations;
21367
21334
  childRefs = children.refs;
21335
+ childParentDepth = children.parentDepth;
21368
21336
  }
21369
21337
  }
21370
21338
  const refSuffix2 = renderedRef.rendered ? `, ${renderedRef.rendered}` : "";
@@ -21372,7 +21340,11 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21372
21340
  `${indent.firstLine}adoptElement("${coordinate}", ${attributes2.rendered}, ${childrenRendered}${refSuffix2})`,
21373
21341
  Imports.for(Import.adoptElement).plus(attributes2.imports).plus(childImports).plus(renderedRef.imports),
21374
21342
  [...attributes2.validations, ...childValidations, ...renderedRef.validations],
21375
- mergeRefsTrees(...[childRefs, renderedRef.refs].filter(Boolean))
21343
+ mergeRefsTrees(...[childRefs, renderedRef.refs].filter(Boolean)),
21344
+ [],
21345
+ // DL#193 Phase 2a: carry the override binding's `$parent` climb up (attributes may
21346
+ // themselves be `$parent`-bound; a wrapping element adds no scope).
21347
+ Math.max(childParentDepth, attributes2.parentDepth)
21376
21348
  );
21377
21349
  }
21378
21350
  const childFragments = mergeHydrateFragments(
@@ -21385,7 +21357,11 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21385
21357
  `${indent.firstLine}adoptElement("${coordinate}", {}, ${childrenArr}${refSuffix})`,
21386
21358
  Imports.for(Import.adoptElement).plus(childFragments.imports).plus(renderedRef.imports),
21387
21359
  [...childFragments.validations, ...renderedRef.validations],
21388
- mergeRefsTrees(childFragments.refs, renderedRef.refs)
21360
+ mergeRefsTrees(childFragments.refs, renderedRef.refs),
21361
+ [],
21362
+ // DL#193 Capability A: a wrapping element adds no scope, so carry children's `$parent`
21363
+ // climb up unchanged so enclosing forEach gates weaken.
21364
+ childFragments.parentDepth
21389
21365
  );
21390
21366
  }
21391
21367
  function renderHydrate(types2, body, importStatements, elementType, preRenderType, refsType, headlessImports, contract) {
@@ -21411,7 +21387,9 @@ function renderHydrate(types2, body, importStatements, elementType, preRenderTyp
21411
21387
  };
21412
21388
  const rootElement = ensureSingleChildElement(body);
21413
21389
  if (!rootElement.val) {
21414
- return new RenderFragment("", Imports.none(), rootElement.validations);
21390
+ return {
21391
+ fragment: new RenderFragment("", Imports.none(), rootElement.validations)
21392
+ };
21415
21393
  }
21416
21394
  const rootComponentMatch = getComponentName(
21417
21395
  rootElement.val.rawTagName,
@@ -21437,8 +21415,8 @@ function renderHydrate(types2, body, importStatements, elementType, preRenderTyp
21437
21415
  ReferenceManagerTarget.element
21438
21416
  );
21439
21417
  const hasAdoptCalls = renderedHydrate.rendered.trim().length > 0;
21440
- const hydrateBody = hasAdoptCalls ? `() =>
21441
- ${renderedHydrate.rendered}` : `() => ({ dom: rootElement, update: () => {}, mount: () => {}, unmount: () => {} })`;
21418
+ const hydrateBody = !hasAdoptCalls ? `() => ({ dom: rootElement, update: () => {}, mount: () => {}, unmount: () => {} })` : `() =>
21419
+ ${renderedHydrate.rendered}`;
21442
21420
  const headlessDefsCode = context.headlessInstanceDefs.map((def) => def.renderFnCode).join("\n");
21443
21421
  let headlessImportsAll = Imports.none();
21444
21422
  for (const def of context.headlessInstanceDefs) {
@@ -21453,12 +21431,14 @@ ${renderedRefsManager}
21453
21431
  const fullOutput = headlessDefsCode ? `${headlessDefsCode}
21454
21432
 
21455
21433
  ${hydrateFunction}` : hydrateFunction;
21456
- return new RenderFragment(
21457
- fullOutput,
21458
- Imports.for(Import.ConstructContext, Import.RenderElementOptions).plus(renderedHydrate.imports).plus(refsManagerImport).plus(headlessImportsAll),
21459
- renderedHydrate.validations,
21460
- renderedHydrate.refs
21461
- );
21434
+ return {
21435
+ fragment: new RenderFragment(
21436
+ fullOutput,
21437
+ Imports.for(Import.ConstructContext, Import.RenderElementOptions).plus(renderedHydrate.imports).plus(refsManagerImport).plus(headlessImportsAll),
21438
+ renderedHydrate.validations,
21439
+ renderedHydrate.refs
21440
+ )
21441
+ };
21462
21442
  }
21463
21443
  function renderFunctionDeclaration(preRenderType) {
21464
21444
  return `export declare function render(options?: RenderElementOptions): ${preRenderType}`;
@@ -21549,7 +21529,7 @@ function renderDynamicAttributes(element, { variables }) {
21549
21529
  if (isDirectiveAttribute(attrCanonical)) return;
21550
21530
  if (attrCanonical === "style") {
21551
21531
  const styleFragment = renderStyleAttribute(attributes2[attrName], variables);
21552
- if (styleFragment.imports.has(Import.dynamicAttribute)) {
21532
+ if (styleFragment.imports.has(Import.dynamicAttribute) || styleFragment.imports.has(Import.dynamicProperty)) {
21553
21533
  renderedAttributes.push(styleFragment);
21554
21534
  }
21555
21535
  } else if (attrCanonical === "class") {
@@ -21640,6 +21620,21 @@ function renderElementRef$1(element, { dynamicRef, variables, importedRefNameToR
21640
21620
  return new RenderFragment(`${constName}()`, Imports.none(), [], refs);
21641
21621
  } else return RenderFragment.empty();
21642
21622
  }
21623
+ function coerceStaticComponentProp(value, expectedType) {
21624
+ if (!expectedType) return void 0;
21625
+ if (isEnumType(expectedType)) {
21626
+ return expectedType.values.includes(value) ? `${expectedType.alias ?? expectedType.name}.${value}` : void 0;
21627
+ }
21628
+ if (expectedType.name === "number") {
21629
+ return /^-?\d+(\.\d+)?$/.test(value) ? value : void 0;
21630
+ }
21631
+ if (expectedType.name === "boolean") {
21632
+ if (value === "true") return "true";
21633
+ if (value === "false") return "false";
21634
+ return void 0;
21635
+ }
21636
+ return void 0;
21637
+ }
21643
21638
  function renderChildCompProps$1(element, { variables }, contractProps) {
21644
21639
  let attributes2 = element.attributes;
21645
21640
  let props = [];
@@ -21657,10 +21652,15 @@ function renderChildCompProps$1(element, { variables }, contractProps) {
21657
21652
  if (attrCanonical === "ref") {
21658
21653
  return;
21659
21654
  } else {
21660
- let prop = parseComponentPropExpression(attributes2[attrName], variables);
21655
+ const rawValue = attributes2[attrName];
21656
+ const isStatic = !rawValue.includes("{");
21657
+ let prop = parseComponentPropExpression(rawValue, variables);
21661
21658
  const outputKey = contractProps?.find((p) => p.name.toLowerCase() === attrCanonical)?.name ?? attrKey;
21662
21659
  const expectedType = propTypeMap?.get(attrName) ?? propTypeMap?.get(outputKey);
21663
- if (expectedType && expectedType.name === "string" && /^\d+$/.test(prop.rendered)) {
21660
+ const coerced = isStatic ? coerceStaticComponentProp(rawValue.trim(), expectedType) : void 0;
21661
+ if (coerced !== void 0) {
21662
+ prop = prop.map(() => coerced);
21663
+ } else if (expectedType && expectedType.name === "string" && /^\d+$/.test(prop.rendered)) {
21664
21664
  prop = prop.map((_) => `'${_}'`);
21665
21665
  }
21666
21666
  props.push(prop.map((_) => `${outputKey}: ${_}`));
@@ -21733,7 +21733,9 @@ function renderNode(node2, context) {
21733
21733
  children.imports.plus(Import.dynamicElement).plus(attributes2.imports).plus(ref.imports).plus(tagFunc.import),
21734
21734
  [...attributes2.validations, ...children.validations, ...ref.validations],
21735
21735
  mergeRefsTrees(attributes2.refs, children.refs, ref.refs),
21736
- [...attributes2.recursiveRegions, ...children.recursiveRegions, ...ref.recursiveRegions]
21736
+ [...attributes2.recursiveRegions, ...children.recursiveRegions, ...ref.recursiveRegions],
21737
+ // DL#193: carry the deepest `$parent` climb up so scope-switch rules can force it.
21738
+ Math.max(attributes2.parentDepth, children.parentDepth, ref.parentDepth)
21737
21739
  );
21738
21740
  }
21739
21741
  function e2(tagName, attributes2, children, ref, currIndent = indent) {
@@ -21744,7 +21746,9 @@ function renderNode(node2, context) {
21744
21746
  children.imports.plus(Import.element).plus(attributes2.imports).plus(ref.imports).plus(tagFunc.import),
21745
21747
  [...attributes2.validations, ...children.validations, ...ref.validations],
21746
21748
  mergeRefsTrees(attributes2.refs, children.refs, ref.refs),
21747
- [...attributes2.recursiveRegions, ...children.recursiveRegions, ...ref.recursiveRegions]
21749
+ [...attributes2.recursiveRegions, ...children.recursiveRegions, ...ref.recursiveRegions],
21750
+ // DL#193: carry the deepest `$parent` climb up so scope-switch rules can force it.
21751
+ Math.max(attributes2.parentDepth, children.parentDepth, ref.parentDepth)
21748
21752
  );
21749
21753
  }
21750
21754
  function renderHtmlElement(htmlElement, newContext) {
@@ -21857,7 +21861,8 @@ ${contextForChildren.indent.firstLine}` : children
21857
21861
  result.imports.plus(Import.baseJayElement),
21858
21862
  result.validations,
21859
21863
  result.refs,
21860
- [...result.recursiveRegions, recursiveRegion]
21864
+ [...result.recursiveRegions, recursiveRegion],
21865
+ result.parentDepth
21861
21866
  );
21862
21867
  }
21863
21868
  return result;
@@ -21870,17 +21875,26 @@ ${indent.firstLine})`,
21870
21875
  Imports.merge(childElement.imports, renderedCondition.imports).plus(Import.conditional),
21871
21876
  [...renderedCondition.validations, ...childElement.validations],
21872
21877
  mergeRefsTrees(renderedCondition.refs, childElement.refs),
21873
- [...renderedCondition.recursiveRegions, ...childElement.recursiveRegions]
21878
+ [...renderedCondition.recursiveRegions, ...childElement.recursiveRegions],
21879
+ // DL#193: `if` is not a data-scope switch — it re-runs its body on every parent
21880
+ // update at runtime, so the residual `$parent` depth passes through unchanged.
21881
+ Math.max(renderedCondition.parentDepth, childElement.parentDepth)
21874
21882
  );
21875
21883
  }
21876
21884
  function renderForEach(renderedForEach, collectionVariables, trackBy, childElement) {
21885
+ const dependsOnParent = childElement.parentDepth > 0;
21886
+ const residualParentDepth = Math.max(
21887
+ renderedForEach.parentDepth,
21888
+ Math.max(0, childElement.parentDepth - 1)
21889
+ );
21877
21890
  return new RenderFragment(
21878
21891
  `${indent.firstLine}forEach(${renderedForEach.rendered}, (${collectionVariables.currentVar}: ${collectionVariables.currentType.name}) => {
21879
- ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
21892
+ ${indent.curr}return ${childElement.rendered}}, '${trackBy}'${dependsOnParent ? ", true" : ""})`,
21880
21893
  childElement.imports.plus(Import.forEach),
21881
21894
  [...renderedForEach.validations, ...childElement.validations],
21882
21895
  childElement.refs,
21883
- [...renderedForEach.recursiveRegions, ...childElement.recursiveRegions]
21896
+ [...renderedForEach.recursiveRegions, ...childElement.recursiveRegions],
21897
+ residualParentDepth
21884
21898
  );
21885
21899
  }
21886
21900
  function renderAsync(asyncType, getPromiseFragment, childElement, resolvedGenericTypes) {
@@ -21889,7 +21903,10 @@ ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
21889
21903
  childElement.imports.plus(asyncType.import),
21890
21904
  [...getPromiseFragment.validations, ...childElement.validations],
21891
21905
  childElement.refs,
21892
- [...getPromiseFragment.recursiveRegions, ...childElement.recursiveRegions]
21906
+ [...getPromiseFragment.recursiveRegions, ...childElement.recursiveRegions],
21907
+ // DL#193: `when`/async is a data-scope switch; it re-runs its body on every parent
21908
+ // update at runtime (no flag needed), and the residual `$parent` depth drops by one.
21909
+ Math.max(getPromiseFragment.parentDepth, Math.max(0, childElement.parentDepth - 1))
21893
21910
  );
21894
21911
  }
21895
21912
  function renderNestedComponent(htmlElement, newContext, componentName) {
@@ -21932,7 +21949,7 @@ ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
21932
21949
  link.names.push({ name: interactiveViewStateType, type: JayUnknown });
21933
21950
  }
21934
21951
  }
21935
- const componentVariables = new Variables(headlessImport.rootType);
21952
+ const componentVariables = new Variables(headlessImport.rootType, newContext.variables);
21936
21953
  const childIndent = newContext.indent.child(false);
21937
21954
  const childNodes = filterContentNodes(htmlElement.childNodes);
21938
21955
  let inlineBody;
@@ -21958,6 +21975,18 @@ ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
21958
21975
  recursiveRegions: [],
21959
21976
  isInsideGuard: false,
21960
21977
  insideFastForEach: false,
21978
+ // Each inline template compiles into its OWN `_headless…Render` function, so its ref
21979
+ // const names (`refCardAction`) are function-local. Give it a fresh generator so two
21980
+ // instances of the same contract don't dedup across the boundary (`refCardAction` in
21981
+ // both, not `refCardAction` / `refCardAction2`).
21982
+ refNameGenerator: new RefNameGenerator(),
21983
+ // DL#193 Phase 3: an instance's OWN template refs are always single — the instance is
21984
+ // one unit. When the instance is repeated at the usage site (inside a forEach), that
21985
+ // collection-ness is captured by the page-level ref manager collecting the instances,
21986
+ // not by the instance's inner refs. Reset `dynamicRef` so forwarded inner refs (e.g.
21987
+ // `<jay:Counter ref="cta">`) stay single here; contract-declared refs already use the
21988
+ // stub's `repeated` flag via importedRefNameToRef, so they are unaffected.
21989
+ dynamicRef: false,
21961
21990
  // Pass headless contract names through so nested headless instances
21962
21991
  // inside headfull FS component templates can be detected (DL#123)
21963
21992
  headlessContractNames: newContext.headlessContractNames
@@ -21974,13 +22003,46 @@ ${renderedChildren.rendered}
21974
22003
  renderedChildren.imports.plus(Import.dynamicElement),
21975
22004
  renderedChildren.validations,
21976
22005
  renderedChildren.refs,
21977
- renderedChildren.recursiveRegions
22006
+ renderedChildren.recursiveRegions,
22007
+ // DL#193 Capability A: preserve the deepest `$parent` climb so the usage site
22008
+ // still weakens gates when instance content spans multiple root children.
22009
+ renderedChildren.parentDepth
21978
22010
  );
21979
22011
  } else {
21980
22012
  inlineBody = renderedChildren;
21981
22013
  }
21982
22014
  }
21983
- const mergedRefs = mergeContractStubRefs(inlineBody.refs, headlessImport.refs);
22015
+ const refOriginalName = htmlElement.attributes.ref || newContext.refNameGenerator.newAutoRefNameGenerator();
22016
+ const refRefName = camelCase$1(refOriginalName);
22017
+ const contractRefNames = /* @__PURE__ */ new Set();
22018
+ const collectContractRefNames = (tree) => {
22019
+ for (const r of tree.refs) contractRefNames.add(camelCase$1(r.ref));
22020
+ for (const child of Object.values(tree.children)) collectContractRefNames(child);
22021
+ };
22022
+ collectContractRefNames(headlessImport.refs);
22023
+ const freeRefDecls = [];
22024
+ const seenFreeRefNames = /* @__PURE__ */ new Set();
22025
+ const collectFreeRefs = (tree) => {
22026
+ for (const r of tree.refs) {
22027
+ if (isComponentType$1(r.elementType)) continue;
22028
+ if (contractRefNames.has(camelCase$1(r.ref))) continue;
22029
+ if (seenFreeRefNames.has(r.ref)) continue;
22030
+ seenFreeRefNames.add(r.ref);
22031
+ freeRefDecls.push({
22032
+ name: r.ref,
22033
+ repeated: r.repeated,
22034
+ viewStateType: r.viewStateType.name,
22035
+ elementType: r.elementType.name
22036
+ });
22037
+ }
22038
+ for (const child of Object.values(tree.children)) collectFreeRefs(child);
22039
+ };
22040
+ collectFreeRefs(inlineBody.refs);
22041
+ const freeRefNames = freeRefDecls.map((f) => f.name);
22042
+ const mergedRefs = hoistFreeRefs(
22043
+ mergeContractStubRefs(inlineBody.refs, headlessImport.refs),
22044
+ new Set(freeRefNames)
22045
+ );
21984
22046
  const { renderedRefsManager, refsManagerImport } = renderReferenceManager(
21985
22047
  mergedRefs,
21986
22048
  ReferenceManagerTarget.element
@@ -21999,6 +22061,17 @@ ${renderedChildren.rendered}
21999
22061
  const coordinateSuffix = `${contractName}:${coordinateRef}`;
22000
22062
  const instanceCoordBase = htmlElement.getAttribute("jay-coordinate-base");
22001
22063
  const coordinateKey2 = isInsideForEach ? void 0 : instanceCoordBase || coordinateSuffix;
22064
+ const coordinateArg = isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`;
22065
+ const freeRefsArg = freeRefNames.length > 0 ? `
22066
+ [${freeRefNames.map((r) => `'${r}'`).join(", ")}],` : "";
22067
+ const componentDefCode = headlessImport.structural ? `const ${componentSymbol} = makePassthroughHeadlessInstanceComponent(
22068
+ ${renderFnName},
22069
+ ${coordinateArg},${freeRefsArg}
22070
+ );` : `const ${componentSymbol} = makeHeadlessInstanceComponent(
22071
+ ${renderFnName},
22072
+ ${pluginComponentName},
22073
+ ${coordinateArg},${freeRefsArg}
22074
+ );`;
22002
22075
  const renderFnCode = `
22003
22076
  // Inline template for headless component: ${contractName} #${idx}
22004
22077
  type ${elementType} = JayElement<${interactiveViewStateType}, ${refsTypeName}>;
@@ -22014,11 +22087,7 @@ ${inlineBody.rendered}
22014
22087
  return [refManager.getPublicAPI() as ${refsTypeName}, render];
22015
22088
  }
22016
22089
 
22017
- const ${componentSymbol} = makeHeadlessInstanceComponent(
22018
- ${renderFnName},
22019
- ${headlessImport.structural ? `{ comp: (_props, _refs) => ({ render: () => _props }) }` : pluginComponentName},
22020
- ${isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`},
22021
- );`;
22090
+ ${componentDefCode}`;
22022
22091
  newContext.headlessInstanceDefs.push({
22023
22092
  componentSymbol,
22024
22093
  renderFnName,
@@ -22031,9 +22100,8 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
22031
22100
  newContext,
22032
22101
  headlessImport.contract?.props
22033
22102
  );
22034
- let getProps = `(${newContext.variables.currentVar}: ${newContext.variables.currentType.name}) => ${propsGetterAndRefs.rendered}`;
22035
- const refOriginalName = htmlElement.attributes.ref || newContext.refNameGenerator.newAutoRefNameGenerator();
22036
- const refRefName = camelCase$1(refOriginalName);
22103
+ const outerVar = newContext.variables.currentVar;
22104
+ let getProps = `(${outerVar}: ${newContext.variables.currentType.name}) => ${propsGetterAndRefs.rendered}`;
22037
22105
  const refConstName = newContext.refNameGenerator.newConstantName(
22038
22106
  refRefName,
22039
22107
  newContext.variables
@@ -22046,6 +22114,18 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
22046
22114
  }
22047
22115
  }
22048
22116
  newContext.usedComponentImports.add(contractRefType);
22117
+ const augmented = freeRefsAugmentedRefType(contractRefType, freeRefDecls, isRepeated);
22118
+ if (freeRefDecls.length > 0) {
22119
+ for (const f of freeRefDecls) {
22120
+ if (!f.viewStateType) continue;
22121
+ for (const link of headlessImport.contractLinks) {
22122
+ if (!link.names.some((n) => n.name === f.viewStateType)) {
22123
+ link.names.push({ name: f.viewStateType, type: JayUnknown });
22124
+ }
22125
+ }
22126
+ newContext.usedComponentImports.add(f.viewStateType);
22127
+ }
22128
+ }
22049
22129
  const instanceRef = mkRef(
22050
22130
  refRefName,
22051
22131
  refOriginalName,
@@ -22053,18 +22133,30 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
22053
22133
  isRepeated,
22054
22134
  !htmlElement.attributes.ref,
22055
22135
  newContext.variables.currentType,
22056
- new JayTypeAlias(contractRefType)
22136
+ new JayTypeAlias(augmented.type),
22137
+ // DL#198 — declare free refs on the page-side region ref so renderReferenceManager emits the
22138
+ // per-region FreeReferenceManager (with per-free-ref `repeated` driving element vs collection).
22139
+ freeRefDecls.length > 0 ? freeRefDecls : void 0
22057
22140
  );
22141
+ const instanceRefsTree = mkRefsTree([instanceRef], {});
22058
22142
  let renderedRef = new RenderFragment(
22059
22143
  `${refConstName}()`,
22060
22144
  Imports.for(),
22061
22145
  [],
22062
- mkRefsTree([instanceRef], {})
22146
+ instanceRefsTree
22063
22147
  );
22064
22148
  if (renderedRef.rendered !== "") renderedRef = renderedRef.map((_) => ", " + _);
22149
+ const freeRefManagerArg = freeRefNames.length > 0 ? `, ${refConstName}FreeRefManager` : "";
22150
+ let childCompImports = Imports.for(Import.childComp).plus(propsGetterAndRefs.imports).plus(renderedRef.imports).plus(Import.ConstructContext).plus(
22151
+ headlessImport.structural ? Import.makePassthroughHeadlessInstanceComponent : Import.makeHeadlessInstanceComponent
22152
+ );
22153
+ if (augmented.needsElementProxy)
22154
+ childCompImports = childCompImports.plus(Import.HTMLElementProxy);
22155
+ if (augmented.needsCollectionProxy)
22156
+ childCompImports = childCompImports.plus(Import.HTMLElementCollectionProxy);
22065
22157
  return new RenderFragment(
22066
- `${newContext.indent.firstLine}childComp(${componentSymbol}, ${getProps}${renderedRef.rendered})`,
22067
- Imports.for(Import.childComp).plus(propsGetterAndRefs.imports).plus(renderedRef.imports).plus(Import.ConstructContext).plus(Import.makeHeadlessInstanceComponent),
22158
+ `${newContext.indent.firstLine}childComp(${componentSymbol}, ${getProps}${renderedRef.rendered}${freeRefManagerArg})`,
22159
+ childCompImports,
22068
22160
  [
22069
22161
  ...propsGetterAndRefs.validations,
22070
22162
  ...inlineBody.validations,
@@ -22113,7 +22205,11 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
22113
22205
  nestedChildElement.imports.plus(Import.withData).plus(accessorExpr.render().imports),
22114
22206
  [...accessorExpr.validations, ...nestedChildElement.validations],
22115
22207
  nestedChildElement.refs,
22116
- nestedChildElement.recursiveRegions
22208
+ nestedChildElement.recursiveRegions,
22209
+ // DL#193: `with-data` is a data-scope switch; it re-runs its body on every
22210
+ // parent update at runtime (no flag needed), and the residual `$parent`
22211
+ // depth drops by one.
22212
+ Math.max(0, nestedChildElement.parentDepth - 1)
22117
22213
  );
22118
22214
  } else if (isRecurse(htmlElement)) {
22119
22215
  const refAttr = htmlElement.getAttribute("ref");
@@ -22187,7 +22283,6 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
22187
22283
  isInsideGuard: true,
22188
22284
  // Mark that we're inside a guard
22189
22285
  insideFastForEach: true
22190
- // Fast-phase forEach — headless instances not supported
22191
22286
  };
22192
22287
  let childElement = renderHtmlElement(htmlElement, newContext);
22193
22288
  return nestRefs(
@@ -22295,9 +22390,7 @@ function renderFunctionImplementation$1(types2, rootBodyElement, importStatement
22295
22390
  const variables = new Variables(types2);
22296
22391
  const { importedSymbols, importedSandboxedSymbols } = processImportedComponents(importStatements);
22297
22392
  const importedRefNameToRef = processImportedHeadless(headlessImports);
22298
- const headlessContractNames = new Set(
22299
- headlessImports.filter((h) => !h.structural).map((h) => h.contractName)
22300
- );
22393
+ const headlessContractNames = new Set(headlessImports.map((h) => h.contractName));
22301
22394
  assignCoordinates(rootBodyElement, { headlessContractNames });
22302
22395
  const rootElement = ensureSingleChildElement(rootBodyElement);
22303
22396
  let renderedRoot;
@@ -22359,8 +22452,12 @@ ${renderedRoot.rendered}
22359
22452
  if (headLinks.length > 0) {
22360
22453
  imports = imports.plus(Import.injectHeadLinks);
22361
22454
  }
22362
- const { imports: refImports, renderedRefs } = renderRefsType(renderedRoot.refs, refsType);
22455
+ const { imports: refImports, renderedRefs: renderedPageRefs } = renderRefsType(
22456
+ renderedRoot.refs,
22457
+ refsType
22458
+ );
22363
22459
  imports = imports.plus(refImports);
22460
+ const renderedRefs = renderedPageRefs;
22364
22461
  let renderedElement = `export type ${elementType} = JayElement<${viewStateType}, ${refsType}>
22365
22462
  export type ${renderType2} = RenderElement<${viewStateType}, ${refsType}, ${elementType}>
22366
22463
  export type ${preRenderType} = [${refsType}, ${renderType2}]
@@ -22374,10 +22471,11 @@ ${Indent.forceIndent(code, 4)},
22374
22471
  funcRepository)`
22375
22472
  );
22376
22473
  }
22377
- const { renderedRefsManager } = renderReferenceManager(
22474
+ const { renderedRefsManager, refsManagerImport } = renderReferenceManager(
22378
22475
  renderedRoot.refs,
22379
22476
  ReferenceManagerTarget.element
22380
22477
  );
22478
+ imports = imports.plus(refsManagerImport);
22381
22479
  const headLinksInjection = headLinks.length > 0 ? ` injectHeadLinks(${renderHeadLinksArray(headLinks)});
22382
22480
  ` : "";
22383
22481
  const recursiveFunctions = generateRecursiveFunctions(renderedRoot.recursiveRegions);
@@ -22389,11 +22487,12 @@ ${recursiveFunctions}
22389
22487
  for (const def of headlessInstanceDefs) {
22390
22488
  imports = imports.plus(def.imports);
22391
22489
  }
22490
+ const rootCallback = `() => ${renderedRoot.rendered.trim()}`;
22392
22491
  const body = `${headlessDefsCode}export function render(options?: RenderElementOptions): ${preRenderType} {
22393
- ${renderedRefsManager}
22492
+ ${renderedRefsManager}
22394
22493
  ${headLinksInjection}${recursiveFunctionsSection} const render = (viewState: ${viewStateType}) => ConstructContext.withRootContext(
22395
22494
  viewState, refManager,
22396
- () => ${renderedRoot.rendered.trim()}
22495
+ ${rootCallback}
22397
22496
  ) as ${elementType};
22398
22497
  return [refManager.getPublicAPI() as ${refsType}, render];
22399
22498
  }`;
@@ -22498,11 +22597,15 @@ function generateElementFile(jayFile, importerMode) {
22498
22597
  const usedHeadlessTypeNames = new Set(usedComponentImports);
22499
22598
  const headlessModules = /* @__PURE__ */ new Set();
22500
22599
  for (const headless of jayFile.headlessImports) {
22501
- usedHeadlessTypeNames.add(headless.rootType.name);
22600
+ if (!headless.structural) {
22601
+ usedHeadlessTypeNames.add(headless.rootType.name);
22602
+ }
22502
22603
  for (const link of headless.contractLinks) {
22503
22604
  headlessModules.add(link.module);
22504
22605
  for (const name of link.names) {
22505
- if (name.name.endsWith("Refs") || isEnumType(name.type)) {
22606
+ if (isEnumType(name.type)) {
22607
+ usedHeadlessTypeNames.add(name.name);
22608
+ } else if (!headless.structural && name.name.endsWith("Refs")) {
22506
22609
  usedHeadlessTypeNames.add(name.name);
22507
22610
  }
22508
22611
  }
@@ -22584,7 +22687,7 @@ function generateElementBridgeFile(jayFile) {
22584
22687
  }
22585
22688
  function generateElementHydrateFile(jayFile, importerMode) {
22586
22689
  const types2 = generateTypes(jayFile.types);
22587
- const headlessImports = jayFile.headlessImports?.filter((h) => !h.key && !h.structural) ?? [];
22690
+ const headlessImports = jayFile.headlessImports?.filter((h) => !h.key) ?? [];
22588
22691
  const headlessContractNames = new Set(headlessImports.map((h) => h.contractName));
22589
22692
  assignCoordinates(jayFile.body, { headlessContractNames });
22590
22693
  const {
@@ -22605,7 +22708,7 @@ function generateElementHydrateFile(jayFile, importerMode) {
22605
22708
  jayFile.headLinks
22606
22709
  );
22607
22710
  const phaseTypes = generatePhaseSpecificTypes(jayFile);
22608
- const renderedHydrate = renderHydrate(
22711
+ const { fragment: renderedHydrate } = renderHydrate(
22609
22712
  jayFile.types,
22610
22713
  jayFile.body,
22611
22714
  jayFile.imports,
@@ -22615,16 +22718,43 @@ function generateElementHydrateFile(jayFile, importerMode) {
22615
22718
  jayFile.headlessImports,
22616
22719
  jayFile.contract
22617
22720
  );
22721
+ const allRenderedRefs = renderedRefs;
22618
22722
  let finalRenderedElement = renderedElement;
22619
22723
  if (jayFile.contract || jayFile.hasInlineData) {
22620
22724
  finalRenderedElement = expandContractType(finalRenderedElement, jayFile.baseElementName);
22621
22725
  }
22622
22726
  const typeOnlyImports = renderedImplementation.imports.minus(renderedHydrate.imports).minus(Import.element).minus(Import.dynamicText).minus(Import.dynamicElement).minus(Import.conditional).minus(Import.forEach);
22623
22727
  const hydrateImports = typeOnlyImports.plus(Import.jayElement).plus(renderedHydrate.imports);
22728
+ const structuralHeadless = (jayFile.headlessImports ?? []).filter((h) => h.structural);
22729
+ let filteredHydrateImports = jayFile.imports;
22730
+ if (structuralHeadless.length > 0) {
22731
+ const structuralModules = new Set(
22732
+ structuralHeadless.flatMap((h) => h.contractLinks.map((l) => l.module))
22733
+ );
22734
+ const dropNames = /* @__PURE__ */ new Set();
22735
+ for (const headless of structuralHeadless) {
22736
+ dropNames.add(headless.rootType.name);
22737
+ }
22738
+ for (const headless of (jayFile.headlessImports ?? []).filter((h) => !h.structural)) {
22739
+ dropNames.delete(headless.rootType.name);
22740
+ for (const link of headless.contractLinks)
22741
+ for (const name of link.names) dropNames.delete(name.name);
22742
+ }
22743
+ filteredHydrateImports = jayFile.imports.map((importLink) => {
22744
+ if (!structuralModules.has(importLink.module)) return importLink;
22745
+ const names = importLink.names.filter((n) => !dropNames.has(n.as || n.name));
22746
+ return names.length === 0 ? null : { ...importLink, names };
22747
+ }).filter((imp) => imp !== null);
22748
+ }
22624
22749
  const renderedFile = [
22625
- renderImports$1(hydrateImports, ImportsFor.implementation, jayFile.imports, importerMode),
22750
+ renderImports$1(
22751
+ hydrateImports,
22752
+ ImportsFor.implementation,
22753
+ filteredHydrateImports,
22754
+ importerMode
22755
+ ),
22626
22756
  types2,
22627
- renderedRefs,
22757
+ allRenderedRefs,
22628
22758
  phaseTypes,
22629
22759
  finalRenderedElement,
22630
22760
  renderedHydrate.rendered
@@ -22863,7 +22993,10 @@ function renderAttributes(element, { variables }) {
22863
22993
  );
22864
22994
  else if (attrName === "class") {
22865
22995
  let classExpression = parseReactClassExpression(attributes2[attrName], variables);
22866
- renderedAttributes.push(classExpression.map((_) => `className=${_}`));
22996
+ let classFragment = classExpression.map((_) => `className=${_}`);
22997
+ if (classExpression.rendered.includes("cx("))
22998
+ classFragment = classFragment.plusImport(Imports.for(Import.classNamesReact));
22999
+ renderedAttributes.push(classFragment);
22867
23000
  } else if (booleanAttributes.has(attrCanonical)) {
22868
23001
  const attrValue = attributes2[attrName];
22869
23002
  if (attrValue === "") {
@@ -23198,6 +23331,24 @@ function generateElementFileReactTarget(jayFile, importerMode) {
23198
23331
  ].filter((_) => _ !== null && _ !== "").join("\n\n");
23199
23332
  return new WithValidations(renderedFile, renderedImplementation.validations);
23200
23333
  }
23334
+ function buildStructuralCoercions(rawVar, tags) {
23335
+ const coercions = [];
23336
+ for (const tag of tags) {
23337
+ const dt = tag.dataType;
23338
+ if (!dt) continue;
23339
+ const field = `${rawVar}.${tag.tag}`;
23340
+ if (isEnumType(dt)) {
23341
+ coercions.push(
23342
+ `${tag.tag}: typeof (${dt.name} as any)[${field}] === 'number' ? (${dt.name} as any)[${field}] : Number(${field})`
23343
+ );
23344
+ } else if (dt.name === "number") {
23345
+ coercions.push(`${tag.tag}: Number(${field})`);
23346
+ } else if (dt.name === "boolean") {
23347
+ coercions.push(`${tag.tag}: ${field} === true || ${field} === 'true'`);
23348
+ }
23349
+ }
23350
+ return coercions;
23351
+ }
23201
23352
  function w(indent, code, imports = Imports.none()) {
23202
23353
  return new RenderFragment(`${indent.firstLine}w(${code});`, imports);
23203
23354
  }
@@ -23314,7 +23465,12 @@ function renderServerHeadlessInstance(element, context, contractName) {
23314
23465
  instanceKeyExpr = `'${instanceCoord}'`;
23315
23466
  }
23316
23467
  const ifCondition = element.attributes.if;
23317
- const componentVariables = new Variables(headlessImport.rootType, void 0, 0, varName);
23468
+ const componentVariables = new Variables(
23469
+ headlessImport.rootType,
23470
+ context.variables,
23471
+ 0,
23472
+ varName
23473
+ ).asLexical();
23318
23474
  const childNodes = filterContentNodes(element.childNodes);
23319
23475
  if (childNodes.length === 0) {
23320
23476
  return new RenderFragment("", Imports.none(), [
@@ -23346,8 +23502,15 @@ function renderServerHeadlessInstance(element, context, contractName) {
23346
23502
  })
23347
23503
  );
23348
23504
  const guardIndent = ifCondition ? new Indent(indent.curr + " ") : indent;
23505
+ const coercions = headlessImport.structural ? buildStructuralCoercions(`${varName}_raw`, headlessImport.contract?.tags ?? []) : [];
23506
+ const extractionLines = coercions.length > 0 ? [
23507
+ `${guardIndent.firstLine}const ${varName}_raw = (vs as any).__headlessInstances?.[${instanceKeyExpr}] as any;`,
23508
+ `${guardIndent.firstLine}const ${varName} = ${varName}_raw ? ({ ...${varName}_raw, ${coercions.join(", ")} } as ${viewStateTypeName}) : undefined;`
23509
+ ] : [
23510
+ `${guardIndent.firstLine}const ${varName} = (vs as any).__headlessInstances?.[${instanceKeyExpr}] as ${viewStateTypeName} | undefined;`
23511
+ ];
23349
23512
  const guardedBlock = [
23350
- `${guardIndent.firstLine}const ${varName} = (vs as any).__headlessInstances?.[${instanceKeyExpr}] as ${viewStateTypeName} | undefined;`,
23513
+ ...extractionLines,
23351
23514
  `${guardIndent.firstLine}if (${varName}) {`,
23352
23515
  renderedChildren.rendered,
23353
23516
  `${guardIndent.firstLine}}`
@@ -23419,12 +23582,9 @@ function renderServerAttributes(element, context) {
23419
23582
  const classExpr = parseClassExpression(attrValue, variables);
23420
23583
  if (classExpr.imports.has(Import.dynamicAttribute)) {
23421
23584
  const rawExpr = classExpr.rendered.replace(/^da\(\w+ => /, "").replace(/\)$/, "");
23585
+ const classImports = classExpr.imports.has(Import.classNames) ? Imports.for(Import.escapeAttr, Import.classNamesServer) : Imports.for(Import.escapeAttr);
23422
23586
  parts.push(
23423
- w(
23424
- indent,
23425
- `' class="' + escapeAttr(String(${rawExpr})) + '"'`,
23426
- Imports.for(Import.escapeAttr)
23427
- )
23587
+ w(indent, `' class="' + escapeAttr(String(${rawExpr})) + '"'`, classImports)
23428
23588
  );
23429
23589
  } else {
23430
23590
  const escaped = escapeForJsString(attrValue);
@@ -23614,10 +23774,11 @@ function renderServerAttributesAsString(element, context) {
23614
23774
  const classExpr = parseClassExpression(attrValue, variables);
23615
23775
  if (classExpr.imports.has(Import.dynamicAttribute)) {
23616
23776
  const rawExpr = classExpr.rendered.replace(/^da\(\w+ => /, "").replace(/\)$/, "");
23777
+ const classImports = classExpr.imports.has(Import.classNames) ? Imports.for(Import.escapeAttr, Import.classNamesServer) : Imports.for(Import.escapeAttr);
23617
23778
  parts.push(
23618
23779
  new RenderFragment(
23619
23780
  `' class="' + escapeAttr(String(${rawExpr})) + '"'`,
23620
- Imports.for(Import.escapeAttr)
23781
+ classImports
23621
23782
  )
23622
23783
  );
23623
23784
  } else {
@@ -23807,7 +23968,14 @@ function renderServerAsyncGroup(group, context) {
23807
23968
  const templateValidations = [];
23808
23969
  if (group.resolvedElement) {
23809
23970
  const promiseResolvedType = asyncAccessor.resolvedType.itemType;
23810
- const resolvedVariables = new Variables(promiseResolvedType, variables, 1);
23971
+ const resolvedVariables = new Variables(
23972
+ promiseResolvedType,
23973
+ variables,
23974
+ 1,
23975
+ void 0,
23976
+ {},
23977
+ variables.lexicallyInScope
23978
+ );
23811
23979
  const resolvedContext = {
23812
23980
  ...context,
23813
23981
  variables: resolvedVariables
@@ -23824,7 +23992,14 @@ function renderServerAsyncGroup(group, context) {
23824
23992
  templateValidations.push(...resolvedString.validations);
23825
23993
  }
23826
23994
  if (group.rejectedElement) {
23827
- const rejectedVariables = new Variables(JayErrorType, variables, 1);
23995
+ const rejectedVariables = new Variables(
23996
+ JayErrorType,
23997
+ variables,
23998
+ 1,
23999
+ void 0,
24000
+ {},
24001
+ variables.lexicallyInScope
24002
+ );
23828
24003
  const rejectedContext = {
23829
24004
  ...context,
23830
24005
  variables: rejectedVariables
@@ -23884,7 +24059,7 @@ function hasMixedContentDynamicTextInteractive(childNodes, interactivePaths) {
23884
24059
  }
23885
24060
  function generateServerElementFile(jayFile, _options) {
23886
24061
  const types2 = generateTypes(jayFile.types);
23887
- const variables = new Variables(jayFile.types);
24062
+ const variables = new Variables(jayFile.types).asLexical();
23888
24063
  const rootElement = ensureSingleChildElement(jayFile.body);
23889
24064
  if (!rootElement.val) {
23890
24065
  return new WithValidations("", rootElement.validations);
@@ -23903,14 +24078,29 @@ function generateServerElementFile(jayFile, _options) {
23903
24078
  interactivePaths: buildInteractivePaths(jayFile.contract),
23904
24079
  parentHasInteractiveChildren: false
23905
24080
  };
23906
- const rendered = renderServerElementContent(rootElement.val, context, {
23907
- isRoot: true
23908
- });
24081
+ const rootComponentMatch = getComponentName(
24082
+ rootElement.val.rawTagName,
24083
+ /* @__PURE__ */ new Set(),
24084
+ // No headful component imports in server-element target
24085
+ headlessContractNames
24086
+ );
24087
+ let rendered;
24088
+ try {
24089
+ rendered = rootComponentMatch !== null && rootComponentMatch.kind === "headless-instance" ? renderServerElement(rootElement.val, context) : renderServerElementContent(rootElement.val, context, {
24090
+ isRoot: true
24091
+ });
24092
+ } catch (e2) {
24093
+ if (e2 instanceof UnsupportedServerParentBindingError) {
24094
+ return new WithValidations("", [UNSUPPORTED_TARGET_PARENT_MESSAGE]);
24095
+ }
24096
+ throw e2;
24097
+ }
23909
24098
  const viewStateType = jayFile.types.name;
23910
24099
  const hasAsync = rendered.rendered.includes("onAsync(");
23911
24100
  const importParts = [];
23912
24101
  if (rendered.imports.has(Import.escapeHtml)) importParts.push("escapeHtml");
23913
24102
  if (rendered.imports.has(Import.escapeAttr)) importParts.push("escapeAttr");
24103
+ if (rendered.imports.has(Import.classNamesServer)) importParts.push("classNames as cx");
23914
24104
  importParts.push("type ServerRenderContext");
23915
24105
  const importStatement = `import {${importParts.join(", ")}} from "@jay-framework/ssr-runtime";`;
23916
24106
  const usedTypeNames = /* @__PURE__ */ new Set();
@@ -23920,17 +24110,35 @@ function generateServerElementFile(jayFile, _options) {
23920
24110
  for (const link of headless.contractLinks) {
23921
24111
  headlessModules.add(link.module);
23922
24112
  for (const name of link.names) {
23923
- if (!name.name.endsWith("Refs")) {
24113
+ if (isEnumType(name.type)) {
24114
+ usedTypeNames.add(name.name);
24115
+ } else if (!name.name.endsWith("Refs")) {
23924
24116
  usedTypeNames.add(name.name);
23925
24117
  }
23926
24118
  }
23927
24119
  }
23928
24120
  }
23929
- const contractImports = jayFile.imports.filter((imp) => headlessModules.has(imp.module)).map((imp) => {
23930
- const typeNames = imp.names.filter((n) => usedTypeNames.has(n.as || n.name)).map((n) => n.as ? `${n.name} as ${n.as}` : n.name);
23931
- if (typeNames.length === 0) return null;
23932
- return `import {${typeNames.join(", ")}} from "${imp.module}";`;
23933
- }).filter((_) => _ !== null);
24121
+ const typeNamesByModule = /* @__PURE__ */ new Map();
24122
+ const typeNamesSeen = /* @__PURE__ */ new Map();
24123
+ for (const imp of jayFile.imports) {
24124
+ if (!headlessModules.has(imp.module)) continue;
24125
+ let names = typeNamesByModule.get(imp.module);
24126
+ let seen = typeNamesSeen.get(imp.module);
24127
+ if (!names) {
24128
+ names = [];
24129
+ seen = /* @__PURE__ */ new Set();
24130
+ typeNamesByModule.set(imp.module, names);
24131
+ typeNamesSeen.set(imp.module, seen);
24132
+ }
24133
+ for (const n of imp.names) {
24134
+ if (!usedTypeNames.has(n.as || n.name)) continue;
24135
+ const rendered2 = n.as ? `${n.name} as ${n.as}` : n.name;
24136
+ if (seen.has(rendered2)) continue;
24137
+ seen.add(rendered2);
24138
+ names.push(rendered2);
24139
+ }
24140
+ }
24141
+ const contractImports = [...typeNamesByModule.entries()].filter(([, typeNames]) => typeNames.length > 0).map(([module, typeNames]) => `import {${typeNames.join(", ")}} from "${module}";`);
23934
24142
  const ctxDestructure = hasAsync ? "const { write: w, onAsync } = ctx;" : "const { write: w } = ctx;";
23935
24143
  const renderedFile = [
23936
24144
  importStatement,
@@ -30653,12 +30861,13 @@ const JAY_IMPORT_RESOLVER = {
30653
30861
  return analyzeExportedTypes(fullPath, options);
30654
30862
  },
30655
30863
  loadContract(fullPath) {
30656
- const content = fs.readFileSync(fullPath).toString();
30864
+ const content = fs$1.readFileSync(fullPath).toString();
30657
30865
  return parseContract(content, fullPath);
30658
30866
  },
30659
30867
  resolveLink(importingModuleDir, link) {
30660
30868
  if (link?.[0] === ".") return path.resolve(importingModuleDir, link);
30661
- else return require$1.resolve(link, { paths: require$1.resolve.paths(importingModuleDir) });
30869
+ if (link?.[0] === "/") return link;
30870
+ return require$1.resolve(link, { paths: require$1.resolve.paths(importingModuleDir) });
30662
30871
  },
30663
30872
  resolvePluginComponent(pluginName, contractName, projectRoot) {
30664
30873
  return resolvePluginComponent(projectRoot, pluginName, contractName);
@@ -30667,7 +30876,7 @@ const JAY_IMPORT_RESOLVER = {
30667
30876
  const resolution = resolvePluginComponent(projectRoot, pluginName, contractName);
30668
30877
  if (resolution.val && resolution.val.contractPath) {
30669
30878
  const contractPath = resolution.val.contractPath;
30670
- const content = fs.readFileSync(contractPath).toString();
30879
+ const content = fs$1.readFileSync(contractPath).toString();
30671
30880
  const contractResult = parseContract(content, contractPath);
30672
30881
  return contractResult.map((contract) => ({ contract, contractPath }));
30673
30882
  }
@@ -30675,14 +30884,14 @@ const JAY_IMPORT_RESOLVER = {
30675
30884
  const pluginDir = pluginName.startsWith("@") ? pluginName.split("/").pop() || pluginName : pluginName;
30676
30885
  const contractFileName = contractName.replace(/\//g, "-") + ".jay-contract";
30677
30886
  const materializedPath = path.join(materializedDir, pluginDir, contractFileName);
30678
- if (fs.existsSync(materializedPath)) {
30679
- const content = fs.readFileSync(materializedPath).toString();
30887
+ if (fs$1.existsSync(materializedPath)) {
30888
+ const content = fs$1.readFileSync(materializedPath).toString();
30680
30889
  const contractResult = parseContract(content, materializedPath);
30681
30890
  let metadata;
30682
30891
  const indexPath = path.join(projectRoot, "agent-kit", "plugins-index.yaml");
30683
- if (fs.existsSync(indexPath)) {
30892
+ if (fs$1.existsSync(indexPath)) {
30684
30893
  try {
30685
- const indexContent = fs.readFileSync(indexPath, "utf-8");
30894
+ const indexContent = fs$1.readFileSync(indexPath, "utf-8");
30686
30895
  const index2 = YAML.parse(indexContent);
30687
30896
  const plugin = index2.plugins?.find(
30688
30897
  (p) => p.name === pluginName || p.name === pluginDir
@@ -30711,13 +30920,13 @@ const JAY_IMPORT_RESOLVER = {
30711
30920
  const resolvedPath = src.startsWith(".") ? path.resolve(importingModuleDir, src) : src;
30712
30921
  const jayHtmlPath = resolvedPath + ".jay-html";
30713
30922
  try {
30714
- const content = fs.readFileSync(jayHtmlPath, "utf-8");
30923
+ const content = fs$1.readFileSync(jayHtmlPath, "utf-8");
30715
30924
  return { content, componentDir: path.dirname(jayHtmlPath), filePath: jayHtmlPath };
30716
30925
  } catch {
30717
30926
  }
30718
30927
  const dirJayHtmlPath = path.resolve(resolvedPath, path.basename(resolvedPath)) + ".jay-html";
30719
30928
  try {
30720
- const content = fs.readFileSync(dirJayHtmlPath, "utf-8");
30929
+ const content = fs$1.readFileSync(dirJayHtmlPath, "utf-8");
30721
30930
  return {
30722
30931
  content,
30723
30932
  componentDir: path.dirname(dirJayHtmlPath),
@@ -30907,6 +31116,10 @@ function resolveRelativePaths(root, sourceDir) {
30907
31116
  if (src && isRelativePath(src)) {
30908
31117
  script.setAttribute("src", path.resolve(sourceDir, src));
30909
31118
  }
31119
+ const contract = script.getAttribute("contract");
31120
+ if (contract && isRelativePath(contract)) {
31121
+ script.setAttribute("contract", path.resolve(sourceDir, contract));
31122
+ }
30910
31123
  }
30911
31124
  const links = root.querySelectorAll('link[rel="stylesheet"]');
30912
31125
  for (const link of links) {
@@ -31014,7 +31227,7 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
31014
31227
  const instances = [];
31015
31228
  const forEachInstances = [];
31016
31229
  const coordinateCounters = /* @__PURE__ */ new Map();
31017
- function walk(element, insidePreservedForEach, forEachContexts) {
31230
+ function walk(element, insidePreservedForEach, forEachContexts, parentCoordinate) {
31018
31231
  const tagName = element.tagName?.toLowerCase();
31019
31232
  const forEachAttr = element.getAttribute("forEach");
31020
31233
  const hasForEach = forEachAttr != null;
@@ -31028,6 +31241,7 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
31028
31241
  props[toCamelCase(key)] = value;
31029
31242
  }
31030
31243
  }
31244
+ let childParentCoordinate = parentCoordinate;
31031
31245
  if (!insidePreservedForEach) {
31032
31246
  let ref = element.getAttribute("ref");
31033
31247
  if (!ref) {
@@ -31043,8 +31257,10 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
31043
31257
  instances.push({
31044
31258
  contractName,
31045
31259
  props,
31046
- coordinate
31260
+ coordinate,
31261
+ parentCoordinate
31047
31262
  });
31263
+ childParentCoordinate = coordinate;
31048
31264
  } else {
31049
31265
  const innerForEach = forEachContexts[forEachContexts.length - 1];
31050
31266
  if (innerForEach) {
@@ -31066,7 +31282,12 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
31066
31282
  }
31067
31283
  for (const child of element.childNodes) {
31068
31284
  if (child.nodeType === NodeType.ELEMENT_NODE) {
31069
- walk(child, insidePreservedForEach, forEachContexts);
31285
+ walk(
31286
+ child,
31287
+ insidePreservedForEach,
31288
+ forEachContexts,
31289
+ childParentCoordinate
31290
+ );
31070
31291
  }
31071
31292
  }
31072
31293
  return;
@@ -31077,14 +31298,15 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
31077
31298
  walk(
31078
31299
  child,
31079
31300
  insidePreservedForEach || hasForEach,
31080
- updatedForEachContexts
31301
+ updatedForEachContexts,
31302
+ parentCoordinate
31081
31303
  );
31082
31304
  }
31083
31305
  }
31084
31306
  }
31085
31307
  const body = root.querySelector("body");
31086
31308
  if (body) {
31087
- walk(body, false, []);
31309
+ walk(body, false, [], void 0);
31088
31310
  }
31089
31311
  return { instances, forEachInstances, preRenderedJayHtml: root.toString() };
31090
31312
  }
@@ -31215,7 +31437,6 @@ export {
31215
31437
  getLinkedContractDir,
31216
31438
  hasSlowPhaseProperties,
31217
31439
  htmlElementTagNameMap,
31218
- injectHeadfullFSTemplates,
31219
31440
  isTagInPhase,
31220
31441
  loadLinkedContract,
31221
31442
  parseAction,