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

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 : {};
@@ -5777,6 +5777,7 @@ parse$1.TextNode = text_1.default;
5777
5777
  parse$1.NodeType = type_1.default;
5778
5778
  const COORD_ATTR$1 = "jay-coordinate-base";
5779
5779
  const SCOPE_ATTR = "jay-scope";
5780
+ const AUTO_REF_PATTERN = /^AR\d+$/;
5780
5781
  function assignCoordinatesToJayHtml(jayHtml, headlessContractNames) {
5781
5782
  const root = parse_2(jayHtml, {
5782
5783
  comment: true,
@@ -5790,6 +5791,12 @@ function assignCoordinatesToJayHtml(jayHtml, headlessContractNames) {
5790
5791
  function nextScopeId(counter) {
5791
5792
  return `S${counter.next++}`;
5792
5793
  }
5794
+ function isScopeAnchorDiv(node2) {
5795
+ if (node2.nodeType !== NodeType.ELEMENT_NODE) return false;
5796
+ const el = node2;
5797
+ if (el.tagName?.toLowerCase() !== "div") return false;
5798
+ return (el.getAttribute("style") ?? "").replace(/\s+/g, "").includes("display:contents");
5799
+ }
5793
5800
  function assignCoordinates(body, options) {
5794
5801
  if (!options._refCounters) options._refCounters = /* @__PURE__ */ new Map();
5795
5802
  const rootChildren = body.childNodes.filter(
@@ -5814,6 +5821,7 @@ function walkChildren(parent, parentCoord, scopeId, options, counter) {
5814
5821
  const contractName = tagName.substring(4);
5815
5822
  if (options.headlessContractNames.has(contractName)) {
5816
5823
  let ref = element.getAttribute("ref");
5824
+ const explicitRef = !!ref && !AUTO_REF_PATTERN.test(ref);
5817
5825
  if (!ref) {
5818
5826
  const idx = options._refCounters.get(contractName) ?? 0;
5819
5827
  options._refCounters.set(contractName, idx + 1);
@@ -5825,9 +5833,9 @@ function walkChildren(parent, parentCoord, scopeId, options, counter) {
5825
5833
  contractName,
5826
5834
  ref,
5827
5835
  parentCoord,
5828
- scopeId,
5829
5836
  options,
5830
- counter
5837
+ counter,
5838
+ explicitRef
5831
5839
  );
5832
5840
  continue;
5833
5841
  }
@@ -5851,18 +5859,20 @@ function walkChildren(parent, parentCoord, scopeId, options, counter) {
5851
5859
  walkChildren(element, coord, scopeId, options, counter);
5852
5860
  }
5853
5861
  }
5854
- function assignHeadlessInstance(element, contractName, ref, parentCoord, parentScopeId, options, counter) {
5862
+ function assignHeadlessInstance(element, contractName, ref, parentCoord, options, counter, explicitRef) {
5855
5863
  const instanceCoord = `${parentCoord}/${contractName}:${ref}`;
5856
5864
  element.setAttribute(COORD_ATTR$1, instanceCoord);
5857
5865
  const childScopeId = nextScopeId(counter);
5858
5866
  element.setAttribute(SCOPE_ATTR, childScopeId);
5859
- const significantChildren = element.childNodes.filter(
5867
+ const significantTemplateChildren = element.childNodes.filter(
5860
5868
  (n) => n.nodeType === NodeType.ELEMENT_NODE || n.nodeType === NodeType.TEXT_NODE && (n.innerText || "").trim() !== ""
5861
5869
  );
5862
- if (significantChildren.length > 1) {
5870
+ const alreadyWrapped = significantTemplateChildren.length === 1 && isScopeAnchorDiv(significantTemplateChildren[0]);
5871
+ if (!alreadyWrapped && (explicitRef || significantTemplateChildren.length > 1)) {
5863
5872
  const wrapper = parse_2("<div></div>").querySelector("div");
5873
+ if (explicitRef) wrapper.setAttribute("class", ref);
5864
5874
  wrapper.setAttribute("style", "display: contents");
5865
- const children = [...element.childNodes];
5875
+ const children = element.childNodes;
5866
5876
  element.innerHTML = "";
5867
5877
  children.forEach((child) => wrapper.appendChild(child));
5868
5878
  element.appendChild(wrapper);
@@ -5879,6 +5889,7 @@ function walkForEachChildren(parent, itemScopeId, options, counter) {
5879
5889
  const contractName = tagName.substring(4);
5880
5890
  if (options.headlessContractNames.has(contractName)) {
5881
5891
  let ref = element.getAttribute("ref");
5892
+ const explicitRef = !!ref && !AUTO_REF_PATTERN.test(ref);
5882
5893
  if (!ref) {
5883
5894
  const counterKey = `forEach/${contractName}`;
5884
5895
  const idx = options._refCounters.get(counterKey) ?? 0;
@@ -5891,9 +5902,9 @@ function walkForEachChildren(parent, itemScopeId, options, counter) {
5891
5902
  contractName,
5892
5903
  ref,
5893
5904
  itemScopeId,
5894
- itemScopeId,
5895
5905
  options,
5896
- counter
5906
+ counter,
5907
+ explicitRef
5897
5908
  );
5898
5909
  continue;
5899
5910
  }
@@ -6108,6 +6119,18 @@ function capitalCase(input, options) {
6108
6119
  }
6109
6120
  return noCase(input, __assign({ delimiter: " ", transform: capitalCaseTransform }, options));
6110
6121
  }
6122
+ function dotCase(input, options) {
6123
+ if (options === void 0) {
6124
+ options = {};
6125
+ }
6126
+ return noCase(input, __assign({ delimiter: "." }, options));
6127
+ }
6128
+ function paramCase(input, options) {
6129
+ if (options === void 0) {
6130
+ options = {};
6131
+ }
6132
+ return dotCase(input, __assign({ delimiter: "-" }, options));
6133
+ }
6111
6134
  function shouldIncludeInPhase(propertyPhase, targetPhase) {
6112
6135
  if (targetPhase === "slow") {
6113
6136
  return propertyPhase === "slow";
@@ -6588,11 +6611,11 @@ function peg$parse(input, options) {
6588
6611
  }, peg$c1 = function(comment2) {
6589
6612
  return null;
6590
6613
  }, 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());
6614
+ const camelProp = prop.startsWith("--") ? prop : prop.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
6592
6615
  const [valueFragment, isDynamic] = value;
6593
6616
  return {
6594
6617
  property: camelProp,
6595
- valueFragment: isDynamic ? valueFragment.map((_) => `dp(${vars.currentVar} => ${_})`).plusImport(dp) : valueFragment,
6618
+ valueFragment: isDynamic ? valueFragment.map((_) => `dp(${jayParams(valueFragment)} => ${_})`).plusImport(dp) : valueFragment,
6596
6619
  isDynamic
6597
6620
  };
6598
6621
  }, peg$c7 = function() {
@@ -6630,31 +6653,32 @@ function peg$parse(input, options) {
6630
6653
  if (isDynamic) {
6631
6654
  if (tail.length === 0)
6632
6655
  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, " "),
6656
+ const elements = classes.map((cls) => cls instanceof RenderFragment2 ? cls : new RenderFragment2(`'${cls}'`));
6657
+ const reduced = elements.reduce(
6658
+ (result, fragment) => RenderFragment2.merge(result, fragment, ", "),
6638
6659
  RenderFragment2.empty()
6639
6660
  );
6640
- return reduced.map((_) => `{\`${_}\`}`);
6661
+ return reduced.map((_) => `{cx(${_})}`).plusImport(cx);
6641
6662
  } else
6642
6663
  return new RenderFragment2(classes.join(" "), none).map((_) => `"${_}"`);
6643
6664
  }, 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((_) => `'${_}'`);
6665
+ const classes = [head, ...tail.map((tuple) => tuple[1])];
6666
+ const isDynamic = classes.some((cls) => cls instanceof RenderFragment2);
6667
+ if (isDynamic) {
6668
+ if (tail.length === 0)
6669
+ return head.map((_) => `da(${jayParams(head)} => ${_})`);
6670
+ const elements = classes.map((cls) => cls instanceof RenderFragment2 ? cls : new RenderFragment2(`'${cls}'`));
6671
+ const arrayContents = elements.reduce(
6672
+ (result, fragment) => RenderFragment2.merge(result, fragment, ", "),
6673
+ RenderFragment2.empty()
6674
+ );
6675
+ return arrayContents.map((_) => `da(${jayParams(arrayContents)} => cx(${_}))`).plusImport(cx);
6676
+ }
6677
+ return new RenderFragment2(classes.join(" "), none).map((_) => `'${_}'`);
6654
6678
  }, peg$c44 = function(cls) {
6655
6679
  return cls.render().plusImport(da);
6656
6680
  }, 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);
6681
+ return new RenderFragment2(`${acc.rendered}?'${classY}':'${classN ? classN[3] : ""}'`, da, acc.validations, void 0, void 0, acc.parentDepth);
6658
6682
  }, 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
6683
  return text2();
6660
6684
  }, peg$c61 = "as", peg$c62 = peg$literalExpectation("as", false), peg$c63 = ",", peg$c64 = peg$literalExpectation(",", false), peg$c65 = function(head, rename, tail) {
@@ -6669,10 +6693,10 @@ function peg$parse(input, options) {
6669
6693
  return [...acc, val[3]];
6670
6694
  }, [first]);
6671
6695
  }, peg$c78 = function(cond) {
6672
- return cond.map((_) => `ba(${vars.currentVar} => ${_})`).plusImport(ba);
6696
+ return cond.map((_) => `ba(${jayParams(cond)} => ${_})`).plusImport(ba);
6673
6697
  }, peg$c79 = function(template) {
6674
6698
  let [renderFragment, isDynamic] = template;
6675
- return isDynamic ? renderFragment.map((_) => `da(${vars.currentVar} => ${_})`).plusImport(da) : renderFragment;
6699
+ return isDynamic ? renderFragment.map((_) => `da(${jayParams(renderFragment)} => ${_})`).plusImport(da) : renderFragment;
6676
6700
  }, peg$c80 = function(num) {
6677
6701
  return new RenderFragment2(num);
6678
6702
  }, peg$c81 = function(template) {
@@ -6680,7 +6704,7 @@ function peg$parse(input, options) {
6680
6704
  return renderFragment;
6681
6705
  }, peg$c82 = function(template) {
6682
6706
  let [renderFragment, isDynamic] = template;
6683
- return isDynamic ? renderFragment.map((_) => `dp(${vars.currentVar} => ${_})`).plusImport(dp) : renderFragment;
6707
+ return isDynamic ? renderFragment.map((_) => `dp(${jayParams(renderFragment)} => ${_})`).plusImport(dp) : renderFragment;
6684
6708
  }, peg$c83 = function(template) {
6685
6709
  let [renderFragment, isDynamic] = template;
6686
6710
  return isDynamic ? renderFragment.map((_) => `{${_}}`) : renderFragment;
@@ -6705,7 +6729,7 @@ function peg$parse(input, options) {
6705
6729
  }
6706
6730
  }, peg$c86 = function(template) {
6707
6731
  let [renderFragment, isDynamic] = template;
6708
- return isDynamic ? renderFragment.map((_) => `dt(${vars.currentVar} => ${_})`).plusImport(dt) : renderFragment;
6732
+ return isDynamic ? renderFragment.map((_) => `dt(${jayParams(renderFragment)} => ${_})`).plusImport(dt) : renderFragment;
6709
6733
  }, peg$c87 = function(a, head, tail) {
6710
6734
  const renderText2 = (w2, h) => {
6711
6735
  return h ? h.reduce((acc, str2) => acc + str2[0] + (str2[1].length ? " " : ""), w2.length ? " " : "") : w2.length ? " " : "";
@@ -6730,7 +6754,7 @@ function peg$parse(input, options) {
6730
6754
  const raw = text2();
6731
6755
  return { kind: "binding", value: raw.slice(1, -1).trim() };
6732
6756
  }, peg$c93 = function(cond) {
6733
- return cond.map((_) => `${vars.currentVar} => ${_}`);
6757
+ return cond.map((_) => `${jayParams(cond)} => ${_}`);
6734
6758
  }, peg$c94 = "||", peg$c95 = peg$literalExpectation("||", false), peg$c96 = function(head, tail) {
6735
6759
  if (tail.length === 0) return head;
6736
6760
  return tail.reduce((acc, curr) => combineOr(acc, curr[3]), head);
@@ -13302,6 +13326,7 @@ function peg$parse(input, options) {
13302
13326
  let da = options.da;
13303
13327
  let dp = options.dp;
13304
13328
  let ba = options.ba;
13329
+ let cx = options.cx;
13305
13330
  let slowContext = options.slowContext;
13306
13331
  function isSlowPhase2(path2) {
13307
13332
  if (!slowContext) return false;
@@ -13420,6 +13445,13 @@ function peg$parse(input, options) {
13420
13445
  expr: `${leftExpr} ${jsOp} ${rightExpr}`
13421
13446
  };
13422
13447
  }
13448
+ function jayParams(renderFragment) {
13449
+ const depth = renderFragment && renderFragment.parentDepth || 0;
13450
+ if (depth === 0) return vars.currentVar;
13451
+ let params = vars.currentVar;
13452
+ for (let i = 1; i <= depth; i++) params += `, _p${i}`;
13453
+ return `(${params})`;
13454
+ }
13423
13455
  function applyStartsWith(left, right) {
13424
13456
  if (left.type === "resolved" && right.type === "resolved") {
13425
13457
  return { type: "resolved", value: String(left.value).startsWith(String(right.value)) };
@@ -13450,18 +13482,37 @@ function peg$parse(input, options) {
13450
13482
  var expressionParser = {
13451
13483
  parse: peg$parse
13452
13484
  };
13485
+ const PARENT_TOKEN = "$parent";
13486
+ function parentParamName(level) {
13487
+ return `_p${level}`;
13488
+ }
13453
13489
  class Accessor {
13454
13490
  rootVar;
13455
13491
  terms;
13456
13492
  validations;
13457
13493
  resolvedType;
13458
- constructor(rootVar, terms, validations, resolvedType) {
13494
+ /** DL#193: how many scopes a `$parent` access climbs (0 = current scope). */
13495
+ parentLevel;
13496
+ constructor(rootVar, terms, validations, resolvedType, parentLevel = 0) {
13459
13497
  this.rootVar = rootVar;
13460
13498
  this.terms = terms;
13461
13499
  this.validations = validations;
13462
13500
  this.resolvedType = resolvedType;
13501
+ this.parentLevel = parentLevel;
13463
13502
  }
13464
13503
  render() {
13504
+ if (this.parentLevel > 0) {
13505
+ const param = parentParamName(this.parentLevel);
13506
+ const rendered = this.terms.length === 0 ? param : param + "." + this.terms.join("?.");
13507
+ return new RenderFragment(
13508
+ rendered,
13509
+ Imports.none(),
13510
+ this.validations,
13511
+ void 0,
13512
+ void 0,
13513
+ this.parentLevel
13514
+ );
13515
+ }
13465
13516
  let renderedAccessor = this.terms.length === 1 && this.terms[0] === "." ? this.rootVar : this.rootVar + "." + this.terms.join("?.");
13466
13517
  return new RenderFragment(`${renderedAccessor}`, Imports.none(), this.validations);
13467
13518
  }
@@ -13471,20 +13522,76 @@ class Variables {
13471
13522
  currentType;
13472
13523
  currentContext;
13473
13524
  parent;
13474
- children = {};
13525
+ children;
13475
13526
  depth;
13476
- constructor(currentTypes, parent = void 0, depth = 0, customVarName) {
13527
+ /**
13528
+ * DL#193 Phase 2c: when true, this scope's `currentVar` is a real variable that is lexically
13529
+ * in scope at every descendant binding site (the server target inlines the whole tree into one
13530
+ * `renderToStream`, so `vs`, forEach item vars, and instance vars are all reachable by name).
13531
+ * In that world a `$parent` climb that lands on this scope must emit `currentVar` directly, not
13532
+ * a `_pN` closure param (which only exists in the client/hydrate per-binding callbacks). The
13533
+ * flag is inherited by every child scope so setting it once on the server root covers the tree.
13534
+ */
13535
+ lexicallyInScope;
13536
+ constructor(currentTypes, parent = void 0, depth = 0, customVarName, children = {}, lexicallyInScope = false) {
13477
13537
  this.currentVar = customVarName || (depth === 0 ? "vs" : "vs" + depth);
13478
13538
  this.currentContext = depth === 0 ? "context" : "cx" + depth;
13479
13539
  this.depth = depth;
13480
13540
  this.parent = parent;
13481
13541
  this.currentType = currentTypes instanceof JayImportedType ? currentTypes.type : currentTypes;
13542
+ this.children = children;
13543
+ this.lexicallyInScope = lexicallyInScope;
13544
+ }
13545
+ /**
13546
+ * DL#193 Phase 2c: return a view of this scope marked lexically-in-scope (see
13547
+ * {@link lexicallyInScope}). Used by the server target on its root scope; child scopes
13548
+ * inherit the flag automatically through {@link childVariableFor} / {@link childVariableForWithData}.
13549
+ */
13550
+ asLexical() {
13551
+ if (this.lexicallyInScope) return this;
13552
+ return new Variables(
13553
+ this.currentType,
13554
+ this.parent,
13555
+ this.depth,
13556
+ this.currentVar,
13557
+ this.children,
13558
+ true
13559
+ );
13482
13560
  }
13483
13561
  resolveAccessor(accessor) {
13484
13562
  if (accessor[0] === "jay") {
13485
13563
  const jayPath = ["__jay", ...accessor.slice(1)];
13486
13564
  return new Accessor(this.currentVar, jayPath, [], JayString);
13487
13565
  }
13566
+ if (accessor[0] === PARENT_TOKEN) {
13567
+ let parentLevel = 0;
13568
+ let scope = this;
13569
+ let terms = accessor;
13570
+ while (terms[0] === PARENT_TOKEN) {
13571
+ parentLevel++;
13572
+ if (!scope.parent) {
13573
+ const message = `${"$parent".repeat(parentLevel)} used but there is no parent scope ${parentLevel} level(s) up`;
13574
+ return new Accessor(
13575
+ this.currentVar,
13576
+ terms.slice(1),
13577
+ [message],
13578
+ JayUnknown,
13579
+ parentLevel
13580
+ );
13581
+ }
13582
+ scope = scope.parent;
13583
+ terms = terms.slice(1);
13584
+ }
13585
+ const resolved = scope.resolveAccessor(terms);
13586
+ const effectiveParentLevel = scope.lexicallyInScope ? resolved.parentLevel : parentLevel + resolved.parentLevel;
13587
+ return new Accessor(
13588
+ scope.currentVar,
13589
+ resolved.terms,
13590
+ resolved.validations,
13591
+ resolved.resolvedType,
13592
+ effectiveParentLevel
13593
+ );
13594
+ }
13488
13595
  let curr = this.currentType;
13489
13596
  let validations = [];
13490
13597
  accessor.forEach((member) => {
@@ -13506,7 +13613,14 @@ class Variables {
13506
13613
  if (this.children[path2]) return this.children[path2];
13507
13614
  else {
13508
13615
  const resolvedForEachType = accessor.resolvedType.itemType;
13509
- const variables = new Variables(resolvedForEachType, this, this.depth + 1);
13616
+ const variables = new Variables(
13617
+ resolvedForEachType,
13618
+ this,
13619
+ this.depth + 1,
13620
+ void 0,
13621
+ {},
13622
+ this.lexicallyInScope
13623
+ );
13510
13624
  this.children[path2] = variables;
13511
13625
  return variables;
13512
13626
  }
@@ -13522,7 +13636,14 @@ class Variables {
13522
13636
  depth++;
13523
13637
  parent = parent.parent;
13524
13638
  }
13525
- const variables = new Variables(accessor.resolvedType, this, depth);
13639
+ const variables = new Variables(
13640
+ accessor.resolvedType,
13641
+ this,
13642
+ depth,
13643
+ void 0,
13644
+ {},
13645
+ this.lexicallyInScope
13646
+ );
13526
13647
  this.children[path2] = variables;
13527
13648
  return variables;
13528
13649
  }
@@ -13677,6 +13798,7 @@ function doParse(expression, startRule, vars, throwOnError = false) {
13677
13798
  da: Imports.for(Import.dynamicAttribute),
13678
13799
  dp: Imports.for(Import.dynamicProperty),
13679
13800
  ba: Imports.for(Import.booleanAttribute),
13801
+ cx: Imports.for(Import.classNames),
13680
13802
  startRule
13681
13803
  });
13682
13804
  } catch (e2) {
@@ -13697,8 +13819,19 @@ function parseAccessor(expression, vars) {
13697
13819
  function parseCondition(expression, vars) {
13698
13820
  return doParse(expression, "conditionFunc", vars);
13699
13821
  }
13822
+ function guardReactParentBinding(fragment) {
13823
+ if (fragment.parentDepth === 0) return fragment;
13824
+ return new RenderFragment(
13825
+ fragment.rendered,
13826
+ fragment.imports,
13827
+ [...fragment.validations, "$parent bindings are not supported in the React target"],
13828
+ fragment.refs,
13829
+ fragment.recursiveRegions,
13830
+ fragment.parentDepth
13831
+ );
13832
+ }
13700
13833
  function parseReactCondition(expression, vars) {
13701
- return doParse(expression, "condition", vars);
13834
+ return guardReactParentBinding(doParse(expression, "condition", vars));
13702
13835
  }
13703
13836
  function parseTextExpression(expression, vars) {
13704
13837
  return doParse(expression, "dynamicText", vars);
@@ -13721,7 +13854,9 @@ function unescapeBackslash(jsCode) {
13721
13854
  });
13722
13855
  }
13723
13856
  function parseReactTextExpression(expression, vars) {
13724
- return doParse(expression, "reactDynamicText", vars).map((_) => unescapeBackslash(_));
13857
+ return guardReactParentBinding(
13858
+ doParse(expression, "reactDynamicText", vars).map((_) => unescapeBackslash(_))
13859
+ );
13725
13860
  }
13726
13861
  function parseBooleanAttributeExpression(expression, vars) {
13727
13862
  return doParse(expression, "booleanAttribute", vars);
@@ -13733,7 +13868,7 @@ function parsePropertyExpression(expression, vars) {
13733
13868
  return doParse(expression, "dynamicProperty", vars);
13734
13869
  }
13735
13870
  function parseReactPropertyExpression(expression, vars) {
13736
- return doParse(expression, "reactDynamicProperty", vars);
13871
+ return guardReactParentBinding(doParse(expression, "reactDynamicProperty", vars));
13737
13872
  }
13738
13873
  function parseComponentPropExpression(expression, vars) {
13739
13874
  return doParse(expression, "dynamicComponentProp", vars);
@@ -13741,23 +13876,34 @@ function parseComponentPropExpression(expression, vars) {
13741
13876
  function parseClassExpression(expression, vars) {
13742
13877
  return doParse(expression, "classExpression", vars);
13743
13878
  }
13879
+ const UNSUPPORTED_TARGET_PARENT_MESSAGE = "$parent bindings are not yet supported in the server target";
13880
+ class UnsupportedServerParentBindingError extends Error {
13881
+ constructor() {
13882
+ super(UNSUPPORTED_TARGET_PARENT_MESSAGE);
13883
+ this.name = "UnsupportedServerParentBindingError";
13884
+ }
13885
+ }
13886
+ function guardServerParentBinding(fragment) {
13887
+ if (fragment.parentDepth === 0) return fragment;
13888
+ throw new UnsupportedServerParentBindingError();
13889
+ }
13744
13890
  function parseServerTemplateExpression(expression, vars) {
13745
- return doParse(expression, "template", vars);
13891
+ const [fragment, isDynamic] = doParse(expression, "template", vars);
13892
+ return [guardServerParentBinding(fragment), isDynamic];
13746
13893
  }
13747
13894
  function parseTemplateParts(value) {
13748
13895
  if (!value) return [];
13749
13896
  return doParse(value, "templateParts");
13750
13897
  }
13751
13898
  function parseServerCondition(expression, vars) {
13752
- return doParse(expression, "condition", vars);
13899
+ return guardServerParentBinding(doParse(expression, "condition", vars));
13753
13900
  }
13754
13901
  function parseReactClassExpression(expression, vars) {
13755
- const { rendered, validations, refs } = doParse(
13756
- expression,
13757
- "reactClassExpression",
13758
- vars
13902
+ const parsed = doParse(expression, "reactClassExpression", vars);
13903
+ const { rendered, validations, refs } = parsed;
13904
+ return guardReactParentBinding(
13905
+ new RenderFragment(rendered, Imports.none(), validations, refs, [], parsed.parentDepth)
13759
13906
  );
13760
- return new RenderFragment(rendered, Imports.none(), validations, refs);
13761
13907
  }
13762
13908
  function parseImportNames(expression) {
13763
13909
  return doParse(expression, "importNames", void 0, true);
@@ -14136,7 +14282,7 @@ const isComponentType = (type2) => type2 instanceof JayComponentType || type2 in
14136
14282
  const isComponentRef = (ref) => isComponentType(ref.elementType);
14137
14283
  const isCollectionRef = (ref) => ref.repeated;
14138
14284
  const isComponentCollectionRef = (ref) => isCollectionRef(ref) && isComponentRef(ref);
14139
- function renderRefsType(refs, refsType, generateTarget = GenerateTarget.jay) {
14285
+ function renderRefsType(refs, refsType, generateTarget = GenerateTarget.jay, emitComponentRefHelpers = true, emittedHelpers) {
14140
14286
  let renderedRefs;
14141
14287
  let imports = Imports.none();
14142
14288
  const componentRefs = /* @__PURE__ */ new Map();
@@ -14193,7 +14339,7 @@ function renderRefsType(refs, refsType, generateTarget = GenerateTarget.jay) {
14193
14339
  }
14194
14340
  return `${indent.curr}${ref.ref}: ${referenceType}`;
14195
14341
  }).join(",\n");
14196
- const childTypes = Object.entries(refsTree.children).filter(([_, childRefNode]) => {
14342
+ const childTypes = Object.entries(refsTree.children).filter(([, childRefNode]) => {
14197
14343
  return childRefNode.imported || hasRefs(childRefNode, false);
14198
14344
  }).map(([childName, childRefNode]) => {
14199
14345
  if (childRefNode.imported) {
@@ -14213,20 +14359,28 @@ ${allTypes}
14213
14359
  ${indent.lastLine}}`;
14214
14360
  };
14215
14361
  const mainType = generateTypeForPath(refs, new Indent("", true, true));
14216
- const renderedComponentRefs = [...componentRefs].map(([componentName, refsNeeded]) => {
14362
+ const renderedComponentRefs = emitComponentRefHelpers ? [...componentRefs].map(([componentName, refsNeeded]) => {
14217
14363
  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
- `;
14364
+ const refId = `${componentName}Ref`;
14365
+ const refsId = `${componentName}Refs`;
14366
+ const parts = [];
14367
+ if (!emittedHelpers?.has(refId)) {
14368
+ parts.push(
14369
+ `export type ${refId}<ParentVS> = MapEventEmitterViewState<ParentVS, ${elementType}>;`
14370
+ );
14371
+ imports = imports.plus(Import.MapEventEmitterViewState);
14372
+ emittedHelpers?.add(refId);
14373
+ }
14374
+ if (refsNeeded === 1 && !emittedHelpers?.has(refsId)) {
14375
+ parts.push(`export type ${refsId}<ParentVS> =
14376
+ ComponentCollectionProxy<ParentVS, ${refId}<ParentVS>> &
14377
+ OnlyEventEmitters<${refId}<ParentVS>>
14378
+ `);
14226
14379
  imports = imports.plus(Import.ComponentCollectionProxy).plus(Import.OnlyEventEmitters);
14380
+ emittedHelpers?.add(refsId);
14227
14381
  }
14228
- return refTypes;
14229
- });
14382
+ return parts.join("\n");
14383
+ }).filter(Boolean) : [];
14230
14384
  renderedRefs = `${renderedComponentRefs.join("\n")}
14231
14385
  export interface ${refsType} ${mainType}`;
14232
14386
  } else renderedRefs = `export interface ${refsType} {}`;
@@ -14241,16 +14395,6 @@ class RefNameGenerator {
14241
14395
  newAutoRefNameGenerator() {
14242
14396
  return "aR" + this.nextId++;
14243
14397
  }
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
14398
  newConstantName(refName, variables) {
14255
14399
  let suffix = 2;
14256
14400
  let constName = camelCase$1(`ref ${refName}`);
@@ -14528,6 +14672,8 @@ const REFERENCE_MANAGER_TYPES = {
14528
14672
  function renderReferenceManager(refs, target) {
14529
14673
  const { referenceManagerInit, imports } = REFERENCE_MANAGER_TYPES[target];
14530
14674
  const options = target === 0 ? "options, " : "";
14675
+ let usesComponentRefsImpl = false;
14676
+ let usesComponentCollectionRefsImpl = false;
14531
14677
  const usedRefManagerNames = /* @__PURE__ */ new Set();
14532
14678
  const usedRefConstNames = /* @__PURE__ */ new Set();
14533
14679
  const collectReservedConstNames = (refsTree) => {
@@ -14587,11 +14733,15 @@ function renderReferenceManager(refs, target) {
14587
14733
  const elemCollectionRefsDeclarations = elemCollectionRefs.map((ref) => `'${ref.ref}'`).join(", ");
14588
14734
  const compRefsDeclarations = compRefs.map((ref) => `'${ref.ref}'`).join(", ");
14589
14735
  const compCollectionRefsDeclarations = compCollectionRefs.map((ref) => `'${ref.ref}'`).join(", ");
14736
+ const elemRefVars = elemRefs.map((ref) => getUniqueRefConstName(ref));
14737
+ const elemCollectionRefVars = elemCollectionRefs.map((ref) => getUniqueRefConstName(ref));
14738
+ const compRefVars = compRefs.map((ref) => getUniqueRefConstName(ref));
14739
+ const compCollectionRefVars = compCollectionRefs.map((ref) => getUniqueRefConstName(ref));
14590
14740
  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))
14741
+ ...elemRefVars,
14742
+ ...elemCollectionRefVars,
14743
+ ...compRefVars,
14744
+ ...compCollectionRefVars
14595
14745
  ].join(", ");
14596
14746
  const childRenderedRefManagers = [];
14597
14747
  const childRefManagerMembers = [];
@@ -14606,11 +14756,38 @@ function renderReferenceManager(refs, target) {
14606
14756
  }` : "";
14607
14757
  const renderedRefManager = ` const [${name}, [${refVariables}]] =
14608
14758
  ${referenceManagerInit}(${options}[${elemRefsDeclarations}], [${elemCollectionRefsDeclarations}], [${compRefsDeclarations}], [${compCollectionRefsDeclarations}]${childRefManager});`;
14609
- return [...childRenderedRefManagers, renderedRefManager].join("\n");
14759
+ const emitFreeRefManager = (ref, constName, regionIsCollection) => {
14760
+ const freeMgrName = `${constName}FreeRefManager`;
14761
+ const asCollection = (free) => regionIsCollection || free.repeated;
14762
+ const elementNames = ref.freeRefs.filter((free) => !asCollection(free)).map((free) => `'${free.name}'`).join(", ");
14763
+ const collectionNames = ref.freeRefs.filter((free) => asCollection(free)).map((free) => `'${free.name}'`).join(", ");
14764
+ const forCall = `${referenceManagerInit}(${options}[${elementNames}], [${collectionNames}], [], [])`;
14765
+ if (regionIsCollection) {
14766
+ usesComponentCollectionRefsImpl = true;
14767
+ return ` const [${freeMgrName}] = ${forCall};
14768
+ (${name}.get('${ref.ref}') as ComponentCollectionRefImpl<any, any>).setFreeRefManager(${freeMgrName});`;
14769
+ } else {
14770
+ usesComponentRefsImpl = true;
14771
+ return ` const [${freeMgrName}] = ${forCall};
14772
+ (${name}.get('${ref.ref}') as ComponentRefsImpl<any, any>).setFreeRefManager(${freeMgrName});`;
14773
+ }
14774
+ };
14775
+ const freeRefManagerLines = target === 0 ? [
14776
+ ...compRefs.map((ref, i) => ({ ref, constName: compRefVars[i] })).filter(({ ref }) => ref.freeRefs && ref.freeRefs.length > 0).map(({ ref, constName }) => emitFreeRefManager(ref, constName, false)),
14777
+ ...compCollectionRefs.map((ref, i) => ({ ref, constName: compCollectionRefVars[i] })).filter(({ ref }) => ref.freeRefs && ref.freeRefs.length > 0).map(({ ref, constName }) => emitFreeRefManager(ref, constName, true))
14778
+ ] : [];
14779
+ return [...childRenderedRefManagers, renderedRefManager, ...freeRefManagerLines].join("\n");
14610
14780
  };
14611
14781
  if (hasRefs(refs, true)) {
14612
14782
  const renderedRefsManager = renderRefManagerNode("refManager", refs);
14613
- return { renderedRefsManager, refsManagerImport: imports };
14783
+ let refsManagerImport = imports;
14784
+ if (usesComponentRefsImpl)
14785
+ refsManagerImport = refsManagerImport.plus(Imports.for(Import.ComponentRefsImpl));
14786
+ if (usesComponentCollectionRefsImpl)
14787
+ refsManagerImport = refsManagerImport.plus(
14788
+ Imports.for(Import.ComponentCollectionRefImpl)
14789
+ );
14790
+ return { renderedRefsManager, refsManagerImport };
14614
14791
  } else {
14615
14792
  const renderedRefsManager = `const [refManager, []] = ${referenceManagerInit}(${options}[], [], [], []);`;
14616
14793
  return { renderedRefsManager, refsManagerImport: imports };
@@ -14618,16 +14795,27 @@ function renderReferenceManager(refs, target) {
14618
14795
  }
14619
14796
  function renderImports$1(imports, importsFor, componentImports, importerMode) {
14620
14797
  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");
14798
+ const mergedByModule = /* @__PURE__ */ new Map();
14799
+ for (const importStatement of componentImports) {
14800
+ const key = `${importStatement.sandbox ? "sandbox:" : ""}${importStatement.module}`;
14801
+ let entry = mergedByModule.get(key);
14802
+ if (!entry) {
14803
+ entry = { link: { ...importStatement, names: [] }, symbols: /* @__PURE__ */ new Set() };
14804
+ mergedByModule.set(key, entry);
14805
+ }
14806
+ for (const symbol of importStatement.names) {
14807
+ const rendered = symbol.as ? `${symbol.name} as ${symbol.as}` : symbol.name;
14808
+ if (entry.symbols.has(rendered)) continue;
14809
+ entry.symbols.add(rendered);
14810
+ entry.link.names.push(symbol);
14811
+ }
14812
+ }
14813
+ let renderedComponentImports = [...mergedByModule.values()].map(({ link }) => {
14814
+ let symbols = link.names.map((symbol) => symbol.as ? `${symbol.name} as ${symbol.as}` : symbol.name).join(", ");
14815
+ return `import {${symbols}} from "${link.module}${getModeFileExtension(
14816
+ link.sandbox,
14817
+ importerMode
14818
+ )}";`;
14631
14819
  });
14632
14820
  return [runtimeImport, ...renderedComponentImports].join("\n");
14633
14821
  }
@@ -14837,6 +15025,12 @@ const DIRECTIVE_ATTRIBUTES = /* @__PURE__ */ new Set([
14837
15025
  "slow",
14838
15026
  "jay-coordinate-base",
14839
15027
  "jay-scope",
15028
+ // Component provenance marker stamped by the parser on `<jay:Name>` usage tags (DL#123 discovery).
15029
+ // Read by the coordinate/discovery pipeline, never a component prop or a rendered attribute.
15030
+ "jc",
15031
+ // DL#202 — template-side content-slot marker. A pure build-time authoring directive read by the
15032
+ // drift/sync differ; it rides into the flattened page copy (verbatim flatten) but must never render.
15033
+ "jay-content",
14840
15034
  AsyncDirectiveTypes.loading.directive,
14841
15035
  AsyncDirectiveTypes.resolved.directive,
14842
15036
  AsyncDirectiveTypes.rejected.directive
@@ -18960,7 +19154,7 @@ function dedentYaml(text2) {
18960
19154
  const minIndent = Math.min(...lines.map((l) => l.match(/^\s*/)?.[0].length ?? 0));
18961
19155
  return lines.map((l) => l.slice(minIndent)).join("\n");
18962
19156
  }
18963
- async function parseHeadlessImports(elements, validations, filePath, importResolver, projectRoot) {
19157
+ async function parseHeadlessImports(elements, validations, filePath, importResolver, projectRoot, options) {
18964
19158
  const result = [];
18965
19159
  for await (const element of elements) {
18966
19160
  const pluginAttr = element.getAttribute("plugin");
@@ -18972,10 +19166,16 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
18972
19166
  );
18973
19167
  continue;
18974
19168
  }
18975
- if (!pluginAttr) {
18976
- validations.push("headless import must specify plugin attribute");
19169
+ const asAttr = element.getAttribute("as") ?? void 0;
19170
+ if (asAttr && !/^[a-z][a-z0-9-]*$/.test(asAttr)) {
19171
+ validations.push(
19172
+ `Headless component as="${asAttr}" is not a valid region tag name. Use kebab-case (e.g., as="feature-card").`
19173
+ );
18977
19174
  continue;
18978
19175
  }
19176
+ const srcAttr = element.getAttribute("src") ?? void 0;
19177
+ const templateAttr = element.getAttribute("template") ?? void 0;
19178
+ const suppressedValidations = (element.getAttribute("jay-validations") ?? "").split(/[\s,]+/).map((s) => s.trim().toUpperCase()).filter(Boolean);
18979
19179
  if (!contractAttr) {
18980
19180
  validations.push("headless import must specify contract attribute");
18981
19181
  continue;
@@ -18997,41 +19197,75 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
18997
19197
  );
18998
19198
  }
18999
19199
  }
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;
19200
+ let loadedContract;
19201
+ let contractFile;
19202
+ let contractMetadata = void 0;
19013
19203
  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
- }
19204
+ let name;
19205
+ let structural = false;
19206
+ let contractTagName = contractAttr;
19022
19207
  try {
19023
- const contractResult = importResolver.loadPluginContract(
19024
- pluginAttr,
19025
- contractAttr,
19026
- projectRoot
19027
- );
19028
- validations.push(...contractResult.validations);
19029
- if (!contractResult.val) {
19030
- continue;
19208
+ if (pluginAttr) {
19209
+ const resolveResult = importResolver.resolvePluginComponent(
19210
+ pluginAttr,
19211
+ contractAttr,
19212
+ projectRoot
19213
+ );
19214
+ validations.push(...resolveResult.validations);
19215
+ if (!resolveResult.val) {
19216
+ continue;
19217
+ }
19218
+ const absoluteComponentPath = resolveResult.val.componentPath;
19219
+ name = resolveResult.val.componentName;
19220
+ const isNpmPackage = resolveResult.val.isNpmPackage;
19221
+ const packageName = resolveResult.val.packageName;
19222
+ if (isNpmPackage && packageName) {
19223
+ module = packageName;
19224
+ } else {
19225
+ module = path.relative(filePath, absoluteComponentPath);
19226
+ if (!module.startsWith(".")) module = "./" + module;
19227
+ }
19228
+ const contractResult = importResolver.loadPluginContract(
19229
+ pluginAttr,
19230
+ contractAttr,
19231
+ projectRoot
19232
+ );
19233
+ validations.push(...contractResult.validations);
19234
+ if (!contractResult.val) {
19235
+ continue;
19236
+ }
19237
+ loadedContract = contractResult.val.contract;
19238
+ contractFile = contractResult.val.contractPath;
19239
+ contractMetadata = contractResult.val.metadata;
19240
+ } else {
19241
+ const contractPath = path.resolve(filePath, contractAttr);
19242
+ const contractResult = importResolver.loadContract(contractPath);
19243
+ validations.push(...contractResult.validations);
19244
+ if (!contractResult.val) {
19245
+ continue;
19246
+ }
19247
+ loadedContract = contractResult.val;
19248
+ contractFile = contractPath;
19249
+ if (srcAttr) {
19250
+ const importedFile = importResolver.resolveLink(filePath, srcAttr);
19251
+ const exportedComponents = importResolver.analyzeExportedTypes(importedFile, options).filter((t) => t instanceof JayComponentType);
19252
+ if (exportedComponents.length !== 1) {
19253
+ validations.push(
19254
+ `headless import src="${srcAttr}" must export exactly one Jay component (found ${exportedComponents.length})`
19255
+ );
19256
+ continue;
19257
+ }
19258
+ name = exportedComponents[0].name;
19259
+ module = path.relative(filePath, importedFile);
19260
+ if (!module.startsWith(".")) module = "./" + module;
19261
+ } else {
19262
+ structural = true;
19263
+ name = pascalCase(loadedContract.name);
19264
+ module = "";
19265
+ }
19266
+ contractTagName = paramCase(loadedContract.name);
19031
19267
  }
19032
- const loadedContract = contractResult.val.contract;
19033
- const contractFile = contractResult.val.contractPath;
19034
- const contractMetadata = contractResult.val.metadata;
19268
+ if (asAttr) contractTagName = asAttr;
19035
19269
  const contractTypes = await contractToImportsViewStateAndRefs(
19036
19270
  loadedContract,
19037
19271
  contractFile,
@@ -19048,13 +19282,17 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
19048
19282
  refsTypeName,
19049
19283
  repeatedRefsTypeName
19050
19284
  );
19285
+ const toRelativeModule = (to) => {
19286
+ const relative = path.relative(filePath, to);
19287
+ return relative.startsWith(".") ? relative : "./" + relative;
19288
+ };
19051
19289
  const enumsToImportRelativeToJayHtml = enumsToImport.map(
19052
19290
  (enumsToImport2) => ({
19053
19291
  type: enumsToImport2.type,
19054
- declaringModule: path.relative(filePath, enumsToImport2.declaringModule)
19292
+ declaringModule: toRelativeModule(enumsToImport2.declaringModule)
19055
19293
  })
19056
19294
  );
19057
- const relativeContractPath = path.relative(filePath, contractFile);
19295
+ const relativeContractPath = toRelativeModule(contractFile);
19058
19296
  const enumsFromContract = enumsToImportRelativeToJayHtml.filter((_) => _.declaringModule === relativeContractPath).map((_) => _.type);
19059
19297
  const nestedTypeNames = collectNestedTypeNames(type2);
19060
19298
  const nestedTypeImports = nestedTypeNames.filter((name2) => name2 !== type2.name).map((name2) => ({ name: name2, type: JayUnknown }));
@@ -19098,7 +19336,7 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
19098
19336
  };
19099
19337
  result.push({
19100
19338
  ...key && { key },
19101
- contractName: contractAttr,
19339
+ contractName: contractTagName,
19102
19340
  refs,
19103
19341
  rootType: type2,
19104
19342
  contractLinks,
@@ -19106,355 +19344,28 @@ async function parseHeadlessImports(elements, validations, filePath, importResol
19106
19344
  contract: loadedContract,
19107
19345
  contractPath: contractFile,
19108
19346
  metadata: contractMetadata,
19109
- headlessProps
19347
+ headlessProps,
19348
+ ...structural && { structural },
19349
+ ...templateAttr && { template: templateAttr },
19350
+ ...suppressedValidations.length && { suppressedValidations }
19110
19351
  });
19111
19352
  });
19112
19353
  } catch (e2) {
19113
19354
  validations.push(`failed to parse linked contract - ${e2.message}${e2.stack}`);
19114
19355
  }
19115
19356
  }
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
- }
19357
+ const tagCounts = /* @__PURE__ */ new Map();
19358
+ for (const imp of result) {
19359
+ tagCounts.set(imp.contractName, (tagCounts.get(imp.contractName) ?? 0) + 1);
19163
19360
  }
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) {
19361
+ for (const [tag, count] of tagCounts) {
19362
+ if (count > 1) {
19447
19363
  validations.push(
19448
- `failed to parse contract for headfull FS component ${src} - ${e2.message}${e2.stack}`
19364
+ `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
19365
  );
19450
19366
  }
19451
19367
  }
19452
- return {
19453
- headlessImports,
19454
- css: cssParts.length > 0 ? cssParts.join("\n\n") : void 0,
19455
- linkedCssFiles,
19456
- linkedComponentFiles
19457
- };
19368
+ return result;
19458
19369
  }
19459
19370
  function normalizeFilename(filename) {
19460
19371
  return filename.replace(".jay-html", "");
@@ -19588,7 +19499,7 @@ function resolveNestedCssImports(css, cssDir, visited) {
19588
19499
  if (seen.has(resolved)) return `/* circular import: ${importPath} */`;
19589
19500
  seen.add(resolved);
19590
19501
  try {
19591
- const imported = fs.readFileSync(resolved, "utf-8");
19502
+ const imported = fs$1.readFileSync(resolved, "utf-8");
19592
19503
  return `/* @import ${importPath} */
19593
19504
  ${resolveNestedCssImports(imported, path.dirname(resolved), seen)}`;
19594
19505
  } catch {
@@ -19610,9 +19521,8 @@ async function extractCss(root, filePath, skipPaths) {
19610
19521
  if (filePath) {
19611
19522
  const cssFilePath = path.resolve(filePath, href);
19612
19523
  linkedCssFiles.push(cssFilePath);
19613
- if (skipPaths?.has(cssFilePath)) continue;
19614
19524
  try {
19615
- const cssContent = await fs$1.readFile(cssFilePath, "utf-8");
19525
+ const cssContent = await fs.readFile(cssFilePath, "utf-8");
19616
19526
  const resolvedCss = resolveNestedCssImports(
19617
19527
  cssContent,
19618
19528
  path.dirname(cssFilePath)
@@ -19707,10 +19617,16 @@ async function parseJayFile(html2, filename, filePath, options, linkedContractRe
19707
19617
  const { val: jayYaml, validations } = parseYaml(root);
19708
19618
  if (validations.length > 0) return new WithValidations(void 0, validations);
19709
19619
  const allHeadfullElements = root.querySelectorAll('script[type="application/jay-headfull"]');
19620
+ for (const el of allHeadfullElements) {
19621
+ if (el.getAttribute("contract")) {
19622
+ validations.push(
19623
+ `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\`).`
19624
+ );
19625
+ }
19626
+ }
19710
19627
  const regularHeadfullElements = allHeadfullElements.filter(
19711
19628
  (el) => !el.getAttribute("contract")
19712
19629
  );
19713
- const fsHeadfullElements = allHeadfullElements.filter((el) => el.getAttribute("contract"));
19714
19630
  const headfullImports = parseHeadfullImports(
19715
19631
  regularHeadfullElements,
19716
19632
  validations,
@@ -19723,33 +19639,15 @@ async function parseJayFile(html2, filename, filePath, options, linkedContractRe
19723
19639
  validations.push(`jay file must have exactly a body tag`);
19724
19640
  return new WithValidations(void 0, validations);
19725
19641
  }
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
19642
  const headlessImports = await parseHeadlessImports(
19746
19643
  root.querySelectorAll('script[type="application/jay-headless"]'),
19747
19644
  validations,
19748
19645
  filePath,
19749
19646
  linkedContractResolver,
19750
- projectRoot
19647
+ projectRoot,
19648
+ options
19751
19649
  );
19752
- const allHeadlessImports = [...headlessImports, ...headfullFSResult.headlessImports];
19650
+ const allHeadlessImports = [...headlessImports];
19753
19651
  resolveEnumCollisions(allHeadlessImports);
19754
19652
  const importNames = headfullImports.flatMap((_) => _.names);
19755
19653
  const types2 = await parseTypes(
@@ -19780,17 +19678,11 @@ async function parseJayFile(html2, filename, filePath, options, linkedContractRe
19780
19678
  validations.push(...cssResult.validations);
19781
19679
  if (validations.length > 0) return new WithValidations(void 0, validations);
19782
19680
  let css = cssResult.val?.css;
19783
- if (headfullFSResult.css) {
19784
- css = css ? css + "\n\n" + headfullFSResult.css : headfullFSResult.css;
19785
- }
19786
19681
  if (css) {
19787
19682
  detectCssNameCollisions(css, validations);
19788
19683
  }
19789
- let allLinkedCssFiles = cssResult.val?.linkedCssFiles || [];
19790
- if (headfullFSResult.linkedCssFiles.length > 0) {
19791
- allLinkedCssFiles = [...allLinkedCssFiles, ...headfullFSResult.linkedCssFiles];
19792
- }
19793
- const allLinkedComponentFiles = headfullFSResult.linkedComponentFiles;
19684
+ const allLinkedCssFiles = cssResult.val?.linkedCssFiles || [];
19685
+ const allLinkedComponentFiles = [];
19794
19686
  const { serverTrackByMap, clientTrackByMap } = extractTrackByMaps(
19795
19687
  jayYaml.parsedContract,
19796
19688
  allHeadlessImports
@@ -20117,7 +20009,15 @@ function generateRefsInterface(contract, refs) {
20117
20009
  ${repeatedRefsRendered}`;
20118
20010
  return { imports: imports.plus(imports2), renderedRefs };
20119
20011
  }
20120
- function generatePropsInterface(contractName, props) {
20012
+ function collectViewStateEnumNames(type2, acc = /* @__PURE__ */ new Set()) {
20013
+ if (isEnumType(type2)) acc.add(type2.name);
20014
+ else if (isObjectType$1(type2))
20015
+ for (const key of Object.keys(type2.props)) collectViewStateEnumNames(type2.props[key], acc);
20016
+ else if (isArrayType$1(type2) || isPromiseType(type2))
20017
+ collectViewStateEnumNames(type2.itemType, acc);
20018
+ return acc;
20019
+ }
20020
+ function generatePropsInterface(contractName, props, existingEnumNames = /* @__PURE__ */ new Set()) {
20121
20021
  const propsTypeName = `${contractName}Props`;
20122
20022
  const enums = [];
20123
20023
  if (props.length === 0) {
@@ -20129,10 +20029,13 @@ function generatePropsInterface(contractName, props) {
20129
20029
  let typeName;
20130
20030
  if (isEnumType(prop.dataType)) {
20131
20031
  typeName = prop.dataType.name;
20132
- const genEnum = `export enum ${prop.dataType.name} {
20032
+ if (!existingEnumNames.has(prop.dataType.name)) {
20033
+ const genEnum = `export enum ${prop.dataType.name} {
20133
20034
  ${prop.dataType.values.map((_) => " " + _).join(",\n")}
20134
20035
  }`;
20135
- enums.push(genEnum);
20036
+ enums.push(genEnum);
20037
+ existingEnumNames.add(prop.dataType.name);
20038
+ }
20136
20039
  } else if (isAtomicType(prop.dataType)) {
20137
20040
  typeName = prop.dataType.name;
20138
20041
  } else {
@@ -20198,9 +20101,11 @@ async function compileContract(contractWithValidations, contractFilePath, jayImp
20198
20101
  const propsTypeName = `${contractName}Props`;
20199
20102
  let renderedProps = "";
20200
20103
  if (hasProps) {
20104
+ const existingEnumNames = collectViewStateEnumNames(type2);
20201
20105
  const { propsInterface, propsEnums } = generatePropsInterface(
20202
20106
  contractName,
20203
- contract.props
20107
+ contract.props,
20108
+ existingEnumNames
20204
20109
  );
20205
20110
  renderedProps = propsEnums ? `${propsEnums}
20206
20111
 
@@ -20876,10 +20781,13 @@ ${indent.firstLine} }`;
20876
20781
  allImports = allImports.plus(Import.adoptElement);
20877
20782
  }
20878
20783
  const itemRootCoordForEach = element.getAttribute(COORD_ATTR) || "0";
20784
+ const bodyParentDepth = Math.max(itemContent.parentDepth, createChildren.parentDepth);
20785
+ const dependsOnParent = bodyParentDepth > 0;
20879
20786
  const hydrateForEachFragment = new RenderFragment(
20880
20787
  `${indent.firstLine}hydrateForEach(${forEachFragment.rendered}, '${trackBy}', '${itemRootCoordForEach}',
20881
20788
  ${indent.firstLine} ${adoptBody},
20882
- ${indent.firstLine} ${createBody},
20789
+ ${indent.firstLine} ${createBody},${dependsOnParent ? `
20790
+ ${indent.firstLine} true,` : ""}
20883
20791
  ${indent.firstLine})`,
20884
20792
  allImports,
20885
20793
  [
@@ -20887,7 +20795,9 @@ ${indent.firstLine})`,
20887
20795
  ...itemContent.validations,
20888
20796
  ...createChildren.validations
20889
20797
  ],
20890
- mergeRefsTrees(itemContent.refs, itemRefFragment.refs)
20798
+ mergeRefsTrees(itemContent.refs, itemRefFragment.refs),
20799
+ [],
20800
+ Math.max(forEachFragment.parentDepth, Math.max(0, bodyParentDepth - 1))
20891
20801
  );
20892
20802
  return nestRefs(forEachAccessor.terms, hydrateForEachFragment);
20893
20803
  }
@@ -20937,7 +20847,7 @@ function renderHydrateHeadlessInstance(element, context, renderContext, contract
20937
20847
  } else {
20938
20848
  coordinateKey2 = instanceCoord;
20939
20849
  }
20940
- const componentVariables = new Variables(headlessImport.rootType);
20850
+ const componentVariables = new Variables(headlessImport.rootType, renderContext.variables);
20941
20851
  const childNodes = filterContentNodes(element.childNodes);
20942
20852
  if (childNodes.length === 0) {
20943
20853
  return new RenderFragment("", Imports.none(), [
@@ -20987,14 +20897,50 @@ ${adoptChildren.rendered}
20987
20897
  ${adoptChildIndent.firstLine}])`,
20988
20898
  adoptChildren.imports.plus(Import.adoptElement),
20989
20899
  adoptChildren.validations,
20990
- adoptChildren.refs
20900
+ adoptChildren.refs,
20901
+ adoptChildren.recursiveRegions,
20902
+ // DL#193 Capability A: preserve the deepest `$parent` climb so the usage site
20903
+ // still weakens gates when instance content spans multiple root children.
20904
+ adoptChildren.parentDepth
20991
20905
  );
20992
20906
  }
20993
- const adoptMergedRefs = mergeContractStubRefs(adoptInlineBody.refs, headlessImport.refs);
20907
+ const contractRefNames = /* @__PURE__ */ new Set();
20908
+ const collectContractRefNames = (tree) => {
20909
+ for (const r of tree.refs) contractRefNames.add(camelCase$1(r.ref));
20910
+ for (const child of Object.values(tree.children)) collectContractRefNames(child);
20911
+ };
20912
+ collectContractRefNames(headlessImport.refs);
20913
+ const freeRefDecls = [];
20914
+ const seenFreeRefNames = /* @__PURE__ */ new Set();
20915
+ const collectFreeRefs = (tree) => {
20916
+ for (const r of tree.refs) {
20917
+ if (isComponentType$1(r.elementType)) continue;
20918
+ if (contractRefNames.has(camelCase$1(r.ref))) continue;
20919
+ if (seenFreeRefNames.has(r.ref)) continue;
20920
+ seenFreeRefNames.add(r.ref);
20921
+ freeRefDecls.push({
20922
+ name: r.ref,
20923
+ repeated: r.repeated,
20924
+ viewStateType: r.viewStateType.name,
20925
+ elementType: r.elementType.name
20926
+ });
20927
+ }
20928
+ for (const child of Object.values(tree.children)) collectFreeRefs(child);
20929
+ };
20930
+ collectFreeRefs(adoptInlineBody.refs);
20931
+ const freeRefNames = freeRefDecls.map((f) => f.name);
20932
+ const freeRefsArg = freeRefNames.length > 0 ? `
20933
+ [${freeRefNames.map((r) => `'${r}'`).join(", ")}],` : "";
20934
+ const adoptMergedRefs = hoistFreeRefs(
20935
+ mergeContractStubRefs(adoptInlineBody.refs, headlessImport.refs),
20936
+ new Set(freeRefNames)
20937
+ );
20994
20938
  const { renderedRefsManager, refsManagerImport } = renderReferenceManager(
20995
20939
  adoptMergedRefs,
20996
20940
  ReferenceManagerTarget.element
20997
20941
  );
20942
+ const refOriginalName = element.attributes.ref || context.refNameGenerator.newAutoRefNameGenerator();
20943
+ const refRefName = camelCase$1(refOriginalName);
20998
20944
  const adoptRenderFnCode = `
20999
20945
  // Hydrate inline template for headless component: ${contractName} #${idx}
21000
20946
  type ${elementType} = JayElement<${interactiveViewStateType}, ${refsTypeName}>;
@@ -21011,22 +20957,31 @@ ${adoptInlineBody.rendered}
21011
20957
  }`;
21012
20958
  let adoptComponentSymbol;
21013
20959
  let adoptComponentDef;
21014
- if (isInsideForEach) {
20960
+ const coordinateArg = isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`;
20961
+ if (headlessImport.structural) {
20962
+ adoptComponentSymbol = `_Headless${pascal}${idx}`;
20963
+ adoptComponentDef = `const ${adoptComponentSymbol} = makePassthroughHeadlessInstanceComponent(
20964
+ ${renderFnName},
20965
+ ${coordinateArg},${freeRefsArg}
20966
+ );`;
20967
+ } else if (isInsideForEach) {
21015
20968
  adoptComponentSymbol = `_Headless${pascal}${idx}Adopt`;
21016
20969
  adoptComponentDef = `const ${adoptComponentSymbol} = makeHeadlessInstanceComponent(
21017
20970
  ${renderFnName},
21018
20971
  ${pluginComponentName},
21019
- (dataIds) => [...dataIds, '${coordinateSuffix}'].toString(),
20972
+ (dataIds) => [...dataIds, '${coordinateSuffix}'].toString(),${freeRefsArg}
21020
20973
  );`;
21021
20974
  } else {
21022
20975
  adoptComponentSymbol = `_Headless${pascal}${idx}`;
21023
20976
  adoptComponentDef = `const ${adoptComponentSymbol} = makeHeadlessInstanceComponent(
21024
20977
  ${renderFnName},
21025
20978
  ${pluginComponentName},
21026
- '${coordinateKey2}',
20979
+ '${coordinateKey2}',${freeRefsArg}
21027
20980
  );`;
21028
20981
  }
21029
- let adoptImports = adoptInlineBody.imports.plus(refsManagerImport).plus(Import.ConstructContext).plus(Import.makeHeadlessInstanceComponent);
20982
+ let adoptImports = adoptInlineBody.imports.plus(refsManagerImport).plus(Import.ConstructContext).plus(
20983
+ headlessImport.structural ? Import.makePassthroughHeadlessInstanceComponent : Import.makeHeadlessInstanceComponent
20984
+ );
21030
20985
  const ifCondition = element.attributes.if;
21031
20986
  const needsCreateVersion = !!ifCondition;
21032
20987
  let createComponentSymbol;
@@ -21067,7 +21022,10 @@ ${createRenderedChildren.rendered}
21067
21022
  } else {
21068
21023
  createInlineBody = createRenderedChildren;
21069
21024
  }
21070
- const createMergedRefs = mergeContractStubRefs(createInlineBody.refs, headlessImport.refs);
21025
+ const createMergedRefs = hoistFreeRefs(
21026
+ mergeContractStubRefs(createInlineBody.refs, headlessImport.refs),
21027
+ new Set(freeRefNames)
21028
+ );
21071
21029
  const { renderedRefsManager: createRefsManager, refsManagerImport: createRefsImport } = renderReferenceManager(createMergedRefs, ReferenceManagerTarget.element);
21072
21030
  createRenderFnCode = `
21073
21031
  function ${createRenderFnName}(options?: RenderElementOptions): ${preRenderType} {
@@ -21079,10 +21037,10 @@ ${createInlineBody.rendered}
21079
21037
  return [refManager.getPublicAPI() as ${refsTypeName}, render];
21080
21038
  }
21081
21039
 
21082
- const ${createComponentSymbol} = makeHeadlessInstanceComponent(
21083
- ${createRenderFnName},
21084
- ${pluginComponentName},
21085
- ${isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`},
21040
+ const ${createComponentSymbol} = ${headlessImport.structural ? "makePassthroughHeadlessInstanceComponent" : "makeHeadlessInstanceComponent"}(
21041
+ ${createRenderFnName},${headlessImport.structural ? "" : `
21042
+ ${pluginComponentName},`}
21043
+ ${isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`},${freeRefsArg}
21086
21044
  );`;
21087
21045
  createImports = createInlineBody.imports.plus(createRefsImport);
21088
21046
  }
@@ -21090,7 +21048,7 @@ const ${createComponentSymbol} = makeHeadlessInstanceComponent(
21090
21048
  componentSymbol: adoptComponentSymbol,
21091
21049
  renderFnName,
21092
21050
  renderFnCode: adoptRenderFnCode + "\n" + adoptComponentDef + (createRenderFnCode ? "\n" + createRenderFnCode : ""),
21093
- pluginComponentName,
21051
+ pluginComponentName: headlessImport.structural ? void 0 : pluginComponentName,
21094
21052
  imports: adoptImports.plus(createImports)
21095
21053
  });
21096
21054
  const propsGetterAndRefs = renderChildCompProps$1(
@@ -21099,8 +21057,6 @@ const ${createComponentSymbol} = makeHeadlessInstanceComponent(
21099
21057
  headlessImport.contract?.props
21100
21058
  );
21101
21059
  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
21060
  const refConstName = context.refNameGenerator.newConstantName(refRefName, context.variables);
21105
21061
  const isRepeated = context.dynamicRef;
21106
21062
  const contractRefType = isRepeated ? `${pascal}RepeatedRefs` : `${pascal}Refs`;
@@ -21109,6 +21065,22 @@ const ${createComponentSymbol} = makeHeadlessInstanceComponent(
21109
21065
  link.names.push({ name: contractRefType, type: JayUnknown });
21110
21066
  }
21111
21067
  }
21068
+ const augmented = freeRefsAugmentedRefType(contractRefType, freeRefDecls, isRepeated);
21069
+ if (freeRefDecls.length > 0) {
21070
+ for (const f of freeRefDecls) {
21071
+ if (!f.viewStateType) continue;
21072
+ for (const link of headlessImport.contractLinks) {
21073
+ if (!link.names.some((n) => n.name === f.viewStateType)) {
21074
+ link.names.push({ name: f.viewStateType, type: JayUnknown });
21075
+ }
21076
+ }
21077
+ }
21078
+ }
21079
+ let freeRefProxyImports = Imports.for();
21080
+ if (augmented.needsElementProxy)
21081
+ freeRefProxyImports = freeRefProxyImports.plus(Import.HTMLElementProxy);
21082
+ if (augmented.needsCollectionProxy)
21083
+ freeRefProxyImports = freeRefProxyImports.plus(Import.HTMLElementCollectionProxy);
21112
21084
  const instanceRef = mkRef(
21113
21085
  refRefName,
21114
21086
  refOriginalName,
@@ -21116,25 +21088,25 @@ const ${createComponentSymbol} = makeHeadlessInstanceComponent(
21116
21088
  isRepeated,
21117
21089
  !element.attributes.ref,
21118
21090
  context.variables.currentType,
21119
- new JayTypeAlias(contractRefType)
21120
- );
21121
- let renderedRef = new RenderFragment(
21122
- `${refConstName}()`,
21123
- Imports.for(),
21124
- [],
21125
- mkRefsTree([instanceRef], {})
21091
+ new JayTypeAlias(augmented.type),
21092
+ // DL#198 — declare free refs on the page-side region ref so the page-level renderReferenceManager
21093
+ // emits the per-region FreeReferenceManager (`${refConstName}FreeRefManager`) + setFreeRefManager.
21094
+ freeRefDecls.length > 0 ? freeRefDecls : void 0
21126
21095
  );
21096
+ const instanceRefsTree = mkRefsTree([instanceRef], {});
21097
+ let renderedRef = new RenderFragment(`${refConstName}()`, Imports.for(), [], instanceRefsTree);
21127
21098
  if (renderedRef.rendered !== "") renderedRef = renderedRef.map((_) => ", " + _);
21099
+ const freeRefManagerArg = freeRefNames.length > 0 ? `, ${refConstName}FreeRefManager` : "";
21128
21100
  const scopeRootCoord = childScopeId ? `'${childScopeId}/0'` : "undefined";
21129
21101
  if (ifCondition) {
21130
21102
  const renderedCondition = parseCondition(ifCondition, context.variables);
21131
- const adoptCall = `() => childCompHydrate(${adoptComponentSymbol}, ${getProps}, ${scopeRootCoord}${renderedRef.rendered})`;
21132
- const createCall = `() => childComp(${createComponentSymbol}, ${getProps}${renderedRef.rendered})`;
21103
+ const adoptCall = `() => childCompHydrate(${adoptComponentSymbol}, ${getProps}, ${scopeRootCoord}${renderedRef.rendered}${freeRefManagerArg})`;
21104
+ const createCall = `() => childComp(${createComponentSymbol}, ${getProps}${renderedRef.rendered}${freeRefManagerArg})`;
21133
21105
  const callExpr = `${context.indent.firstLine}hydrateConditional(${renderedCondition.rendered}, ${adoptCall},
21134
21106
  ${context.indent.firstLine} ${createCall})`;
21135
21107
  return new RenderFragment(
21136
21108
  callExpr,
21137
- Imports.for(Import.childCompHydrate, Import.hydrateConditional, Import.childComp).plus(propsGetterAndRefs.imports).plus(renderedRef.imports).plus(renderedCondition.imports),
21109
+ Imports.for(Import.childCompHydrate, Import.hydrateConditional, Import.childComp).plus(propsGetterAndRefs.imports).plus(renderedRef.imports).plus(renderedCondition.imports).plus(freeRefProxyImports),
21138
21110
  [
21139
21111
  ...propsGetterAndRefs.validations,
21140
21112
  ...adoptInlineBody.validations,
@@ -21144,8 +21116,8 @@ ${context.indent.firstLine} ${createCall})`;
21144
21116
  );
21145
21117
  }
21146
21118
  return new RenderFragment(
21147
- `${context.indent.firstLine}childCompHydrate(${adoptComponentSymbol}, ${getProps}, ${scopeRootCoord}${renderedRef.rendered})`,
21148
- Imports.for(Import.childCompHydrate).plus(propsGetterAndRefs.imports).plus(renderedRef.imports),
21119
+ `${context.indent.firstLine}childCompHydrate(${adoptComponentSymbol}, ${getProps}, ${scopeRootCoord}${renderedRef.rendered}${freeRefManagerArg})`,
21120
+ Imports.for(Import.childCompHydrate).plus(propsGetterAndRefs.imports).plus(renderedRef.imports).plus(freeRefProxyImports),
21149
21121
  [
21150
21122
  ...propsGetterAndRefs.validations,
21151
21123
  ...adoptInlineBody.validations,
@@ -21221,6 +21193,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21221
21193
  let childImports = Imports.none();
21222
21194
  const childValidations = [];
21223
21195
  const childRefs = [];
21196
+ let childParentDepth = 0;
21224
21197
  for (const child of childNodes) {
21225
21198
  if (child.nodeType !== NodeType.ELEMENT_NODE) continue;
21226
21199
  const htmlChild = child;
@@ -21234,6 +21207,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21234
21207
  childImports = childImports.plus(frag.imports);
21235
21208
  childValidations.push(...frag.validations);
21236
21209
  if (frag.refs) childRefs.push(frag.refs);
21210
+ childParentDepth = Math.max(childParentDepth, frag.parentDepth);
21237
21211
  }
21238
21212
  } else {
21239
21213
  const htmlChild2 = child;
@@ -21256,6 +21230,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21256
21230
  childImports = childImports.plus(frag.imports);
21257
21231
  childValidations.push(...frag.validations);
21258
21232
  if (frag.refs) childRefs.push(frag.refs);
21233
+ childParentDepth = Math.max(childParentDepth, frag.parentDepth);
21259
21234
  } else {
21260
21235
  childParts.push(`${indent.firstLine}STATIC`);
21261
21236
  childImports = childImports.plus(Import.STATIC);
@@ -21263,12 +21238,16 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21263
21238
  }
21264
21239
  }
21265
21240
  const refSuffix2 = renderedRef.rendered ? `, ${renderedRef.rendered}` : "";
21266
- const childrenArr2 = childParts.length ? `[${childParts.join(",\n")}]` : "[]";
21241
+ const childrenArr2 = childParts.length ? `[
21242
+ ${childParts.join(",\n")}
21243
+ ${indent.firstLine}]` : "[]";
21267
21244
  return new RenderFragment(
21268
21245
  `${indent.firstLine}adoptDynamicElement("${coordinate}", ${attributes2.rendered}, ${childrenArr2}${refSuffix2})`,
21269
21246
  Imports.for(Import.adoptDynamicElement).plus(attributes2.imports).plus(childImports).plus(renderedRef.imports),
21270
21247
  [...attributes2.validations, ...childValidations, ...renderedRef.validations],
21271
- mergeRefsTrees(...childRefs, renderedRef.refs)
21248
+ mergeRefsTrees(...childRefs, renderedRef.refs),
21249
+ [],
21250
+ Math.max(childParentDepth, attributes2.parentDepth)
21272
21251
  );
21273
21252
  }
21274
21253
  if (hasMixedContentDynamicText2 && !hasDynamicAttrs) {
@@ -21276,6 +21255,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21276
21255
  let childImports = Imports.none();
21277
21256
  const childValidations = [];
21278
21257
  const childRefs = [];
21258
+ let childParentDepth = 0;
21279
21259
  childNodes.forEach((child) => {
21280
21260
  if (child.nodeType === NodeType.ELEMENT_NODE) {
21281
21261
  const frag = renderHydrateNode(child, context);
@@ -21284,6 +21264,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21284
21264
  childImports = childImports.plus(frag.imports);
21285
21265
  childValidations.push(...frag.validations);
21286
21266
  if (frag.refs) childRefs.push(frag.refs);
21267
+ childParentDepth = Math.max(childParentDepth, frag.parentDepth);
21287
21268
  }
21288
21269
  }
21289
21270
  });
@@ -21299,6 +21280,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21299
21280
  );
21300
21281
  childImports = childImports.plus(Import.adoptText).plus(rendered.imports.minus(Import.dynamicText));
21301
21282
  childValidations.push(...rendered.validations);
21283
+ childParentDepth = Math.max(childParentDepth, rendered.parentDepth);
21302
21284
  }
21303
21285
  }
21304
21286
  }
@@ -21309,7 +21291,9 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21309
21291
  `${indent.firstLine}adoptElement("${coordinate}", ${attributes2.rendered}, ${childrenArr2}${refSuffix2})`,
21310
21292
  Imports.for(Import.adoptElement).plus(attributes2.imports).plus(childImports).plus(renderedRef.imports),
21311
21293
  [...attributes2.validations, ...childValidations, ...renderedRef.validations],
21312
- mergeRefsTrees(...childRefs, renderedRef.refs)
21294
+ mergeRefsTrees(...childRefs, renderedRef.refs),
21295
+ [],
21296
+ childParentDepth
21313
21297
  );
21314
21298
  }
21315
21299
  if (isHtmlStringChild && textFragment && !hasDynamicAttrs) {
@@ -21319,7 +21303,9 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21319
21303
  `${indent.firstLine}adoptElement("${coordinate}", ${attributes2.rendered}, [dh(${accessor})]${refSuffix2})`,
21320
21304
  Imports.for(Import.adoptElement).plus(Import.dynamicHtml).plus(textFragment.imports.minus(Import.dynamicText)).plus(renderedRef.imports).plus(attributes2.imports),
21321
21305
  [...textFragment.validations, ...renderedRef.validations, ...attributes2.validations],
21322
- renderedRef.refs
21306
+ renderedRef.refs,
21307
+ [],
21308
+ textFragment.parentDepth
21323
21309
  );
21324
21310
  }
21325
21311
  if (textFragment && !hasDynamicAttrs) {
@@ -21334,13 +21320,19 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21334
21320
  ...renderedRef.validations,
21335
21321
  ...attributes2.validations
21336
21322
  ],
21337
- renderedRef.refs
21323
+ renderedRef.refs,
21324
+ [],
21325
+ // DL#193 Capability A: carry the `$parent` climb up so enclosing forEach gates weaken.
21326
+ textFragment.parentDepth
21338
21327
  );
21339
21328
  }
21340
21329
  return new RenderFragment(
21341
21330
  `${indent.firstLine}adoptText("${coordinate}", ${accessor})`,
21342
21331
  Imports.for(Import.adoptText).plus(textFragment.imports.minus(Import.dynamicText)),
21343
- [...textFragment.validations]
21332
+ [...textFragment.validations],
21333
+ mkRefsTree([], {}),
21334
+ [],
21335
+ textFragment.parentDepth
21344
21336
  );
21345
21337
  }
21346
21338
  if (hasDynamicAttrs) {
@@ -21348,6 +21340,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21348
21340
  let childImports = Imports.none();
21349
21341
  let childValidations = [];
21350
21342
  let childRefs;
21343
+ let childParentDepth = 0;
21351
21344
  if (textFragment) {
21352
21345
  const accessor = textFragment.rendered.replace(/^dt\(/, "").replace(/\)$/, "");
21353
21346
  childrenRendered = `[adoptText("${coordinate}", ${accessor})]`;
@@ -21355,6 +21348,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21355
21348
  textFragment.imports.minus(Import.dynamicText)
21356
21349
  );
21357
21350
  childValidations = textFragment.validations;
21351
+ childParentDepth = textFragment.parentDepth;
21358
21352
  } else {
21359
21353
  const children = mergeHydrateFragments(
21360
21354
  childNodes.map((child) => renderHydrateNode(child, context)),
@@ -21365,6 +21359,7 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21365
21359
  childImports = children.imports;
21366
21360
  childValidations = children.validations;
21367
21361
  childRefs = children.refs;
21362
+ childParentDepth = children.parentDepth;
21368
21363
  }
21369
21364
  }
21370
21365
  const refSuffix2 = renderedRef.rendered ? `, ${renderedRef.rendered}` : "";
@@ -21372,7 +21367,11 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21372
21367
  `${indent.firstLine}adoptElement("${coordinate}", ${attributes2.rendered}, ${childrenRendered}${refSuffix2})`,
21373
21368
  Imports.for(Import.adoptElement).plus(attributes2.imports).plus(childImports).plus(renderedRef.imports),
21374
21369
  [...attributes2.validations, ...childValidations, ...renderedRef.validations],
21375
- mergeRefsTrees(...[childRefs, renderedRef.refs].filter(Boolean))
21370
+ mergeRefsTrees(...[childRefs, renderedRef.refs].filter(Boolean)),
21371
+ [],
21372
+ // DL#193 Phase 2a: carry the override binding's `$parent` climb up (attributes may
21373
+ // themselves be `$parent`-bound; a wrapping element adds no scope).
21374
+ Math.max(childParentDepth, attributes2.parentDepth)
21376
21375
  );
21377
21376
  }
21378
21377
  const childFragments = mergeHydrateFragments(
@@ -21385,7 +21384,11 @@ function renderHydrateElementContent(element, context, renderContext, coordinate
21385
21384
  `${indent.firstLine}adoptElement("${coordinate}", {}, ${childrenArr}${refSuffix})`,
21386
21385
  Imports.for(Import.adoptElement).plus(childFragments.imports).plus(renderedRef.imports),
21387
21386
  [...childFragments.validations, ...renderedRef.validations],
21388
- mergeRefsTrees(childFragments.refs, renderedRef.refs)
21387
+ mergeRefsTrees(childFragments.refs, renderedRef.refs),
21388
+ [],
21389
+ // DL#193 Capability A: a wrapping element adds no scope, so carry children's `$parent`
21390
+ // climb up unchanged so enclosing forEach gates weaken.
21391
+ childFragments.parentDepth
21389
21392
  );
21390
21393
  }
21391
21394
  function renderHydrate(types2, body, importStatements, elementType, preRenderType, refsType, headlessImports, contract) {
@@ -21411,7 +21414,9 @@ function renderHydrate(types2, body, importStatements, elementType, preRenderTyp
21411
21414
  };
21412
21415
  const rootElement = ensureSingleChildElement(body);
21413
21416
  if (!rootElement.val) {
21414
- return new RenderFragment("", Imports.none(), rootElement.validations);
21417
+ return {
21418
+ fragment: new RenderFragment("", Imports.none(), rootElement.validations)
21419
+ };
21415
21420
  }
21416
21421
  const rootComponentMatch = getComponentName(
21417
21422
  rootElement.val.rawTagName,
@@ -21437,8 +21442,8 @@ function renderHydrate(types2, body, importStatements, elementType, preRenderTyp
21437
21442
  ReferenceManagerTarget.element
21438
21443
  );
21439
21444
  const hasAdoptCalls = renderedHydrate.rendered.trim().length > 0;
21440
- const hydrateBody = hasAdoptCalls ? `() =>
21441
- ${renderedHydrate.rendered}` : `() => ({ dom: rootElement, update: () => {}, mount: () => {}, unmount: () => {} })`;
21445
+ const hydrateBody = !hasAdoptCalls ? `() => ({ dom: rootElement, update: () => {}, mount: () => {}, unmount: () => {} })` : `() =>
21446
+ ${renderedHydrate.rendered}`;
21442
21447
  const headlessDefsCode = context.headlessInstanceDefs.map((def) => def.renderFnCode).join("\n");
21443
21448
  let headlessImportsAll = Imports.none();
21444
21449
  for (const def of context.headlessInstanceDefs) {
@@ -21453,12 +21458,14 @@ ${renderedRefsManager}
21453
21458
  const fullOutput = headlessDefsCode ? `${headlessDefsCode}
21454
21459
 
21455
21460
  ${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
- );
21461
+ return {
21462
+ fragment: new RenderFragment(
21463
+ fullOutput,
21464
+ Imports.for(Import.ConstructContext, Import.RenderElementOptions).plus(renderedHydrate.imports).plus(refsManagerImport).plus(headlessImportsAll),
21465
+ renderedHydrate.validations,
21466
+ renderedHydrate.refs
21467
+ )
21468
+ };
21462
21469
  }
21463
21470
  function renderFunctionDeclaration(preRenderType) {
21464
21471
  return `export declare function render(options?: RenderElementOptions): ${preRenderType}`;
@@ -21549,7 +21556,7 @@ function renderDynamicAttributes(element, { variables }) {
21549
21556
  if (isDirectiveAttribute(attrCanonical)) return;
21550
21557
  if (attrCanonical === "style") {
21551
21558
  const styleFragment = renderStyleAttribute(attributes2[attrName], variables);
21552
- if (styleFragment.imports.has(Import.dynamicAttribute)) {
21559
+ if (styleFragment.imports.has(Import.dynamicAttribute) || styleFragment.imports.has(Import.dynamicProperty)) {
21553
21560
  renderedAttributes.push(styleFragment);
21554
21561
  }
21555
21562
  } else if (attrCanonical === "class") {
@@ -21640,6 +21647,21 @@ function renderElementRef$1(element, { dynamicRef, variables, importedRefNameToR
21640
21647
  return new RenderFragment(`${constName}()`, Imports.none(), [], refs);
21641
21648
  } else return RenderFragment.empty();
21642
21649
  }
21650
+ function coerceStaticComponentProp(value, expectedType) {
21651
+ if (!expectedType) return void 0;
21652
+ if (isEnumType(expectedType)) {
21653
+ return expectedType.values.includes(value) ? `${expectedType.alias ?? expectedType.name}.${value}` : void 0;
21654
+ }
21655
+ if (expectedType.name === "number") {
21656
+ return /^-?\d+(\.\d+)?$/.test(value) ? value : void 0;
21657
+ }
21658
+ if (expectedType.name === "boolean") {
21659
+ if (value === "true") return "true";
21660
+ if (value === "false") return "false";
21661
+ return void 0;
21662
+ }
21663
+ return void 0;
21664
+ }
21643
21665
  function renderChildCompProps$1(element, { variables }, contractProps) {
21644
21666
  let attributes2 = element.attributes;
21645
21667
  let props = [];
@@ -21657,10 +21679,15 @@ function renderChildCompProps$1(element, { variables }, contractProps) {
21657
21679
  if (attrCanonical === "ref") {
21658
21680
  return;
21659
21681
  } else {
21660
- let prop = parseComponentPropExpression(attributes2[attrName], variables);
21682
+ const rawValue = attributes2[attrName];
21683
+ const isStatic = !rawValue.includes("{");
21684
+ let prop = parseComponentPropExpression(rawValue, variables);
21661
21685
  const outputKey = contractProps?.find((p) => p.name.toLowerCase() === attrCanonical)?.name ?? attrKey;
21662
21686
  const expectedType = propTypeMap?.get(attrName) ?? propTypeMap?.get(outputKey);
21663
- if (expectedType && expectedType.name === "string" && /^\d+$/.test(prop.rendered)) {
21687
+ const coerced = isStatic ? coerceStaticComponentProp(rawValue.trim(), expectedType) : void 0;
21688
+ if (coerced !== void 0) {
21689
+ prop = prop.map(() => coerced);
21690
+ } else if (expectedType && expectedType.name === "string" && /^\d+$/.test(prop.rendered)) {
21664
21691
  prop = prop.map((_) => `'${_}'`);
21665
21692
  }
21666
21693
  props.push(prop.map((_) => `${outputKey}: ${_}`));
@@ -21733,7 +21760,9 @@ function renderNode(node2, context) {
21733
21760
  children.imports.plus(Import.dynamicElement).plus(attributes2.imports).plus(ref.imports).plus(tagFunc.import),
21734
21761
  [...attributes2.validations, ...children.validations, ...ref.validations],
21735
21762
  mergeRefsTrees(attributes2.refs, children.refs, ref.refs),
21736
- [...attributes2.recursiveRegions, ...children.recursiveRegions, ...ref.recursiveRegions]
21763
+ [...attributes2.recursiveRegions, ...children.recursiveRegions, ...ref.recursiveRegions],
21764
+ // DL#193: carry the deepest `$parent` climb up so scope-switch rules can force it.
21765
+ Math.max(attributes2.parentDepth, children.parentDepth, ref.parentDepth)
21737
21766
  );
21738
21767
  }
21739
21768
  function e2(tagName, attributes2, children, ref, currIndent = indent) {
@@ -21744,7 +21773,9 @@ function renderNode(node2, context) {
21744
21773
  children.imports.plus(Import.element).plus(attributes2.imports).plus(ref.imports).plus(tagFunc.import),
21745
21774
  [...attributes2.validations, ...children.validations, ...ref.validations],
21746
21775
  mergeRefsTrees(attributes2.refs, children.refs, ref.refs),
21747
- [...attributes2.recursiveRegions, ...children.recursiveRegions, ...ref.recursiveRegions]
21776
+ [...attributes2.recursiveRegions, ...children.recursiveRegions, ...ref.recursiveRegions],
21777
+ // DL#193: carry the deepest `$parent` climb up so scope-switch rules can force it.
21778
+ Math.max(attributes2.parentDepth, children.parentDepth, ref.parentDepth)
21748
21779
  );
21749
21780
  }
21750
21781
  function renderHtmlElement(htmlElement, newContext) {
@@ -21857,7 +21888,8 @@ ${contextForChildren.indent.firstLine}` : children
21857
21888
  result.imports.plus(Import.baseJayElement),
21858
21889
  result.validations,
21859
21890
  result.refs,
21860
- [...result.recursiveRegions, recursiveRegion]
21891
+ [...result.recursiveRegions, recursiveRegion],
21892
+ result.parentDepth
21861
21893
  );
21862
21894
  }
21863
21895
  return result;
@@ -21870,17 +21902,26 @@ ${indent.firstLine})`,
21870
21902
  Imports.merge(childElement.imports, renderedCondition.imports).plus(Import.conditional),
21871
21903
  [...renderedCondition.validations, ...childElement.validations],
21872
21904
  mergeRefsTrees(renderedCondition.refs, childElement.refs),
21873
- [...renderedCondition.recursiveRegions, ...childElement.recursiveRegions]
21905
+ [...renderedCondition.recursiveRegions, ...childElement.recursiveRegions],
21906
+ // DL#193: `if` is not a data-scope switch — it re-runs its body on every parent
21907
+ // update at runtime, so the residual `$parent` depth passes through unchanged.
21908
+ Math.max(renderedCondition.parentDepth, childElement.parentDepth)
21874
21909
  );
21875
21910
  }
21876
21911
  function renderForEach(renderedForEach, collectionVariables, trackBy, childElement) {
21912
+ const dependsOnParent = childElement.parentDepth > 0;
21913
+ const residualParentDepth = Math.max(
21914
+ renderedForEach.parentDepth,
21915
+ Math.max(0, childElement.parentDepth - 1)
21916
+ );
21877
21917
  return new RenderFragment(
21878
21918
  `${indent.firstLine}forEach(${renderedForEach.rendered}, (${collectionVariables.currentVar}: ${collectionVariables.currentType.name}) => {
21879
- ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
21919
+ ${indent.curr}return ${childElement.rendered}}, '${trackBy}'${dependsOnParent ? ", true" : ""})`,
21880
21920
  childElement.imports.plus(Import.forEach),
21881
21921
  [...renderedForEach.validations, ...childElement.validations],
21882
21922
  childElement.refs,
21883
- [...renderedForEach.recursiveRegions, ...childElement.recursiveRegions]
21923
+ [...renderedForEach.recursiveRegions, ...childElement.recursiveRegions],
21924
+ residualParentDepth
21884
21925
  );
21885
21926
  }
21886
21927
  function renderAsync(asyncType, getPromiseFragment, childElement, resolvedGenericTypes) {
@@ -21889,7 +21930,10 @@ ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
21889
21930
  childElement.imports.plus(asyncType.import),
21890
21931
  [...getPromiseFragment.validations, ...childElement.validations],
21891
21932
  childElement.refs,
21892
- [...getPromiseFragment.recursiveRegions, ...childElement.recursiveRegions]
21933
+ [...getPromiseFragment.recursiveRegions, ...childElement.recursiveRegions],
21934
+ // DL#193: `when`/async is a data-scope switch; it re-runs its body on every parent
21935
+ // update at runtime (no flag needed), and the residual `$parent` depth drops by one.
21936
+ Math.max(getPromiseFragment.parentDepth, Math.max(0, childElement.parentDepth - 1))
21893
21937
  );
21894
21938
  }
21895
21939
  function renderNestedComponent(htmlElement, newContext, componentName) {
@@ -21932,7 +21976,7 @@ ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
21932
21976
  link.names.push({ name: interactiveViewStateType, type: JayUnknown });
21933
21977
  }
21934
21978
  }
21935
- const componentVariables = new Variables(headlessImport.rootType);
21979
+ const componentVariables = new Variables(headlessImport.rootType, newContext.variables);
21936
21980
  const childIndent = newContext.indent.child(false);
21937
21981
  const childNodes = filterContentNodes(htmlElement.childNodes);
21938
21982
  let inlineBody;
@@ -21958,6 +22002,18 @@ ${indent.curr}return ${childElement.rendered}}, '${trackBy}')`,
21958
22002
  recursiveRegions: [],
21959
22003
  isInsideGuard: false,
21960
22004
  insideFastForEach: false,
22005
+ // Each inline template compiles into its OWN `_headless…Render` function, so its ref
22006
+ // const names (`refCardAction`) are function-local. Give it a fresh generator so two
22007
+ // instances of the same contract don't dedup across the boundary (`refCardAction` in
22008
+ // both, not `refCardAction` / `refCardAction2`).
22009
+ refNameGenerator: new RefNameGenerator(),
22010
+ // DL#193 Phase 3: an instance's OWN template refs are always single — the instance is
22011
+ // one unit. When the instance is repeated at the usage site (inside a forEach), that
22012
+ // collection-ness is captured by the page-level ref manager collecting the instances,
22013
+ // not by the instance's inner refs. Reset `dynamicRef` so forwarded inner refs (e.g.
22014
+ // `<jay:Counter ref="cta">`) stay single here; contract-declared refs already use the
22015
+ // stub's `repeated` flag via importedRefNameToRef, so they are unaffected.
22016
+ dynamicRef: false,
21961
22017
  // Pass headless contract names through so nested headless instances
21962
22018
  // inside headfull FS component templates can be detected (DL#123)
21963
22019
  headlessContractNames: newContext.headlessContractNames
@@ -21974,13 +22030,46 @@ ${renderedChildren.rendered}
21974
22030
  renderedChildren.imports.plus(Import.dynamicElement),
21975
22031
  renderedChildren.validations,
21976
22032
  renderedChildren.refs,
21977
- renderedChildren.recursiveRegions
22033
+ renderedChildren.recursiveRegions,
22034
+ // DL#193 Capability A: preserve the deepest `$parent` climb so the usage site
22035
+ // still weakens gates when instance content spans multiple root children.
22036
+ renderedChildren.parentDepth
21978
22037
  );
21979
22038
  } else {
21980
22039
  inlineBody = renderedChildren;
21981
22040
  }
21982
22041
  }
21983
- const mergedRefs = mergeContractStubRefs(inlineBody.refs, headlessImport.refs);
22042
+ const refOriginalName = htmlElement.attributes.ref || newContext.refNameGenerator.newAutoRefNameGenerator();
22043
+ const refRefName = camelCase$1(refOriginalName);
22044
+ const contractRefNames = /* @__PURE__ */ new Set();
22045
+ const collectContractRefNames = (tree) => {
22046
+ for (const r of tree.refs) contractRefNames.add(camelCase$1(r.ref));
22047
+ for (const child of Object.values(tree.children)) collectContractRefNames(child);
22048
+ };
22049
+ collectContractRefNames(headlessImport.refs);
22050
+ const freeRefDecls = [];
22051
+ const seenFreeRefNames = /* @__PURE__ */ new Set();
22052
+ const collectFreeRefs = (tree) => {
22053
+ for (const r of tree.refs) {
22054
+ if (isComponentType$1(r.elementType)) continue;
22055
+ if (contractRefNames.has(camelCase$1(r.ref))) continue;
22056
+ if (seenFreeRefNames.has(r.ref)) continue;
22057
+ seenFreeRefNames.add(r.ref);
22058
+ freeRefDecls.push({
22059
+ name: r.ref,
22060
+ repeated: r.repeated,
22061
+ viewStateType: r.viewStateType.name,
22062
+ elementType: r.elementType.name
22063
+ });
22064
+ }
22065
+ for (const child of Object.values(tree.children)) collectFreeRefs(child);
22066
+ };
22067
+ collectFreeRefs(inlineBody.refs);
22068
+ const freeRefNames = freeRefDecls.map((f) => f.name);
22069
+ const mergedRefs = hoistFreeRefs(
22070
+ mergeContractStubRefs(inlineBody.refs, headlessImport.refs),
22071
+ new Set(freeRefNames)
22072
+ );
21984
22073
  const { renderedRefsManager, refsManagerImport } = renderReferenceManager(
21985
22074
  mergedRefs,
21986
22075
  ReferenceManagerTarget.element
@@ -21999,6 +22088,17 @@ ${renderedChildren.rendered}
21999
22088
  const coordinateSuffix = `${contractName}:${coordinateRef}`;
22000
22089
  const instanceCoordBase = htmlElement.getAttribute("jay-coordinate-base");
22001
22090
  const coordinateKey2 = isInsideForEach ? void 0 : instanceCoordBase || coordinateSuffix;
22091
+ const coordinateArg = isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`;
22092
+ const freeRefsArg = freeRefNames.length > 0 ? `
22093
+ [${freeRefNames.map((r) => `'${r}'`).join(", ")}],` : "";
22094
+ const componentDefCode = headlessImport.structural ? `const ${componentSymbol} = makePassthroughHeadlessInstanceComponent(
22095
+ ${renderFnName},
22096
+ ${coordinateArg},${freeRefsArg}
22097
+ );` : `const ${componentSymbol} = makeHeadlessInstanceComponent(
22098
+ ${renderFnName},
22099
+ ${pluginComponentName},
22100
+ ${coordinateArg},${freeRefsArg}
22101
+ );`;
22002
22102
  const renderFnCode = `
22003
22103
  // Inline template for headless component: ${contractName} #${idx}
22004
22104
  type ${elementType} = JayElement<${interactiveViewStateType}, ${refsTypeName}>;
@@ -22014,11 +22114,7 @@ ${inlineBody.rendered}
22014
22114
  return [refManager.getPublicAPI() as ${refsTypeName}, render];
22015
22115
  }
22016
22116
 
22017
- const ${componentSymbol} = makeHeadlessInstanceComponent(
22018
- ${renderFnName},
22019
- ${headlessImport.structural ? `{ comp: (_props, _refs) => ({ render: () => _props }) }` : pluginComponentName},
22020
- ${isInsideForEach ? `(dataIds) => [...dataIds, '${coordinateSuffix}'].toString()` : `'${coordinateKey2}'`},
22021
- );`;
22117
+ ${componentDefCode}`;
22022
22118
  newContext.headlessInstanceDefs.push({
22023
22119
  componentSymbol,
22024
22120
  renderFnName,
@@ -22031,9 +22127,8 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
22031
22127
  newContext,
22032
22128
  headlessImport.contract?.props
22033
22129
  );
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);
22130
+ const outerVar = newContext.variables.currentVar;
22131
+ let getProps = `(${outerVar}: ${newContext.variables.currentType.name}) => ${propsGetterAndRefs.rendered}`;
22037
22132
  const refConstName = newContext.refNameGenerator.newConstantName(
22038
22133
  refRefName,
22039
22134
  newContext.variables
@@ -22046,6 +22141,18 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
22046
22141
  }
22047
22142
  }
22048
22143
  newContext.usedComponentImports.add(contractRefType);
22144
+ const augmented = freeRefsAugmentedRefType(contractRefType, freeRefDecls, isRepeated);
22145
+ if (freeRefDecls.length > 0) {
22146
+ for (const f of freeRefDecls) {
22147
+ if (!f.viewStateType) continue;
22148
+ for (const link of headlessImport.contractLinks) {
22149
+ if (!link.names.some((n) => n.name === f.viewStateType)) {
22150
+ link.names.push({ name: f.viewStateType, type: JayUnknown });
22151
+ }
22152
+ }
22153
+ newContext.usedComponentImports.add(f.viewStateType);
22154
+ }
22155
+ }
22049
22156
  const instanceRef = mkRef(
22050
22157
  refRefName,
22051
22158
  refOriginalName,
@@ -22053,18 +22160,30 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
22053
22160
  isRepeated,
22054
22161
  !htmlElement.attributes.ref,
22055
22162
  newContext.variables.currentType,
22056
- new JayTypeAlias(contractRefType)
22163
+ new JayTypeAlias(augmented.type),
22164
+ // DL#198 — declare free refs on the page-side region ref so renderReferenceManager emits the
22165
+ // per-region FreeReferenceManager (with per-free-ref `repeated` driving element vs collection).
22166
+ freeRefDecls.length > 0 ? freeRefDecls : void 0
22057
22167
  );
22168
+ const instanceRefsTree = mkRefsTree([instanceRef], {});
22058
22169
  let renderedRef = new RenderFragment(
22059
22170
  `${refConstName}()`,
22060
22171
  Imports.for(),
22061
22172
  [],
22062
- mkRefsTree([instanceRef], {})
22173
+ instanceRefsTree
22063
22174
  );
22064
22175
  if (renderedRef.rendered !== "") renderedRef = renderedRef.map((_) => ", " + _);
22176
+ const freeRefManagerArg = freeRefNames.length > 0 ? `, ${refConstName}FreeRefManager` : "";
22177
+ let childCompImports = Imports.for(Import.childComp).plus(propsGetterAndRefs.imports).plus(renderedRef.imports).plus(Import.ConstructContext).plus(
22178
+ headlessImport.structural ? Import.makePassthroughHeadlessInstanceComponent : Import.makeHeadlessInstanceComponent
22179
+ );
22180
+ if (augmented.needsElementProxy)
22181
+ childCompImports = childCompImports.plus(Import.HTMLElementProxy);
22182
+ if (augmented.needsCollectionProxy)
22183
+ childCompImports = childCompImports.plus(Import.HTMLElementCollectionProxy);
22065
22184
  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),
22185
+ `${newContext.indent.firstLine}childComp(${componentSymbol}, ${getProps}${renderedRef.rendered}${freeRefManagerArg})`,
22186
+ childCompImports,
22068
22187
  [
22069
22188
  ...propsGetterAndRefs.validations,
22070
22189
  ...inlineBody.validations,
@@ -22113,7 +22232,11 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
22113
22232
  nestedChildElement.imports.plus(Import.withData).plus(accessorExpr.render().imports),
22114
22233
  [...accessorExpr.validations, ...nestedChildElement.validations],
22115
22234
  nestedChildElement.refs,
22116
- nestedChildElement.recursiveRegions
22235
+ nestedChildElement.recursiveRegions,
22236
+ // DL#193: `with-data` is a data-scope switch; it re-runs its body on every
22237
+ // parent update at runtime (no flag needed), and the residual `$parent`
22238
+ // depth drops by one.
22239
+ Math.max(0, nestedChildElement.parentDepth - 1)
22117
22240
  );
22118
22241
  } else if (isRecurse(htmlElement)) {
22119
22242
  const refAttr = htmlElement.getAttribute("ref");
@@ -22187,7 +22310,6 @@ const ${componentSymbol} = makeHeadlessInstanceComponent(
22187
22310
  isInsideGuard: true,
22188
22311
  // Mark that we're inside a guard
22189
22312
  insideFastForEach: true
22190
- // Fast-phase forEach — headless instances not supported
22191
22313
  };
22192
22314
  let childElement = renderHtmlElement(htmlElement, newContext);
22193
22315
  return nestRefs(
@@ -22295,9 +22417,7 @@ function renderFunctionImplementation$1(types2, rootBodyElement, importStatement
22295
22417
  const variables = new Variables(types2);
22296
22418
  const { importedSymbols, importedSandboxedSymbols } = processImportedComponents(importStatements);
22297
22419
  const importedRefNameToRef = processImportedHeadless(headlessImports);
22298
- const headlessContractNames = new Set(
22299
- headlessImports.filter((h) => !h.structural).map((h) => h.contractName)
22300
- );
22420
+ const headlessContractNames = new Set(headlessImports.map((h) => h.contractName));
22301
22421
  assignCoordinates(rootBodyElement, { headlessContractNames });
22302
22422
  const rootElement = ensureSingleChildElement(rootBodyElement);
22303
22423
  let renderedRoot;
@@ -22359,8 +22479,12 @@ ${renderedRoot.rendered}
22359
22479
  if (headLinks.length > 0) {
22360
22480
  imports = imports.plus(Import.injectHeadLinks);
22361
22481
  }
22362
- const { imports: refImports, renderedRefs } = renderRefsType(renderedRoot.refs, refsType);
22482
+ const { imports: refImports, renderedRefs: renderedPageRefs } = renderRefsType(
22483
+ renderedRoot.refs,
22484
+ refsType
22485
+ );
22363
22486
  imports = imports.plus(refImports);
22487
+ const renderedRefs = renderedPageRefs;
22364
22488
  let renderedElement = `export type ${elementType} = JayElement<${viewStateType}, ${refsType}>
22365
22489
  export type ${renderType2} = RenderElement<${viewStateType}, ${refsType}, ${elementType}>
22366
22490
  export type ${preRenderType} = [${refsType}, ${renderType2}]
@@ -22374,10 +22498,11 @@ ${Indent.forceIndent(code, 4)},
22374
22498
  funcRepository)`
22375
22499
  );
22376
22500
  }
22377
- const { renderedRefsManager } = renderReferenceManager(
22501
+ const { renderedRefsManager, refsManagerImport } = renderReferenceManager(
22378
22502
  renderedRoot.refs,
22379
22503
  ReferenceManagerTarget.element
22380
22504
  );
22505
+ imports = imports.plus(refsManagerImport);
22381
22506
  const headLinksInjection = headLinks.length > 0 ? ` injectHeadLinks(${renderHeadLinksArray(headLinks)});
22382
22507
  ` : "";
22383
22508
  const recursiveFunctions = generateRecursiveFunctions(renderedRoot.recursiveRegions);
@@ -22389,11 +22514,12 @@ ${recursiveFunctions}
22389
22514
  for (const def of headlessInstanceDefs) {
22390
22515
  imports = imports.plus(def.imports);
22391
22516
  }
22517
+ const rootCallback = `() => ${renderedRoot.rendered.trim()}`;
22392
22518
  const body = `${headlessDefsCode}export function render(options?: RenderElementOptions): ${preRenderType} {
22393
- ${renderedRefsManager}
22519
+ ${renderedRefsManager}
22394
22520
  ${headLinksInjection}${recursiveFunctionsSection} const render = (viewState: ${viewStateType}) => ConstructContext.withRootContext(
22395
22521
  viewState, refManager,
22396
- () => ${renderedRoot.rendered.trim()}
22522
+ ${rootCallback}
22397
22523
  ) as ${elementType};
22398
22524
  return [refManager.getPublicAPI() as ${refsType}, render];
22399
22525
  }`;
@@ -22498,11 +22624,15 @@ function generateElementFile(jayFile, importerMode) {
22498
22624
  const usedHeadlessTypeNames = new Set(usedComponentImports);
22499
22625
  const headlessModules = /* @__PURE__ */ new Set();
22500
22626
  for (const headless of jayFile.headlessImports) {
22501
- usedHeadlessTypeNames.add(headless.rootType.name);
22627
+ if (!headless.structural) {
22628
+ usedHeadlessTypeNames.add(headless.rootType.name);
22629
+ }
22502
22630
  for (const link of headless.contractLinks) {
22503
22631
  headlessModules.add(link.module);
22504
22632
  for (const name of link.names) {
22505
- if (name.name.endsWith("Refs") || isEnumType(name.type)) {
22633
+ if (isEnumType(name.type)) {
22634
+ usedHeadlessTypeNames.add(name.name);
22635
+ } else if (!headless.structural && name.name.endsWith("Refs")) {
22506
22636
  usedHeadlessTypeNames.add(name.name);
22507
22637
  }
22508
22638
  }
@@ -22584,7 +22714,7 @@ function generateElementBridgeFile(jayFile) {
22584
22714
  }
22585
22715
  function generateElementHydrateFile(jayFile, importerMode) {
22586
22716
  const types2 = generateTypes(jayFile.types);
22587
- const headlessImports = jayFile.headlessImports?.filter((h) => !h.key && !h.structural) ?? [];
22717
+ const headlessImports = jayFile.headlessImports?.filter((h) => !h.key) ?? [];
22588
22718
  const headlessContractNames = new Set(headlessImports.map((h) => h.contractName));
22589
22719
  assignCoordinates(jayFile.body, { headlessContractNames });
22590
22720
  const {
@@ -22605,7 +22735,7 @@ function generateElementHydrateFile(jayFile, importerMode) {
22605
22735
  jayFile.headLinks
22606
22736
  );
22607
22737
  const phaseTypes = generatePhaseSpecificTypes(jayFile);
22608
- const renderedHydrate = renderHydrate(
22738
+ const { fragment: renderedHydrate } = renderHydrate(
22609
22739
  jayFile.types,
22610
22740
  jayFile.body,
22611
22741
  jayFile.imports,
@@ -22615,16 +22745,43 @@ function generateElementHydrateFile(jayFile, importerMode) {
22615
22745
  jayFile.headlessImports,
22616
22746
  jayFile.contract
22617
22747
  );
22748
+ const allRenderedRefs = renderedRefs;
22618
22749
  let finalRenderedElement = renderedElement;
22619
22750
  if (jayFile.contract || jayFile.hasInlineData) {
22620
22751
  finalRenderedElement = expandContractType(finalRenderedElement, jayFile.baseElementName);
22621
22752
  }
22622
22753
  const typeOnlyImports = renderedImplementation.imports.minus(renderedHydrate.imports).minus(Import.element).minus(Import.dynamicText).minus(Import.dynamicElement).minus(Import.conditional).minus(Import.forEach);
22623
22754
  const hydrateImports = typeOnlyImports.plus(Import.jayElement).plus(renderedHydrate.imports);
22755
+ const structuralHeadless = (jayFile.headlessImports ?? []).filter((h) => h.structural);
22756
+ let filteredHydrateImports = jayFile.imports;
22757
+ if (structuralHeadless.length > 0) {
22758
+ const structuralModules = new Set(
22759
+ structuralHeadless.flatMap((h) => h.contractLinks.map((l) => l.module))
22760
+ );
22761
+ const dropNames = /* @__PURE__ */ new Set();
22762
+ for (const headless of structuralHeadless) {
22763
+ dropNames.add(headless.rootType.name);
22764
+ }
22765
+ for (const headless of (jayFile.headlessImports ?? []).filter((h) => !h.structural)) {
22766
+ dropNames.delete(headless.rootType.name);
22767
+ for (const link of headless.contractLinks)
22768
+ for (const name of link.names) dropNames.delete(name.name);
22769
+ }
22770
+ filteredHydrateImports = jayFile.imports.map((importLink) => {
22771
+ if (!structuralModules.has(importLink.module)) return importLink;
22772
+ const names = importLink.names.filter((n) => !dropNames.has(n.as || n.name));
22773
+ return names.length === 0 ? null : { ...importLink, names };
22774
+ }).filter((imp) => imp !== null);
22775
+ }
22624
22776
  const renderedFile = [
22625
- renderImports$1(hydrateImports, ImportsFor.implementation, jayFile.imports, importerMode),
22777
+ renderImports$1(
22778
+ hydrateImports,
22779
+ ImportsFor.implementation,
22780
+ filteredHydrateImports,
22781
+ importerMode
22782
+ ),
22626
22783
  types2,
22627
- renderedRefs,
22784
+ allRenderedRefs,
22628
22785
  phaseTypes,
22629
22786
  finalRenderedElement,
22630
22787
  renderedHydrate.rendered
@@ -22863,7 +23020,10 @@ function renderAttributes(element, { variables }) {
22863
23020
  );
22864
23021
  else if (attrName === "class") {
22865
23022
  let classExpression = parseReactClassExpression(attributes2[attrName], variables);
22866
- renderedAttributes.push(classExpression.map((_) => `className=${_}`));
23023
+ let classFragment = classExpression.map((_) => `className=${_}`);
23024
+ if (classExpression.rendered.includes("cx("))
23025
+ classFragment = classFragment.plusImport(Imports.for(Import.classNamesReact));
23026
+ renderedAttributes.push(classFragment);
22867
23027
  } else if (booleanAttributes.has(attrCanonical)) {
22868
23028
  const attrValue = attributes2[attrName];
22869
23029
  if (attrValue === "") {
@@ -23198,6 +23358,24 @@ function generateElementFileReactTarget(jayFile, importerMode) {
23198
23358
  ].filter((_) => _ !== null && _ !== "").join("\n\n");
23199
23359
  return new WithValidations(renderedFile, renderedImplementation.validations);
23200
23360
  }
23361
+ function buildStructuralCoercions(rawVar, tags) {
23362
+ const coercions = [];
23363
+ for (const tag of tags) {
23364
+ const dt = tag.dataType;
23365
+ if (!dt) continue;
23366
+ const field = `${rawVar}.${tag.tag}`;
23367
+ if (isEnumType(dt)) {
23368
+ coercions.push(
23369
+ `${tag.tag}: typeof (${dt.name} as any)[${field}] === 'number' ? (${dt.name} as any)[${field}] : Number(${field})`
23370
+ );
23371
+ } else if (dt.name === "number") {
23372
+ coercions.push(`${tag.tag}: Number(${field})`);
23373
+ } else if (dt.name === "boolean") {
23374
+ coercions.push(`${tag.tag}: ${field} === true || ${field} === 'true'`);
23375
+ }
23376
+ }
23377
+ return coercions;
23378
+ }
23201
23379
  function w(indent, code, imports = Imports.none()) {
23202
23380
  return new RenderFragment(`${indent.firstLine}w(${code});`, imports);
23203
23381
  }
@@ -23314,7 +23492,12 @@ function renderServerHeadlessInstance(element, context, contractName) {
23314
23492
  instanceKeyExpr = `'${instanceCoord}'`;
23315
23493
  }
23316
23494
  const ifCondition = element.attributes.if;
23317
- const componentVariables = new Variables(headlessImport.rootType, void 0, 0, varName);
23495
+ const componentVariables = new Variables(
23496
+ headlessImport.rootType,
23497
+ context.variables,
23498
+ 0,
23499
+ varName
23500
+ ).asLexical();
23318
23501
  const childNodes = filterContentNodes(element.childNodes);
23319
23502
  if (childNodes.length === 0) {
23320
23503
  return new RenderFragment("", Imports.none(), [
@@ -23346,8 +23529,15 @@ function renderServerHeadlessInstance(element, context, contractName) {
23346
23529
  })
23347
23530
  );
23348
23531
  const guardIndent = ifCondition ? new Indent(indent.curr + " ") : indent;
23532
+ const coercions = headlessImport.structural ? buildStructuralCoercions(`${varName}_raw`, headlessImport.contract?.tags ?? []) : [];
23533
+ const extractionLines = coercions.length > 0 ? [
23534
+ `${guardIndent.firstLine}const ${varName}_raw = (vs as any).__headlessInstances?.[${instanceKeyExpr}] as any;`,
23535
+ `${guardIndent.firstLine}const ${varName} = ${varName}_raw ? ({ ...${varName}_raw, ${coercions.join(", ")} } as ${viewStateTypeName}) : undefined;`
23536
+ ] : [
23537
+ `${guardIndent.firstLine}const ${varName} = (vs as any).__headlessInstances?.[${instanceKeyExpr}] as ${viewStateTypeName} | undefined;`
23538
+ ];
23349
23539
  const guardedBlock = [
23350
- `${guardIndent.firstLine}const ${varName} = (vs as any).__headlessInstances?.[${instanceKeyExpr}] as ${viewStateTypeName} | undefined;`,
23540
+ ...extractionLines,
23351
23541
  `${guardIndent.firstLine}if (${varName}) {`,
23352
23542
  renderedChildren.rendered,
23353
23543
  `${guardIndent.firstLine}}`
@@ -23419,12 +23609,9 @@ function renderServerAttributes(element, context) {
23419
23609
  const classExpr = parseClassExpression(attrValue, variables);
23420
23610
  if (classExpr.imports.has(Import.dynamicAttribute)) {
23421
23611
  const rawExpr = classExpr.rendered.replace(/^da\(\w+ => /, "").replace(/\)$/, "");
23612
+ const classImports = classExpr.imports.has(Import.classNames) ? Imports.for(Import.escapeAttr, Import.classNamesServer) : Imports.for(Import.escapeAttr);
23422
23613
  parts.push(
23423
- w(
23424
- indent,
23425
- `' class="' + escapeAttr(String(${rawExpr})) + '"'`,
23426
- Imports.for(Import.escapeAttr)
23427
- )
23614
+ w(indent, `' class="' + escapeAttr(String(${rawExpr})) + '"'`, classImports)
23428
23615
  );
23429
23616
  } else {
23430
23617
  const escaped = escapeForJsString(attrValue);
@@ -23614,10 +23801,11 @@ function renderServerAttributesAsString(element, context) {
23614
23801
  const classExpr = parseClassExpression(attrValue, variables);
23615
23802
  if (classExpr.imports.has(Import.dynamicAttribute)) {
23616
23803
  const rawExpr = classExpr.rendered.replace(/^da\(\w+ => /, "").replace(/\)$/, "");
23804
+ const classImports = classExpr.imports.has(Import.classNames) ? Imports.for(Import.escapeAttr, Import.classNamesServer) : Imports.for(Import.escapeAttr);
23617
23805
  parts.push(
23618
23806
  new RenderFragment(
23619
23807
  `' class="' + escapeAttr(String(${rawExpr})) + '"'`,
23620
- Imports.for(Import.escapeAttr)
23808
+ classImports
23621
23809
  )
23622
23810
  );
23623
23811
  } else {
@@ -23807,7 +23995,14 @@ function renderServerAsyncGroup(group, context) {
23807
23995
  const templateValidations = [];
23808
23996
  if (group.resolvedElement) {
23809
23997
  const promiseResolvedType = asyncAccessor.resolvedType.itemType;
23810
- const resolvedVariables = new Variables(promiseResolvedType, variables, 1);
23998
+ const resolvedVariables = new Variables(
23999
+ promiseResolvedType,
24000
+ variables,
24001
+ 1,
24002
+ void 0,
24003
+ {},
24004
+ variables.lexicallyInScope
24005
+ );
23811
24006
  const resolvedContext = {
23812
24007
  ...context,
23813
24008
  variables: resolvedVariables
@@ -23824,7 +24019,14 @@ function renderServerAsyncGroup(group, context) {
23824
24019
  templateValidations.push(...resolvedString.validations);
23825
24020
  }
23826
24021
  if (group.rejectedElement) {
23827
- const rejectedVariables = new Variables(JayErrorType, variables, 1);
24022
+ const rejectedVariables = new Variables(
24023
+ JayErrorType,
24024
+ variables,
24025
+ 1,
24026
+ void 0,
24027
+ {},
24028
+ variables.lexicallyInScope
24029
+ );
23828
24030
  const rejectedContext = {
23829
24031
  ...context,
23830
24032
  variables: rejectedVariables
@@ -23884,7 +24086,7 @@ function hasMixedContentDynamicTextInteractive(childNodes, interactivePaths) {
23884
24086
  }
23885
24087
  function generateServerElementFile(jayFile, _options) {
23886
24088
  const types2 = generateTypes(jayFile.types);
23887
- const variables = new Variables(jayFile.types);
24089
+ const variables = new Variables(jayFile.types).asLexical();
23888
24090
  const rootElement = ensureSingleChildElement(jayFile.body);
23889
24091
  if (!rootElement.val) {
23890
24092
  return new WithValidations("", rootElement.validations);
@@ -23903,14 +24105,29 @@ function generateServerElementFile(jayFile, _options) {
23903
24105
  interactivePaths: buildInteractivePaths(jayFile.contract),
23904
24106
  parentHasInteractiveChildren: false
23905
24107
  };
23906
- const rendered = renderServerElementContent(rootElement.val, context, {
23907
- isRoot: true
23908
- });
24108
+ const rootComponentMatch = getComponentName(
24109
+ rootElement.val.rawTagName,
24110
+ /* @__PURE__ */ new Set(),
24111
+ // No headful component imports in server-element target
24112
+ headlessContractNames
24113
+ );
24114
+ let rendered;
24115
+ try {
24116
+ rendered = rootComponentMatch !== null && rootComponentMatch.kind === "headless-instance" ? renderServerElement(rootElement.val, context) : renderServerElementContent(rootElement.val, context, {
24117
+ isRoot: true
24118
+ });
24119
+ } catch (e2) {
24120
+ if (e2 instanceof UnsupportedServerParentBindingError) {
24121
+ return new WithValidations("", [UNSUPPORTED_TARGET_PARENT_MESSAGE]);
24122
+ }
24123
+ throw e2;
24124
+ }
23909
24125
  const viewStateType = jayFile.types.name;
23910
24126
  const hasAsync = rendered.rendered.includes("onAsync(");
23911
24127
  const importParts = [];
23912
24128
  if (rendered.imports.has(Import.escapeHtml)) importParts.push("escapeHtml");
23913
24129
  if (rendered.imports.has(Import.escapeAttr)) importParts.push("escapeAttr");
24130
+ if (rendered.imports.has(Import.classNamesServer)) importParts.push("classNames as cx");
23914
24131
  importParts.push("type ServerRenderContext");
23915
24132
  const importStatement = `import {${importParts.join(", ")}} from "@jay-framework/ssr-runtime";`;
23916
24133
  const usedTypeNames = /* @__PURE__ */ new Set();
@@ -23920,17 +24137,35 @@ function generateServerElementFile(jayFile, _options) {
23920
24137
  for (const link of headless.contractLinks) {
23921
24138
  headlessModules.add(link.module);
23922
24139
  for (const name of link.names) {
23923
- if (!name.name.endsWith("Refs")) {
24140
+ if (isEnumType(name.type)) {
24141
+ usedTypeNames.add(name.name);
24142
+ } else if (!name.name.endsWith("Refs")) {
23924
24143
  usedTypeNames.add(name.name);
23925
24144
  }
23926
24145
  }
23927
24146
  }
23928
24147
  }
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);
24148
+ const typeNamesByModule = /* @__PURE__ */ new Map();
24149
+ const typeNamesSeen = /* @__PURE__ */ new Map();
24150
+ for (const imp of jayFile.imports) {
24151
+ if (!headlessModules.has(imp.module)) continue;
24152
+ let names = typeNamesByModule.get(imp.module);
24153
+ let seen = typeNamesSeen.get(imp.module);
24154
+ if (!names) {
24155
+ names = [];
24156
+ seen = /* @__PURE__ */ new Set();
24157
+ typeNamesByModule.set(imp.module, names);
24158
+ typeNamesSeen.set(imp.module, seen);
24159
+ }
24160
+ for (const n of imp.names) {
24161
+ if (!usedTypeNames.has(n.as || n.name)) continue;
24162
+ const rendered2 = n.as ? `${n.name} as ${n.as}` : n.name;
24163
+ if (seen.has(rendered2)) continue;
24164
+ seen.add(rendered2);
24165
+ names.push(rendered2);
24166
+ }
24167
+ }
24168
+ const contractImports = [...typeNamesByModule.entries()].filter(([, typeNames]) => typeNames.length > 0).map(([module, typeNames]) => `import {${typeNames.join(", ")}} from "${module}";`);
23934
24169
  const ctxDestructure = hasAsync ? "const { write: w, onAsync } = ctx;" : "const { write: w } = ctx;";
23935
24170
  const renderedFile = [
23936
24171
  importStatement,
@@ -30653,12 +30888,13 @@ const JAY_IMPORT_RESOLVER = {
30653
30888
  return analyzeExportedTypes(fullPath, options);
30654
30889
  },
30655
30890
  loadContract(fullPath) {
30656
- const content = fs.readFileSync(fullPath).toString();
30891
+ const content = fs$1.readFileSync(fullPath).toString();
30657
30892
  return parseContract(content, fullPath);
30658
30893
  },
30659
30894
  resolveLink(importingModuleDir, link) {
30660
30895
  if (link?.[0] === ".") return path.resolve(importingModuleDir, link);
30661
- else return require$1.resolve(link, { paths: require$1.resolve.paths(importingModuleDir) });
30896
+ if (link?.[0] === "/") return link;
30897
+ return require$1.resolve(link, { paths: require$1.resolve.paths(importingModuleDir) });
30662
30898
  },
30663
30899
  resolvePluginComponent(pluginName, contractName, projectRoot) {
30664
30900
  return resolvePluginComponent(projectRoot, pluginName, contractName);
@@ -30667,7 +30903,7 @@ const JAY_IMPORT_RESOLVER = {
30667
30903
  const resolution = resolvePluginComponent(projectRoot, pluginName, contractName);
30668
30904
  if (resolution.val && resolution.val.contractPath) {
30669
30905
  const contractPath = resolution.val.contractPath;
30670
- const content = fs.readFileSync(contractPath).toString();
30906
+ const content = fs$1.readFileSync(contractPath).toString();
30671
30907
  const contractResult = parseContract(content, contractPath);
30672
30908
  return contractResult.map((contract) => ({ contract, contractPath }));
30673
30909
  }
@@ -30675,14 +30911,14 @@ const JAY_IMPORT_RESOLVER = {
30675
30911
  const pluginDir = pluginName.startsWith("@") ? pluginName.split("/").pop() || pluginName : pluginName;
30676
30912
  const contractFileName = contractName.replace(/\//g, "-") + ".jay-contract";
30677
30913
  const materializedPath = path.join(materializedDir, pluginDir, contractFileName);
30678
- if (fs.existsSync(materializedPath)) {
30679
- const content = fs.readFileSync(materializedPath).toString();
30914
+ if (fs$1.existsSync(materializedPath)) {
30915
+ const content = fs$1.readFileSync(materializedPath).toString();
30680
30916
  const contractResult = parseContract(content, materializedPath);
30681
30917
  let metadata;
30682
30918
  const indexPath = path.join(projectRoot, "agent-kit", "plugins-index.yaml");
30683
- if (fs.existsSync(indexPath)) {
30919
+ if (fs$1.existsSync(indexPath)) {
30684
30920
  try {
30685
- const indexContent = fs.readFileSync(indexPath, "utf-8");
30921
+ const indexContent = fs$1.readFileSync(indexPath, "utf-8");
30686
30922
  const index2 = YAML.parse(indexContent);
30687
30923
  const plugin = index2.plugins?.find(
30688
30924
  (p) => p.name === pluginName || p.name === pluginDir
@@ -30711,13 +30947,13 @@ const JAY_IMPORT_RESOLVER = {
30711
30947
  const resolvedPath = src.startsWith(".") ? path.resolve(importingModuleDir, src) : src;
30712
30948
  const jayHtmlPath = resolvedPath + ".jay-html";
30713
30949
  try {
30714
- const content = fs.readFileSync(jayHtmlPath, "utf-8");
30950
+ const content = fs$1.readFileSync(jayHtmlPath, "utf-8");
30715
30951
  return { content, componentDir: path.dirname(jayHtmlPath), filePath: jayHtmlPath };
30716
30952
  } catch {
30717
30953
  }
30718
30954
  const dirJayHtmlPath = path.resolve(resolvedPath, path.basename(resolvedPath)) + ".jay-html";
30719
30955
  try {
30720
- const content = fs.readFileSync(dirJayHtmlPath, "utf-8");
30956
+ const content = fs$1.readFileSync(dirJayHtmlPath, "utf-8");
30721
30957
  return {
30722
30958
  content,
30723
30959
  componentDir: path.dirname(dirJayHtmlPath),
@@ -30907,6 +31143,10 @@ function resolveRelativePaths(root, sourceDir) {
30907
31143
  if (src && isRelativePath(src)) {
30908
31144
  script.setAttribute("src", path.resolve(sourceDir, src));
30909
31145
  }
31146
+ const contract = script.getAttribute("contract");
31147
+ if (contract && isRelativePath(contract)) {
31148
+ script.setAttribute("contract", path.resolve(sourceDir, contract));
31149
+ }
30910
31150
  }
30911
31151
  const links = root.querySelectorAll('link[rel="stylesheet"]');
30912
31152
  for (const link of links) {
@@ -31014,7 +31254,7 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
31014
31254
  const instances = [];
31015
31255
  const forEachInstances = [];
31016
31256
  const coordinateCounters = /* @__PURE__ */ new Map();
31017
- function walk(element, insidePreservedForEach, forEachContexts) {
31257
+ function walk(element, insidePreservedForEach, forEachContexts, parentCoordinate) {
31018
31258
  const tagName = element.tagName?.toLowerCase();
31019
31259
  const forEachAttr = element.getAttribute("forEach");
31020
31260
  const hasForEach = forEachAttr != null;
@@ -31028,6 +31268,7 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
31028
31268
  props[toCamelCase(key)] = value;
31029
31269
  }
31030
31270
  }
31271
+ let childParentCoordinate = parentCoordinate;
31031
31272
  if (!insidePreservedForEach) {
31032
31273
  let ref = element.getAttribute("ref");
31033
31274
  if (!ref) {
@@ -31043,8 +31284,10 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
31043
31284
  instances.push({
31044
31285
  contractName,
31045
31286
  props,
31046
- coordinate
31287
+ coordinate,
31288
+ parentCoordinate
31047
31289
  });
31290
+ childParentCoordinate = coordinate;
31048
31291
  } else {
31049
31292
  const innerForEach = forEachContexts[forEachContexts.length - 1];
31050
31293
  if (innerForEach) {
@@ -31066,7 +31309,12 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
31066
31309
  }
31067
31310
  for (const child of element.childNodes) {
31068
31311
  if (child.nodeType === NodeType.ELEMENT_NODE) {
31069
- walk(child, insidePreservedForEach, forEachContexts);
31312
+ walk(
31313
+ child,
31314
+ insidePreservedForEach,
31315
+ forEachContexts,
31316
+ childParentCoordinate
31317
+ );
31070
31318
  }
31071
31319
  }
31072
31320
  return;
@@ -31077,14 +31325,15 @@ function discoverHeadlessInstances(preRenderedJayHtml) {
31077
31325
  walk(
31078
31326
  child,
31079
31327
  insidePreservedForEach || hasForEach,
31080
- updatedForEachContexts
31328
+ updatedForEachContexts,
31329
+ parentCoordinate
31081
31330
  );
31082
31331
  }
31083
31332
  }
31084
31333
  }
31085
31334
  const body = root.querySelector("body");
31086
31335
  if (body) {
31087
- walk(body, false, []);
31336
+ walk(body, false, [], void 0);
31088
31337
  }
31089
31338
  return { instances, forEachInstances, preRenderedJayHtml: root.toString() };
31090
31339
  }
@@ -31215,7 +31464,6 @@ export {
31215
31464
  getLinkedContractDir,
31216
31465
  hasSlowPhaseProperties,
31217
31466
  htmlElementTagNameMap,
31218
- injectHeadfullFSTemplates,
31219
31467
  isTagInPhase,
31220
31468
  loadLinkedContract,
31221
31469
  parseAction,