@adaas/are-html 0.0.22 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/dist/browser/index.d.mts +176 -8
  2. package/dist/browser/index.mjs +661 -235
  3. package/dist/browser/index.mjs.map +1 -1
  4. package/dist/node/{AreBinding.attribute-doUvtOjc.d.mts → AreBinding.attribute-BWzEIw6H.d.mts} +45 -0
  5. package/dist/node/{AreBinding.attribute-Bm5LlOyE.d.ts → AreBinding.attribute-GpT-5Qmf.d.ts} +45 -0
  6. package/dist/node/attributes/AreBinding.attribute.d.mts +1 -1
  7. package/dist/node/attributes/AreBinding.attribute.d.ts +1 -1
  8. package/dist/node/attributes/AreDirective.attribute.d.mts +1 -1
  9. package/dist/node/attributes/AreDirective.attribute.d.ts +1 -1
  10. package/dist/node/attributes/AreEvent.attribute.d.mts +1 -1
  11. package/dist/node/attributes/AreEvent.attribute.d.ts +1 -1
  12. package/dist/node/attributes/AreStatic.attribute.d.mts +1 -1
  13. package/dist/node/attributes/AreStatic.attribute.d.ts +1 -1
  14. package/dist/node/directives/AreDirectiveFor.directive.d.mts +18 -1
  15. package/dist/node/directives/AreDirectiveFor.directive.d.ts +18 -1
  16. package/dist/node/directives/AreDirectiveFor.directive.js +57 -9
  17. package/dist/node/directives/AreDirectiveFor.directive.js.map +1 -1
  18. package/dist/node/directives/AreDirectiveFor.directive.mjs +57 -9
  19. package/dist/node/directives/AreDirectiveFor.directive.mjs.map +1 -1
  20. package/dist/node/directives/AreDirectiveIf.directive.d.mts +1 -1
  21. package/dist/node/directives/AreDirectiveIf.directive.d.ts +1 -1
  22. package/dist/node/directives/AreDirectiveShow.directive.d.mts +1 -1
  23. package/dist/node/directives/AreDirectiveShow.directive.d.ts +1 -1
  24. package/dist/node/engine/AreHTML.compiler.d.mts +1 -1
  25. package/dist/node/engine/AreHTML.compiler.d.ts +1 -1
  26. package/dist/node/engine/AreHTML.compiler.js +4 -0
  27. package/dist/node/engine/AreHTML.compiler.js.map +1 -1
  28. package/dist/node/engine/AreHTML.compiler.mjs +4 -0
  29. package/dist/node/engine/AreHTML.compiler.mjs.map +1 -1
  30. package/dist/node/engine/AreHTML.constants.d.mts +33 -1
  31. package/dist/node/engine/AreHTML.constants.d.ts +33 -1
  32. package/dist/node/engine/AreHTML.constants.js +166 -0
  33. package/dist/node/engine/AreHTML.constants.js.map +1 -1
  34. package/dist/node/engine/AreHTML.constants.mjs +165 -1
  35. package/dist/node/engine/AreHTML.constants.mjs.map +1 -1
  36. package/dist/node/engine/AreHTML.context.d.mts +66 -0
  37. package/dist/node/engine/AreHTML.context.d.ts +66 -0
  38. package/dist/node/engine/AreHTML.context.js +98 -0
  39. package/dist/node/engine/AreHTML.context.js.map +1 -1
  40. package/dist/node/engine/AreHTML.context.mjs +98 -0
  41. package/dist/node/engine/AreHTML.context.mjs.map +1 -1
  42. package/dist/node/engine/AreHTML.interpreter.d.mts +3 -0
  43. package/dist/node/engine/AreHTML.interpreter.d.ts +3 -0
  44. package/dist/node/engine/AreHTML.interpreter.js +66 -10
  45. package/dist/node/engine/AreHTML.interpreter.js.map +1 -1
  46. package/dist/node/engine/AreHTML.interpreter.mjs +66 -10
  47. package/dist/node/engine/AreHTML.interpreter.mjs.map +1 -1
  48. package/dist/node/engine/AreHTML.lifecycle.d.mts +1 -8
  49. package/dist/node/engine/AreHTML.lifecycle.d.ts +1 -8
  50. package/dist/node/engine/AreHTML.lifecycle.js +29 -44
  51. package/dist/node/engine/AreHTML.lifecycle.js.map +1 -1
  52. package/dist/node/engine/AreHTML.lifecycle.mjs +29 -44
  53. package/dist/node/engine/AreHTML.lifecycle.mjs.map +1 -1
  54. package/dist/node/engine/AreHTML.tokenizer.d.mts +1 -1
  55. package/dist/node/engine/AreHTML.tokenizer.d.ts +1 -1
  56. package/dist/node/engine/AreHTML.tokenizer.js +7 -1
  57. package/dist/node/engine/AreHTML.tokenizer.js.map +1 -1
  58. package/dist/node/engine/AreHTML.tokenizer.mjs +7 -1
  59. package/dist/node/engine/AreHTML.tokenizer.mjs.map +1 -1
  60. package/dist/node/engine/AreHTML.transformer.d.mts +1 -1
  61. package/dist/node/engine/AreHTML.transformer.d.ts +1 -1
  62. package/dist/node/index.d.mts +4 -3
  63. package/dist/node/index.d.ts +4 -3
  64. package/dist/node/index.js +7 -0
  65. package/dist/node/index.mjs +1 -0
  66. package/dist/node/instructions/AddStaticHTML.instruction.d.mts +8 -0
  67. package/dist/node/instructions/AddStaticHTML.instruction.d.ts +8 -0
  68. package/dist/node/instructions/AddStaticHTML.instruction.js +31 -0
  69. package/dist/node/instructions/AddStaticHTML.instruction.js.map +1 -0
  70. package/dist/node/instructions/AddStaticHTML.instruction.mjs +24 -0
  71. package/dist/node/instructions/AddStaticHTML.instruction.mjs.map +1 -0
  72. package/dist/node/instructions/AreHTML.instructions.constants.d.mts +1 -0
  73. package/dist/node/instructions/AreHTML.instructions.constants.d.ts +1 -0
  74. package/dist/node/instructions/AreHTML.instructions.constants.js +1 -0
  75. package/dist/node/instructions/AreHTML.instructions.constants.js.map +1 -1
  76. package/dist/node/instructions/AreHTML.instructions.constants.mjs +1 -0
  77. package/dist/node/instructions/AreHTML.instructions.constants.mjs.map +1 -1
  78. package/dist/node/instructions/AreHTML.instructions.types.d.mts +9 -1
  79. package/dist/node/instructions/AreHTML.instructions.types.d.ts +9 -1
  80. package/dist/node/lib/AreDirective/AreDirective.component.d.mts +1 -1
  81. package/dist/node/lib/AreDirective/AreDirective.component.d.ts +1 -1
  82. package/dist/node/lib/AreDirective/AreDirective.types.d.mts +1 -1
  83. package/dist/node/lib/AreDirective/AreDirective.types.d.ts +1 -1
  84. package/dist/node/lib/AreHTML/AreHTML.tokenizer.d.mts +1 -1
  85. package/dist/node/lib/AreHTML/AreHTML.tokenizer.d.ts +1 -1
  86. package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.d.mts +1 -1
  87. package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.d.ts +1 -1
  88. package/dist/node/lib/AreHTMLNode/AreHTMLNode.d.mts +1 -1
  89. package/dist/node/lib/AreHTMLNode/AreHTMLNode.d.ts +1 -1
  90. package/dist/node/lib/AreHTMLNode/AreHTMLNode.js +51 -0
  91. package/dist/node/lib/AreHTMLNode/AreHTMLNode.js.map +1 -1
  92. package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs +51 -0
  93. package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs.map +1 -1
  94. package/dist/node/lib/AreRoot/AreRoot.component.js.map +1 -1
  95. package/dist/node/lib/AreRoot/AreRoot.component.mjs.map +1 -1
  96. package/dist/node/nodes/AreComment.d.mts +1 -1
  97. package/dist/node/nodes/AreComment.d.ts +1 -1
  98. package/dist/node/nodes/AreComponent.d.mts +1 -1
  99. package/dist/node/nodes/AreComponent.d.ts +1 -1
  100. package/dist/node/nodes/AreInterpolation.d.mts +1 -1
  101. package/dist/node/nodes/AreInterpolation.d.ts +1 -1
  102. package/dist/node/nodes/AreRoot.d.mts +1 -1
  103. package/dist/node/nodes/AreRoot.d.ts +1 -1
  104. package/dist/node/nodes/AreText.d.mts +1 -1
  105. package/dist/node/nodes/AreText.d.ts +1 -1
  106. package/examples/dashboard/concept.ts +1 -1
  107. package/examples/dashboard/dist/index.html +1 -1
  108. package/examples/dashboard/dist/{mqh9ryml-xat335.js → mqiw5sqa-ypckmj.js} +403 -57
  109. package/examples/for-perf/dist/index.html +1 -1
  110. package/examples/for-perf/dist/{mqh9ryfo-6a8d0o.js → mqj1mpf2-z4aokv.js} +558 -117
  111. package/examples/for-perf/dist/{mqh9ryfq-4pf5cv.js → mqj1mpff-4fr7mw.js} +558 -117
  112. package/examples/signal-routing/dist/index.html +1 -1
  113. package/examples/signal-routing/dist/{mqh9ryc9-dkcbkx.js → mqiwo23h-bhcolu.js} +413 -60
  114. package/package.json +5 -5
  115. package/src/directives/AreDirectiveFor.directive.ts +99 -16
  116. package/src/engine/AreHTML.compiler.ts +13 -0
  117. package/src/engine/AreHTML.constants.ts +142 -0
  118. package/src/engine/AreHTML.context.ts +112 -0
  119. package/src/engine/AreHTML.interpreter.ts +114 -13
  120. package/src/engine/AreHTML.lifecycle.ts +81 -74
  121. package/src/engine/AreHTML.tokenizer.ts +30 -1
  122. package/src/index.ts +1 -0
  123. package/src/instructions/AddStaticHTML.instruction.ts +23 -0
  124. package/src/instructions/AreHTML.instructions.constants.ts +1 -0
  125. package/src/instructions/AreHTML.instructions.types.ts +9 -0
  126. package/src/lib/AreHTMLNode/AreHTMLNode.ts +74 -0
  127. package/src/lib/AreRoot/AreRoot.component.ts +3 -3
  128. package/tests/StaticIsland.test.ts +115 -0
  129. package/examples/for-perf/dist/mqh9ryde-m243t8.js +0 -15223
@@ -2845,8 +2845,8 @@ var __decorateClass2 = /* @__PURE__ */ __name((decorators, target, key, kind) =>
2845
2845
  }, "__decorateClass");
2846
2846
  var __decorateParam2 = /* @__PURE__ */ __name((index, decorator) => (target, key) => decorator(target, key, index), "__decorateParam");
2847
2847
 
2848
- // node_modules/@adaas/a-frame/dist/browser/chunk-UWXOV5XO.mjs
2849
- var t = "0.1.15";
2848
+ // node_modules/@adaas/a-frame/dist/browser/chunk-VBGV4GJ5.mjs
2849
+ var t = "0.1.16";
2850
2850
 
2851
2851
  // node_modules/@adaas/a-frame/dist/browser/chunk-IKIN4MJV.mjs
2852
2852
  var h2 = Object.defineProperty;
@@ -2858,7 +2858,7 @@ var k2 = /* @__PURE__ */ __name((c3, a4, e, d4) => {
2858
2858
  }, "k");
2859
2859
  var l = /* @__PURE__ */ __name((c3, a4) => (e, d4) => a4(e, d4, c3), "l");
2860
2860
 
2861
- // node_modules/@adaas/a-frame/dist/browser/chunk-PTHJM2MP.mjs
2861
+ // node_modules/@adaas/a-frame/dist/browser/chunk-6K7VISNV.mjs
2862
2862
  var _a39;
2863
2863
  var _2 = (_a39 = class extends H {
2864
2864
  get A_FRAME_TOKEN() {
@@ -3468,7 +3468,7 @@ var n3 = (_a48 = class extends D {
3468
3468
  j2(n3, "A_FrameDefinition");
3469
3469
  var h3 = n3;
3470
3470
 
3471
- // node_modules/@adaas/a-frame/dist/browser/chunk-FYZWOISU.mjs
3471
+ // node_modules/@adaas/a-frame/dist/browser/chunk-3ROGJ3YE.mjs
3472
3472
  var _a49;
3473
3473
  var z2 = (_a49 = class extends H {
3474
3474
  constructor() {
@@ -4439,7 +4439,7 @@ var a3 = (_a64 = class extends D {
4439
4439
  j2(a3, "A_FrameSchema");
4440
4440
  var s2 = a3;
4441
4441
 
4442
- // node_modules/@adaas/a-frame/dist/browser/chunk-PIH5MHMV.mjs
4442
+ // node_modules/@adaas/a-frame/dist/browser/chunk-4KUCX5R6.mjs
4443
4443
  var d3 = { Request: "_A_FRAME_REQUEST", Socket: "_A_FRAME_SOCKET", Stream: "_A_FRAME_STREAM", Send: "_A_FRAME_SEND" };
4444
4444
  var _a65;
4445
4445
  var J2 = (_a65 = class extends H {
@@ -5008,7 +5008,7 @@ var l3 = (_a76 = class extends O {
5008
5008
  j2(l3, "A_FrameChannel"), k2([N.Extend({ name: r4.Embed, scope: [h3] }), l(0, Yt(te)), l(1, Yt(f3))], l3.prototype, "embedDefinition", 1), k2([N.Extend({ name: m3.Embed, scope: [w2] }), l(0, Yt(te)), l(1, Yt(f3))], l3.prototype, "embedNamespace", 1), k2([N.Extend({ name: r5.Embed, scope: [_4] }), l(0, Yt(te)), l(1, Yt(f3))], l3.prototype, "embedSegment", 1), k2([N.Extend({ name: D3.Generate, scope: [O3] }), l(0, Yt(te)), l(1, Yt(v4))], l3.prototype, "generateDynamicStructure", 1), k2([N.Extend({ name: D3.Patch, scope: [O3] }), l(0, Yt(te)), l(1, Yt(v4))], l3.prototype, "patchDynamicStructure", 1), k2([N.Extend({ name: C4.Generate, scope: [P3] }), l(0, Yt(te)), l(1, Yt(w5))], l3.prototype, "generateDynamicFeature", 1), k2([N.Extend({ name: C4.Patch, scope: [P3] }), l(0, Yt(te)), l(1, Yt(w5))], l3.prototype, "patchDynamicFeature", 1), k2([N.Extend({ name: F3.Generate, scope: [w4] }), l(0, Yt(te)), l(1, Yt(f2))], l3.prototype, "generateDynamicContent", 1), k2([N.Extend({ name: F3.Patch, scope: [w4] }), l(0, Yt(te)), l(1, Yt(f2))], l3.prototype, "patchDynamicContent", 1), k2([N.Extend({ name: n4.Generate, scope: [m4] }), l(0, Yt(te)), l(1, Yt(f3))], l3.prototype, "generateCompletion", 1), k2([N.Extend({ name: r6.Extract, scope: [s2] }), l(0, Yt(te)), l(1, Yt(f3))], l3.prototype, "extractSchema", 1), k2([N.Extend({ name: r6.ExtractMultiple, scope: [s2] }), l(0, Yt(te)), l(1, Yt(f3))], l3.prototype, "extractSchemaMultiple", 1);
5009
5009
  var Fe2 = l3;
5010
5010
 
5011
- // node_modules/@adaas/a-frame/dist/browser/chunk-2O3HAEYW.mjs
5011
+ // node_modules/@adaas/a-frame/dist/browser/chunk-P6WICUUU.mjs
5012
5012
  var _a77;
5013
5013
  var r7 = (_a77 = class extends Fe2 {
5014
5014
  async request(e, a4) {
@@ -8809,7 +8809,9 @@ var AreScene = (_a127 = class extends H {
8809
8809
  super({ name: id.toString() });
8810
8810
  this._groupToInstructionsMap = /* @__PURE__ */ new Map();
8811
8811
  this._plan = [];
8812
+ this._planIndex = /* @__PURE__ */ new Map();
8812
8813
  this._state = [];
8814
+ this._stateIndex = /* @__PURE__ */ new Map();
8813
8815
  this._status = AreSceneStatuses.Active;
8814
8816
  }
8815
8817
  /**
@@ -8969,6 +8971,7 @@ var AreScene = (_a127 = class extends H {
8969
8971
  } catch (error) {
8970
8972
  }
8971
8973
  this._plan.push(instruction);
8974
+ this._planIndex.set(instruction.aseid.toString(), instruction);
8972
8975
  if (!this._groupToInstructionsMap.has(instruction.group || "default")) {
8973
8976
  this._groupToInstructionsMap.set(instruction.group || "default", /* @__PURE__ */ new Set());
8974
8977
  }
@@ -8989,6 +8992,7 @@ var AreScene = (_a127 = class extends H {
8989
8992
  } catch (error) {
8990
8993
  }
8991
8994
  this._plan.splice(beforeIndex, 0, instruction);
8995
+ this._planIndex.set(instruction.aseid.toString(), instruction);
8992
8996
  } else {
8993
8997
  this._plan.splice(instructionIndex, 1);
8994
8998
  this._plan.splice(beforeIndex, 0, instruction);
@@ -9006,6 +9010,7 @@ var AreScene = (_a127 = class extends H {
9006
9010
  if (instructionIndex === -1) {
9007
9011
  this.scope.register(instruction);
9008
9012
  this._plan.splice(afterIndex + 1, 0, instruction);
9013
+ this._planIndex.set(instruction.aseid.toString(), instruction);
9009
9014
  } else {
9010
9015
  this._plan.splice(instructionIndex, 1);
9011
9016
  this._plan.splice(afterIndex + 1, 0, instruction);
@@ -9035,7 +9040,9 @@ var AreScene = (_a127 = class extends H {
9035
9040
  * @param instruction
9036
9041
  */
9037
9042
  unPlan(instruction) {
9038
- this._plan = this._plan.filter((i6) => i6.aseid.toString() !== instruction.aseid.toString());
9043
+ const key = instruction.aseid.toString();
9044
+ if (!this._planIndex.delete(key)) return;
9045
+ this._plan = this._plan.filter((i6) => i6.aseid.toString() !== key);
9039
9046
  }
9040
9047
  /**
9041
9048
  * Checks if the instruction is already in the plan, so it will be rendered in the next render cycle.
@@ -9044,8 +9051,7 @@ var AreScene = (_a127 = class extends H {
9044
9051
  * @returns
9045
9052
  */
9046
9053
  getPlanned(instruction) {
9047
- const found = this._plan.find((i6) => i6.aseid.toString() === instruction.aseid.toString());
9048
- return found;
9054
+ return this._planIndex.get(instruction.aseid.toString());
9049
9055
  }
9050
9056
  /**
9051
9057
  * Checks if the instruction is already in the plan, so it will be rendered in the next render cycle.
@@ -9054,7 +9060,7 @@ var AreScene = (_a127 = class extends H {
9054
9060
  * @returns
9055
9061
  */
9056
9062
  isInPlan(instruction) {
9057
- return !!this.getPlanned(instruction);
9063
+ return this._planIndex.has(instruction.aseid.toString());
9058
9064
  }
9059
9065
  // -------------------------------------------------------------------------------------------------------------
9060
9066
  // Scene Apply Methods
@@ -9065,8 +9071,10 @@ var AreScene = (_a127 = class extends H {
9065
9071
  * @param instruction
9066
9072
  */
9067
9073
  apply(instruction) {
9068
- if (!this.isApplied(instruction)) {
9074
+ const key = instruction.aseid.toString();
9075
+ if (!this._stateIndex.has(key)) {
9069
9076
  this._state.push(instruction);
9077
+ this._stateIndex.set(key, instruction);
9070
9078
  }
9071
9079
  }
9072
9080
  /**
@@ -9075,7 +9083,9 @@ var AreScene = (_a127 = class extends H {
9075
9083
  * @param instruction
9076
9084
  */
9077
9085
  unApply(instruction) {
9078
- this._state = this._state.filter((i6) => i6.aseid.toString() !== instruction.aseid.toString());
9086
+ const key = instruction.aseid.toString();
9087
+ if (!this._stateIndex.delete(key)) return;
9088
+ this._state = this._state.filter((i6) => i6.aseid.toString() !== key);
9079
9089
  }
9080
9090
  /**
9081
9091
  * Checks if the instruction is already in the state, so it is currently applied to the scene.
@@ -9084,8 +9094,7 @@ var AreScene = (_a127 = class extends H {
9084
9094
  * @returns
9085
9095
  */
9086
9096
  getApplied(instruction) {
9087
- const found = this._state.find((i6) => i6.aseid.toString() === instruction.aseid.toString());
9088
- return found;
9097
+ return this._stateIndex.get(instruction.aseid.toString());
9089
9098
  }
9090
9099
  /**
9091
9100
  * Checks if the instruction is already in the state, so it is currently applied to the scene.
@@ -9094,7 +9103,7 @@ var AreScene = (_a127 = class extends H {
9094
9103
  * @returns
9095
9104
  */
9096
9105
  isApplied(instruction) {
9097
- return !!this.getApplied(instruction);
9106
+ return this._stateIndex.has(instruction.aseid.toString());
9098
9107
  }
9099
9108
  /**
9100
9109
  * Method that should reset the scene to the initial state, so it will clear the plan and state, but it will not deregister the instructions from the scene scope, so they will still be registered in the scene and can be planned and applied again if needed.
@@ -9103,6 +9112,8 @@ var AreScene = (_a127 = class extends H {
9103
9112
  reset() {
9104
9113
  this._plan = [];
9105
9114
  this._state = [];
9115
+ this._planIndex.clear();
9116
+ this._stateIndex.clear();
9106
9117
  }
9107
9118
  }, __name(_a127, "AreScene"), _a127);
9108
9119
  AreScene = __decorateClass3([
@@ -12440,6 +12451,7 @@ var AreHTMLInstructions = {
12440
12451
  AddListener: "_AreHTML_AddListener",
12441
12452
  AddInterpolation: "_AreHTML_AddInterpolation",
12442
12453
  AddComment: "_AreHTML_AddComment",
12454
+ AddStaticHTML: "_AreHTML_AddStaticHTML",
12443
12455
  HideElement: "_AreHTML_HideElement"
12444
12456
  };
12445
12457
 
@@ -12495,11 +12507,109 @@ var AreHTMLEngineContext = class extends AreContext {
12495
12507
  */
12496
12508
  elementListeners: /* @__PURE__ */ new WeakMap()
12497
12509
  };
12510
+ /**
12511
+ * Parsed-fragment cache for static islands (see AddStaticHTMLInstruction).
12512
+ *
12513
+ * Keyed by `hostTag\u0000markup`, each entry holds a `DocumentFragment` whose
12514
+ * children were parsed by the browser exactly once — in the *correct element
12515
+ * context* (the host tag), so table fragments (`<tr>`, `<td>`, …) and other
12516
+ * context-sensitive content parse correctly. Repeated static islands with
12517
+ * identical markup (e.g. list rows, reused components) clone the pre-parsed
12518
+ * fragment instead of re-parsing the HTML string on every mount — turning an
12519
+ * O(parse) operation into an O(clone) one.
12520
+ */
12521
+ this._staticFragmentCache = /* @__PURE__ */ new Map();
12522
+ /**
12523
+ * Live-DOM attachments deferred while a mount pass is batching.
12524
+ *
12525
+ * A freshly-mounted subtree is built inside a *detached* root element, so
12526
+ * every descendant `appendChild`/`insertBefore` happens off-document and
12527
+ * triggers zero layout/paint invalidation. The single mutation that actually
12528
+ * connects the built subtree to the live document is deferred and collected
12529
+ * here, then flushed once when the batch closes — collapsing O(nodes) reflows
12530
+ * into O(1) per mount root.
12531
+ */
12532
+ this._pendingAttachments = [];
12533
+ /**
12534
+ * Depth of the currently open batching scopes. Re-entrant so that nested
12535
+ * `beginBatch`/`endBatch` pairs flush exactly once, when the outermost scope
12536
+ * closes.
12537
+ */
12538
+ this._batchDepth = 0;
12498
12539
  this._container = props.container;
12499
12540
  }
12500
12541
  get container() {
12501
12542
  return this._container;
12502
12543
  }
12544
+ /**
12545
+ * `true` while a synchronous mount pass is batching live-DOM attachments.
12546
+ * Interpreter handlers consult this to decide whether to attach an element
12547
+ * immediately or hand the attachment to {@link deferAttach}.
12548
+ */
12549
+ get isBatching() {
12550
+ return this._batchDepth > 0;
12551
+ }
12552
+ /**
12553
+ * Opens a batching scope. Re-entrant: only the outermost matching
12554
+ * {@link endBatch} flushes the deferred attachments, so a single mount pass
12555
+ * connects its built subtree to the live DOM exactly once.
12556
+ */
12557
+ beginBatch() {
12558
+ this._batchDepth++;
12559
+ }
12560
+ /**
12561
+ * Registers a live-DOM attachment to run when the current batch flushes. If
12562
+ * no batch is active the attachment runs immediately, preserving the original
12563
+ * synchronous behaviour for updates that mount outside a batch.
12564
+ *
12565
+ * @param attach the DOM mutation that connects a built subtree to the document
12566
+ */
12567
+ deferAttach(attach) {
12568
+ if (this._batchDepth > 0) {
12569
+ this._pendingAttachments.push(attach);
12570
+ } else {
12571
+ attach();
12572
+ }
12573
+ }
12574
+ /**
12575
+ * Closes a batching scope. When the outermost scope closes, every deferred
12576
+ * attachment runs in registration (document) order, connecting the built
12577
+ * subtrees to the live DOM in a single pass.
12578
+ */
12579
+ endBatch() {
12580
+ if (this._batchDepth === 0) return;
12581
+ this._batchDepth--;
12582
+ if (this._batchDepth > 0) return;
12583
+ const pending = this._pendingAttachments;
12584
+ this._pendingAttachments = [];
12585
+ for (let i6 = 0; i6 < pending.length; i6++) {
12586
+ pending[i6]();
12587
+ }
12588
+ }
12589
+ /**
12590
+ * Returns a `DocumentFragment` containing the parsed form of `html`, parsed
12591
+ * once in the context of `hostTag` (so context-sensitive content such as
12592
+ * table rows/cells parses correctly) and cached thereafter. Callers should
12593
+ * `cloneNode(true)` the returned fragment rather than mutating it, so the
12594
+ * cache stays reusable.
12595
+ *
12596
+ * @param hostTag the tag name of the element the markup will be injected into
12597
+ * @param html verbatim static-island inner markup
12598
+ */
12599
+ getStaticFragment(hostTag, html) {
12600
+ const key = `${hostTag}\0${html}`;
12601
+ let fragment = this._staticFragmentCache.get(key);
12602
+ if (!fragment) {
12603
+ const container = this._container.createElement(hostTag);
12604
+ container.innerHTML = html;
12605
+ fragment = this._container.createDocumentFragment();
12606
+ while (container.firstChild) {
12607
+ fragment.appendChild(container.firstChild);
12608
+ }
12609
+ this._staticFragmentCache.set(key, fragment);
12610
+ }
12611
+ return fragment;
12612
+ }
12503
12613
  getNodeElement(node) {
12504
12614
  if (typeof node === "string") {
12505
12615
  return this.index.nodeToHostElements.get(node);
@@ -12753,6 +12863,171 @@ function toDOMString(value) {
12753
12863
  }
12754
12864
  }
12755
12865
  __name(toDOMString, "toDOMString");
12866
+ var STANDARD_HTML_TAGS = /* @__PURE__ */ new Set([
12867
+ // root / sections
12868
+ "html",
12869
+ "body",
12870
+ "header",
12871
+ "footer",
12872
+ "main",
12873
+ "nav",
12874
+ "section",
12875
+ "article",
12876
+ "aside",
12877
+ "address",
12878
+ "hgroup",
12879
+ // headings
12880
+ "h1",
12881
+ "h2",
12882
+ "h3",
12883
+ "h4",
12884
+ "h5",
12885
+ "h6",
12886
+ // grouping
12887
+ "div",
12888
+ "p",
12889
+ "span",
12890
+ "pre",
12891
+ "blockquote",
12892
+ "figure",
12893
+ "figcaption",
12894
+ "hr",
12895
+ "br",
12896
+ "wbr",
12897
+ // lists
12898
+ "ul",
12899
+ "ol",
12900
+ "li",
12901
+ "dl",
12902
+ "dt",
12903
+ "dd",
12904
+ "menu",
12905
+ // text-level / phrasing
12906
+ "a",
12907
+ "b",
12908
+ "i",
12909
+ "u",
12910
+ "s",
12911
+ "em",
12912
+ "strong",
12913
+ "small",
12914
+ "mark",
12915
+ "abbr",
12916
+ "cite",
12917
+ "q",
12918
+ "code",
12919
+ "kbd",
12920
+ "samp",
12921
+ "var",
12922
+ "sub",
12923
+ "sup",
12924
+ "time",
12925
+ "data",
12926
+ "dfn",
12927
+ "bdi",
12928
+ "bdo",
12929
+ "ruby",
12930
+ "rt",
12931
+ "rp",
12932
+ "del",
12933
+ "ins",
12934
+ // media / embedded (no special namespace handling needed)
12935
+ "img",
12936
+ "picture",
12937
+ "source",
12938
+ "figure",
12939
+ "audio",
12940
+ "video",
12941
+ "track",
12942
+ // tables
12943
+ "table",
12944
+ "caption",
12945
+ "colgroup",
12946
+ "col",
12947
+ "thead",
12948
+ "tbody",
12949
+ "tfoot",
12950
+ "tr",
12951
+ "th",
12952
+ "td",
12953
+ // forms (display only — these still render fine from innerHTML)
12954
+ "label",
12955
+ "fieldset",
12956
+ "legend",
12957
+ "datalist",
12958
+ "option",
12959
+ "optgroup",
12960
+ "output",
12961
+ "progress",
12962
+ "meter",
12963
+ // interactive
12964
+ "details",
12965
+ "summary",
12966
+ "dialog"
12967
+ ]);
12968
+ function isStaticMarkup(inner) {
12969
+ if (!inner) return false;
12970
+ if (inner.indexOf("{{") !== -1) return false;
12971
+ const n6 = inner.length;
12972
+ let i6 = 0;
12973
+ while (i6 < n6) {
12974
+ const lt = inner.indexOf("<", i6);
12975
+ if (lt === -1) break;
12976
+ if (inner.startsWith("<!--", lt)) {
12977
+ const end = inner.indexOf("-->", lt + 4);
12978
+ if (end === -1) return false;
12979
+ i6 = end + 3;
12980
+ continue;
12981
+ }
12982
+ if (inner[lt + 1] === "/" || inner[lt + 1] === "!" || inner[lt + 1] === "?") {
12983
+ const gt2 = inner.indexOf(">", lt);
12984
+ if (gt2 === -1) return false;
12985
+ i6 = gt2 + 1;
12986
+ continue;
12987
+ }
12988
+ const nameMatch = /^<([a-zA-Z][a-zA-Z0-9-]*)/.exec(inner.slice(lt));
12989
+ if (!nameMatch) {
12990
+ i6 = lt + 1;
12991
+ continue;
12992
+ }
12993
+ const tag = nameMatch[1].toLowerCase();
12994
+ if (tag.indexOf("-") !== -1 || !STANDARD_HTML_TAGS.has(tag)) return false;
12995
+ let j5 = lt + nameMatch[0].length;
12996
+ let inSingle = false;
12997
+ let inDouble = false;
12998
+ let atNameBoundary = true;
12999
+ let tagEnd = -1;
13000
+ while (j5 < n6) {
13001
+ const ch = inner[j5];
13002
+ if (inDouble) {
13003
+ if (ch === '"') inDouble = false;
13004
+ } else if (inSingle) {
13005
+ if (ch === "'") inSingle = false;
13006
+ } else if (ch === '"') {
13007
+ inDouble = true;
13008
+ atNameBoundary = false;
13009
+ } else if (ch === "'") {
13010
+ inSingle = true;
13011
+ atNameBoundary = false;
13012
+ } else if (ch === ">") {
13013
+ tagEnd = j5;
13014
+ break;
13015
+ } else if (ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "/") {
13016
+ atNameBoundary = true;
13017
+ } else {
13018
+ if (atNameBoundary && (ch === "$" || ch === ":" || ch === "@")) {
13019
+ return false;
13020
+ }
13021
+ atNameBoundary = false;
13022
+ }
13023
+ j5++;
13024
+ }
13025
+ if (tagEnd === -1) return false;
13026
+ i6 = tagEnd + 1;
13027
+ }
13028
+ return true;
13029
+ }
13030
+ __name(isStaticMarkup, "isStaticMarkup");
12756
13031
 
12757
13032
  // src/engine/AreHTML.interpreter.ts
12758
13033
  var AreHTMLInterpreter = class extends AreInterpreter {
@@ -12779,12 +13054,15 @@ var AreHTMLInterpreter = class extends AreInterpreter {
12779
13054
  });
12780
13055
  }
12781
13056
  const element = isSVG ? context.container.createElementNS(SVG_NAMESPACE, tag) : context.container.createElement(tag);
12782
- if (mountPoint.nodeType === Node.ELEMENT_NODE) {
12783
- mountPoint.appendChild(element);
12784
- } else {
13057
+ context.setInstructionElement(declaration, element);
13058
+ const attach = mountPoint.nodeType === Node.ELEMENT_NODE ? () => mountPoint.appendChild(element) : () => {
12785
13059
  mountPoint.parentNode?.insertBefore(element, mountPoint);
13060
+ };
13061
+ if (context.isBatching && mountPoint.isConnected) {
13062
+ context.deferAttach(attach);
13063
+ } else {
13064
+ attach();
12786
13065
  }
12787
- context.setInstructionElement(declaration, element);
12788
13066
  } else {
12789
13067
  const mountPoint = context.container.getElementById(node.id);
12790
13068
  if (!mountPoint) {
@@ -12794,8 +13072,15 @@ var AreHTMLInterpreter = class extends AreInterpreter {
12794
13072
  });
12795
13073
  }
12796
13074
  const element = isSVG ? context.container.createElementNS(SVG_NAMESPACE, tag) : context.container.createElement(tag);
12797
- mountPoint.parentNode?.replaceChild(element, mountPoint);
12798
13075
  context.setInstructionElement(declaration, element);
13076
+ const attach = /* @__PURE__ */ __name(() => {
13077
+ mountPoint.parentNode?.replaceChild(element, mountPoint);
13078
+ }, "attach");
13079
+ if (context.isBatching && mountPoint.isConnected) {
13080
+ context.deferAttach(attach);
13081
+ } else {
13082
+ attach();
13083
+ }
12799
13084
  }
12800
13085
  logger?.debug("green", `Element ${node.aseid.toString()} added to Context:`);
12801
13086
  } catch (error) {
@@ -12805,7 +13090,7 @@ var AreHTMLInterpreter = class extends AreInterpreter {
12805
13090
  }
12806
13091
  removeElement(declaration, context) {
12807
13092
  const element = context.getElementByInstruction(declaration);
12808
- if (element && element.parentNode) {
13093
+ if (element && element.parentNode && element.isConnected) {
12809
13094
  element.parentNode.removeChild(element);
12810
13095
  }
12811
13096
  context.removeInstructionElement(declaration);
@@ -12908,7 +13193,7 @@ var AreHTMLInterpreter = class extends AreInterpreter {
12908
13193
  const element = context.getElementByInstruction(mutation.parent);
12909
13194
  if (!element) return;
12910
13195
  const { name } = mutation.payload;
12911
- if (name && element.nodeType === Node.ELEMENT_NODE) {
13196
+ if (name && element.nodeType === Node.ELEMENT_NODE && element.isConnected) {
12912
13197
  const colonIdx = name.indexOf(":");
12913
13198
  if (colonIdx > 0) {
12914
13199
  const ns = SVG_ATTRIBUTE_NS[name.slice(0, colonIdx)];
@@ -12935,6 +13220,7 @@ var AreHTMLInterpreter = class extends AreInterpreter {
12935
13220
  showElement(mutation, context) {
12936
13221
  const element = context.getElementByInstruction(mutation.parent);
12937
13222
  if (!element || element.nodeType !== Node.ELEMENT_NODE) return;
13223
+ if (!element.isConnected) return;
12938
13224
  const el = element;
12939
13225
  el.style.display = mutation.payload?.display ?? mutation.cache ?? "";
12940
13226
  }
@@ -13029,7 +13315,9 @@ var AreHTMLInterpreter = class extends AreInterpreter {
13029
13315
  const { event: eventName } = parseEventName(name);
13030
13316
  const listener = mutation.payload._callback;
13031
13317
  if (listener) {
13032
- element.removeEventListener(eventName, listener);
13318
+ if (element.isConnected) {
13319
+ element.removeEventListener(eventName, listener);
13320
+ }
13033
13321
  context.removeListener(element, name, listener);
13034
13322
  mutation.payload._callback = void 0;
13035
13323
  }
@@ -13067,9 +13355,32 @@ var AreHTMLInterpreter = class extends AreInterpreter {
13067
13355
  removeText(declaration, context) {
13068
13356
  const element = context.getElementByInstruction(declaration);
13069
13357
  if (!element) return;
13070
- element.parentNode?.removeChild(element);
13358
+ if (element.isConnected) {
13359
+ element.parentNode?.removeChild(element);
13360
+ }
13071
13361
  context.removeInstructionElement(declaration);
13072
13362
  }
13363
+ addStaticHTML(mutation, context, logger) {
13364
+ const element = context.getElementByInstruction(mutation.parent);
13365
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) {
13366
+ throw new AreInterpreterError({
13367
+ title: "Element Not Found",
13368
+ description: `Could not find a DOM element associated with the instruction ASEID "${mutation.parent}". Ensure the host element is rendered before materialising its static island.`
13369
+ });
13370
+ }
13371
+ const el = element;
13372
+ const { html } = mutation.payload;
13373
+ el.textContent = "";
13374
+ const fragment = context.getStaticFragment(el.tagName.toLowerCase(), html);
13375
+ el.appendChild(fragment.cloneNode(true));
13376
+ logger?.debug("green", `Static island materialised onto <${(mutation.owner.parent ?? mutation.owner)?.aseid?.toString?.()}>`);
13377
+ }
13378
+ removeStaticHTML(mutation, context) {
13379
+ const element = context.getElementByInstruction(mutation.parent);
13380
+ if (element && element.nodeType === Node.ELEMENT_NODE && element.isConnected) {
13381
+ element.textContent = "";
13382
+ }
13383
+ }
13073
13384
  addComment(declaration, context, store, syntax, directiveContext, logger) {
13074
13385
  const node = declaration.owner.parent;
13075
13386
  const { content, evaluate } = declaration.payload;
@@ -13103,7 +13414,9 @@ var AreHTMLInterpreter = class extends AreInterpreter {
13103
13414
  removeComment(declaration, context) {
13104
13415
  const element = context.getElementByInstruction(declaration);
13105
13416
  if (!element) return;
13106
- element.parentNode?.removeChild(element);
13417
+ if (element.isConnected) {
13418
+ element.parentNode?.removeChild(element);
13419
+ }
13107
13420
  context.removeInstructionElement(declaration);
13108
13421
  }
13109
13422
  addStyle(mutation, context, logger) {
@@ -13248,6 +13561,24 @@ __decorateClass([
13248
13561
  __decorateParam(0, Yt(te)),
13249
13562
  __decorateParam(1, Yt(AreHTMLEngineContext))
13250
13563
  ], AreHTMLInterpreter.prototype, "removeText", 1);
13564
+ __decorateClass([
13565
+ R4.Define({
13566
+ description: "Inject a static island's inner markup onto its host element in one pass via a cached, browser-parsed <template> clone. Decodes HTML entities natively."
13567
+ }),
13568
+ AreInterpreter.Apply(AreHTMLInstructions.AddStaticHTML),
13569
+ AreInterpreter.Update(AreHTMLInstructions.AddStaticHTML),
13570
+ __decorateParam(0, Yt(te)),
13571
+ __decorateParam(1, Yt(AreHTMLEngineContext)),
13572
+ __decorateParam(2, Yt(A_Logger))
13573
+ ], AreHTMLInterpreter.prototype, "addStaticHTML", 1);
13574
+ __decorateClass([
13575
+ R4.Define({
13576
+ description: "Clear a static island's injected markup from its host element on revert."
13577
+ }),
13578
+ AreInterpreter.Revert(AreHTMLInstructions.AddStaticHTML),
13579
+ __decorateParam(0, Yt(te)),
13580
+ __decorateParam(1, Yt(AreHTMLEngineContext))
13581
+ ], AreHTMLInterpreter.prototype, "removeStaticHTML", 1);
13251
13582
  __decorateClass([
13252
13583
  R4.Define({
13253
13584
  description: "Add a comment node to the DOM based on the provided declaration instruction."
@@ -13392,6 +13723,57 @@ var AreHTMLNode = class extends AreNode {
13392
13723
  get tag() {
13393
13724
  return this.aseid.entity;
13394
13725
  }
13726
+ /**
13727
+ * The verbatim inner markup captured when this node was identified as a
13728
+ * static island, or `undefined` for ordinary (per-node) nodes.
13729
+ */
13730
+ get staticInnerHTML() {
13731
+ return this._staticInnerHTML;
13732
+ }
13733
+ /**
13734
+ * Whether this node is a static-island root (see `_staticInnerHTML`).
13735
+ */
13736
+ get isStaticIsland() {
13737
+ return this._staticInnerHTML !== void 0;
13738
+ }
13739
+ /**
13740
+ * Marks this node as a static-island root, capturing the verbatim inner
13741
+ * markup to be materialised in one shot by the interpreter. Called by the
13742
+ * tokenizer when the node's inner content is detected to be fully static.
13743
+ */
13744
+ markStatic(innerHTML) {
13745
+ this._staticInnerHTML = innerHTML;
13746
+ }
13747
+ /**
13748
+ * Deep-clone the node. Overridden to carry over the static-island marker
13749
+ * (`_staticInnerHTML`), which lives on AreHTMLNode and is therefore NOT
13750
+ * copied by the base AreNode.clone(). Without this, cloning a directive
13751
+ * template ($if/$for) that wraps a static island (e.g. `<span $if>★</span>`)
13752
+ * would drop the captured inner markup and render an empty element. The
13753
+ * base clone() recurses via each child's polymorphic clone(), so nested
13754
+ * island children are preserved automatically through this override.
13755
+ */
13756
+ clone() {
13757
+ const cloned = super.clone();
13758
+ const self = this;
13759
+ if (self._staticInnerHTML !== void 0)
13760
+ cloned.markStatic(self._staticInnerHTML);
13761
+ return cloned;
13762
+ }
13763
+ /**
13764
+ * Clone the node while transferring its existing scope to the clone (used by
13765
+ * the $if/$for directives to turn the original node into a lightweight group
13766
+ * container). Overridden for the same reason as `clone()`: the static-island
13767
+ * marker must survive so a directive applied to an island root keeps its
13768
+ * inner markup.
13769
+ */
13770
+ cloneWithScope() {
13771
+ const cloned = super.cloneWithScope();
13772
+ const self = this;
13773
+ if (self._staticInnerHTML !== void 0)
13774
+ cloned.markStatic(self._staticInnerHTML);
13775
+ return cloned;
13776
+ }
13395
13777
  /**
13396
13778
  * The static attributes defined for the node, which are typically used to represent static properties or characteristics of the node that do not change based on the context or state. These attributes are usually defined in the template and are not reactive.
13397
13779
  *
@@ -13575,7 +13957,12 @@ var AreHTMLTokenizer = class extends AreTokenizer {
13575
13957
  this.ATTR_PATTERN = /([$:@]?[\w.-]+(?::[\w.-]+)?)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>/"'=]+)))?/g;
13576
13958
  }
13577
13959
  tokenize(node, context, logger) {
13578
- super.tokenize(node, context, logger);
13960
+ const isStaticIsland = node instanceof AreComponentNode && !!node.content && isStaticMarkup(node.content);
13961
+ if (isStaticIsland) {
13962
+ node.markStatic(node.content);
13963
+ } else {
13964
+ super.tokenize(node, context, logger);
13965
+ }
13579
13966
  context.startPerformance("attributeExtraction");
13580
13967
  const attributes = this.extractAttributes(node.markup);
13581
13968
  for (const attr of attributes) {
@@ -13649,44 +14036,6 @@ var AreDirectiveFeatures = {
13649
14036
  Update: "_AreDirective_Update"
13650
14037
  };
13651
14038
 
13652
- // src/helpers/AreScheduler.helper.ts
13653
- var _AreSchedulerHelper = class _AreSchedulerHelper {
13654
- /**
13655
- * High-resolution wall-clock time in milliseconds. Uses `performance.now()`
13656
- * when available (monotonic, sub-millisecond), falling back to `Date.now()`.
13657
- */
13658
- static now() {
13659
- return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
13660
- }
13661
- /**
13662
- * Schedule `fn` to run on the next macrotask.
13663
- *
13664
- * `MessageChannel` yields a true macrotask without the ~4ms clamp that nested
13665
- * `setTimeout(0)` calls incur, so the browser can paint between chunks with
13666
- * minimal scheduling overhead. Falls back to `setTimeout` in non-DOM
13667
- * environments (e.g. tests / SSR).
13668
- */
13669
- static scheduleMacrotask(fn) {
13670
- if (typeof MessageChannel === "undefined") {
13671
- setTimeout(fn, 0);
13672
- return;
13673
- }
13674
- if (!this._channel) {
13675
- this._channel = new MessageChannel();
13676
- this._channel.port1.onmessage = () => {
13677
- const next = this._queue.shift();
13678
- if (next) next();
13679
- };
13680
- }
13681
- this._queue.push(fn);
13682
- this._channel.port2.postMessage(null);
13683
- }
13684
- };
13685
- __name(_AreSchedulerHelper, "AreSchedulerHelper");
13686
- /** FIFO queue of callbacks waiting for their posted macrotask to fire. */
13687
- _AreSchedulerHelper._queue = [];
13688
- var AreSchedulerHelper = _AreSchedulerHelper;
13689
-
13690
14039
  // src/engine/AreHTML.lifecycle.ts
13691
14040
  var AreHTMLLifecycle = class extends AreLifecycle {
13692
14041
  initComponent(node, scope, context, signalsContext, logger, ...args) {
@@ -13709,46 +14058,39 @@ var AreHTMLLifecycle = class extends AreLifecycle {
13709
14058
  mount(node, scene, logger, ...args) {
13710
14059
  logger?.debug(`[Mount] Component Trigger for <${node.aseid.entity}> with aseid :{${node.aseid.toString()}}`);
13711
14060
  if (scene.isInactive) return;
13712
- node.interpret();
13713
- const stack = [];
13714
- for (let i6 = node.children.length - 1; i6 >= 0; i6--) {
13715
- stack.push({ node: node.children[i6], entered: false });
13716
- }
13717
- const step = /* @__PURE__ */ __name(() => {
13718
- const frame = stack[stack.length - 1];
13719
- const current = frame.node;
13720
- if (frame.entered) {
13721
- stack.pop();
13722
- current.call(AreNodeFeatures.onAfterMount, current.scope);
13723
- return;
13724
- }
13725
- frame.entered = true;
13726
- current.call(AreNodeFeatures.onBeforeMount, current.scope);
13727
- if (!current.scene.isInactive) {
13728
- current.interpret();
13729
- for (let i6 = current.children.length - 1; i6 >= 0; i6--) {
13730
- stack.push({ node: current.children[i6], entered: false });
13731
- }
14061
+ const context = node.scope.resolve(AreHTMLEngineContext);
14062
+ context?.beginBatch();
14063
+ const afterMountQueue = [];
14064
+ try {
14065
+ node.interpret();
14066
+ const stack = [];
14067
+ for (let i6 = node.children.length - 1; i6 >= 0; i6--) {
14068
+ stack.push({ node: node.children[i6], entered: false });
13732
14069
  }
13733
- }, "step");
13734
- const drive = /* @__PURE__ */ __name(() => {
13735
- const start = AreSchedulerHelper.now();
13736
14070
  while (stack.length > 0) {
13737
- step();
13738
- if (stack.length > 0 && AreSchedulerHelper.now() - start >= AreHTMLLifecycle.MOUNT_BUDGET_MS) {
13739
- return new Promise((resolve, reject) => {
13740
- AreSchedulerHelper.scheduleMacrotask(() => {
13741
- try {
13742
- resolve(drive());
13743
- } catch (error) {
13744
- reject(error);
13745
- }
13746
- });
13747
- });
14071
+ const frame = stack[stack.length - 1];
14072
+ const current = frame.node;
14073
+ if (frame.entered) {
14074
+ stack.pop();
14075
+ afterMountQueue.push(current);
14076
+ continue;
14077
+ }
14078
+ frame.entered = true;
14079
+ current.call(AreNodeFeatures.onBeforeMount, current.scope);
14080
+ if (!current.scene.isInactive) {
14081
+ current.interpret();
14082
+ for (let i6 = current.children.length - 1; i6 >= 0; i6--) {
14083
+ stack.push({ node: current.children[i6], entered: false });
14084
+ }
13748
14085
  }
13749
14086
  }
13750
- }, "drive");
13751
- return drive();
14087
+ } finally {
14088
+ context?.endBatch();
14089
+ }
14090
+ for (let i6 = 0; i6 < afterMountQueue.length; i6++) {
14091
+ const mounted = afterMountQueue[i6];
14092
+ mounted.call(AreNodeFeatures.onAfterMount, mounted.scope);
14093
+ }
13752
14094
  }
13753
14095
  updateDirectiveAttribute(directive, scope, feature, logger, ...args) {
13754
14096
  if (directive.component) {
@@ -13759,13 +14101,6 @@ var AreHTMLLifecycle = class extends AreLifecycle {
13759
14101
  }
13760
14102
  };
13761
14103
  __name(AreHTMLLifecycle, "AreHTMLLifecycle");
13762
- /**
13763
- * Per-chunk time budget (ms) for the time-sliced initial mount walk. While
13764
- * mounting a large subtree we keep applying nodes until this much wall-clock
13765
- * time has elapsed, then yield to the browser so it can paint and process
13766
- * input before the next chunk. ~16ms targets a single animation frame.
13767
- */
13768
- AreHTMLLifecycle.MOUNT_BUDGET_MS = 16;
13769
14104
  __decorateClass([
13770
14105
  AreLifecycle.Init(AreComponentNode),
13771
14106
  __decorateParam(0, Yt(te)),
@@ -13924,10 +14259,31 @@ AddStyleInstruction = __decorateClass([
13924
14259
  })
13925
14260
  ], AddStyleInstruction);
13926
14261
 
14262
+ // src/instructions/AddStaticHTML.instruction.ts
14263
+ var AddStaticHTMLInstruction = class extends AreMutation {
14264
+ constructor(parent, props) {
14265
+ if ("aseid" in props) {
14266
+ super(props);
14267
+ } else {
14268
+ super(AreHTMLInstructions.AddStaticHTML, parent, props);
14269
+ }
14270
+ }
14271
+ };
14272
+ __name(AddStaticHTMLInstruction, "AddStaticHTMLInstruction");
14273
+ AddStaticHTMLInstruction = __decorateClass([
14274
+ R4.Define({
14275
+ namespace: "a-are-html",
14276
+ description: 'Materialises a fully static subtree (a "static island") onto its parent element in a single pass via browser-parsed innerHTML / a cached <template> clone. Apply injects the markup; revert clears it. Decodes HTML entities (e.g. &nbsp;) for free.'
14277
+ })
14278
+ ], AddStaticHTMLInstruction);
14279
+
13927
14280
  // src/engine/AreHTML.compiler.ts
13928
14281
  var AreHTMLCompiler = class extends AreCompiler {
13929
14282
  compileHTMLNode(node, scene, logger, ...args) {
13930
14283
  super.compile(node, scene, logger, ...args);
14284
+ if (node.isStaticIsland && scene.host) {
14285
+ scene.plan(new AddStaticHTMLInstruction(scene.host, { html: node.staticInnerHTML }));
14286
+ }
13931
14287
  if (node.styles?.styles) {
13932
14288
  const host = scene.host;
13933
14289
  if (host) {
@@ -14496,6 +14852,44 @@ AreDirectiveIf = __decorateClass([
14496
14852
  AreDirective.Priority(2)
14497
14853
  ], AreDirectiveIf);
14498
14854
 
14855
+ // src/helpers/AreScheduler.helper.ts
14856
+ var _AreSchedulerHelper = class _AreSchedulerHelper {
14857
+ /**
14858
+ * High-resolution wall-clock time in milliseconds. Uses `performance.now()`
14859
+ * when available (monotonic, sub-millisecond), falling back to `Date.now()`.
14860
+ */
14861
+ static now() {
14862
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
14863
+ }
14864
+ /**
14865
+ * Schedule `fn` to run on the next macrotask.
14866
+ *
14867
+ * `MessageChannel` yields a true macrotask without the ~4ms clamp that nested
14868
+ * `setTimeout(0)` calls incur, so the browser can paint between chunks with
14869
+ * minimal scheduling overhead. Falls back to `setTimeout` in non-DOM
14870
+ * environments (e.g. tests / SSR).
14871
+ */
14872
+ static scheduleMacrotask(fn) {
14873
+ if (typeof MessageChannel === "undefined") {
14874
+ setTimeout(fn, 0);
14875
+ return;
14876
+ }
14877
+ if (!this._channel) {
14878
+ this._channel = new MessageChannel();
14879
+ this._channel.port1.onmessage = () => {
14880
+ const next = this._queue.shift();
14881
+ if (next) next();
14882
+ };
14883
+ }
14884
+ this._queue.push(fn);
14885
+ this._channel.port2.postMessage(null);
14886
+ }
14887
+ };
14888
+ __name(_AreSchedulerHelper, "AreSchedulerHelper");
14889
+ /** FIFO queue of callbacks waiting for their posted macrotask to fire. */
14890
+ _AreSchedulerHelper._queue = [];
14891
+ var AreSchedulerHelper = _AreSchedulerHelper;
14892
+
14499
14893
  // src/directives/AreDirectiveFor.directive.ts
14500
14894
  var AreDirectiveFor = class extends AreDirective {
14501
14895
  transform(attribute, scope, store, scene, logger, ...args) {
@@ -14510,7 +14904,8 @@ var AreDirectiveFor = class extends AreDirective {
14510
14904
  node.init();
14511
14905
  attribute.template = forTemplate;
14512
14906
  const { key, index, arrayExpr } = this.parseExpression(attribute.content);
14513
- const array = this.resolveArray(store, arrayExpr, attribute.content);
14907
+ const contextScope = attribute.owner.scope.resolve(AreDirectiveContext)?.scope || {};
14908
+ const array = this.resolveArray(store, arrayExpr, attribute.content, contextScope);
14514
14909
  attribute.value = array;
14515
14910
  for (let i6 = 0; i6 < array.length; i6++) {
14516
14911
  this.spawnItemNode(attribute.template, attribute.owner, key, index, array[i6], i6);
@@ -14544,8 +14939,9 @@ var AreDirectiveFor = class extends AreDirective {
14544
14939
  */
14545
14940
  performUpdate(attribute, store, scene, state) {
14546
14941
  const { key, index, arrayExpr, trackExpr } = this.parseExpression(attribute.content);
14547
- const newArray = this.resolveArray(store, arrayExpr, attribute.content);
14548
14942
  const owner = attribute.owner;
14943
+ const contextScope = owner.scope.resolve(AreDirectiveContext)?.scope || {};
14944
+ const newArray = this.resolveArray(store, arrayExpr, attribute.content, contextScope);
14549
14945
  const currentChildren = [...owner.children];
14550
14946
  attribute.value = newArray;
14551
14947
  const attached = this.isAttached(owner);
@@ -14560,12 +14956,16 @@ var AreDirectiveFor = class extends AreDirective {
14560
14956
  remaining.add(child);
14561
14957
  }
14562
14958
  const toCreate = [];
14959
+ const finalByKey = /* @__PURE__ */ new Map();
14960
+ const orderedKeys = new Array(newArray.length);
14563
14961
  for (let i6 = 0; i6 < newArray.length; i6++) {
14564
14962
  const item = newArray[i6];
14565
14963
  const k4 = computeKey(item, i6);
14964
+ orderedKeys[i6] = k4;
14566
14965
  const existing = childByKey.get(k4);
14567
14966
  if (existing) {
14568
14967
  remaining.delete(existing);
14968
+ finalByKey.set(k4, existing);
14569
14969
  let directiveContext = existing.scope.resolveFlat(AreDirectiveContext);
14570
14970
  if (!directiveContext) {
14571
14971
  directiveContext = new AreDirectiveContext(existing.aseid);
@@ -14577,7 +14977,7 @@ var AreDirectiveFor = class extends AreDirective {
14577
14977
  [index || "index"]: i6
14578
14978
  };
14579
14979
  } else {
14580
- toCreate.push({ item, idx: i6 });
14980
+ toCreate.push({ item, idx: i6, key: k4 });
14581
14981
  }
14582
14982
  }
14583
14983
  for (const child of remaining) {
@@ -14586,12 +14986,14 @@ var AreDirectiveFor = class extends AreDirective {
14586
14986
  }
14587
14987
  const createItem = /* @__PURE__ */ __name((desc) => {
14588
14988
  const child = this.spawnItemNode(attribute.template, owner, key, index, desc.item, desc.idx);
14989
+ finalByKey.set(desc.key, child);
14589
14990
  child.transform();
14590
14991
  child.compile();
14591
14992
  if (attached) child.mount();
14592
14993
  }, "createItem");
14593
14994
  if (toCreate.length <= AreDirectiveFor.SYNC_THRESHOLD) {
14594
14995
  for (const desc of toCreate) createItem(desc);
14996
+ if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);
14595
14997
  return this.finishUpdate(attribute, store, scene, state);
14596
14998
  }
14597
14999
  state.running = true;
@@ -14614,10 +15016,39 @@ var AreDirectiveFor = class extends AreDirective {
14614
15016
  AreSchedulerHelper.scheduleMacrotask(() => resolve(processChunk()));
14615
15017
  });
14616
15018
  }
15019
+ if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);
14617
15020
  return this.finishUpdate(attribute, store, scene, state);
14618
15021
  }, "processChunk");
14619
15022
  return processChunk();
14620
15023
  }
15024
+ /**
15025
+ * Repositions the item nodes' DOM elements so the rendered order matches the
15026
+ * source array order. The keyed diff (steps 1–4) reuses existing nodes in
15027
+ * place and mounts new ones at the end; without this pass a `prepend` or
15028
+ * `shuffle` would leave reused rows where they were and pile new rows at the
15029
+ * bottom. We walk the desired order RIGHT-TO-LEFT, keeping a `ref` pointer to
15030
+ * the element each item must precede (starting at the `$for` anchor comment),
15031
+ * and only call `insertBefore` when an element is not already in position —
15032
+ * so a plain `append` (already-correct order) performs ZERO DOM moves.
15033
+ */
15034
+ reconcileOrder(owner, orderedKeys, finalByKey) {
15035
+ const context = owner.scope.resolve(AreHTMLEngineContext);
15036
+ if (!context) return;
15037
+ const anchor = context.getNodeElement(owner);
15038
+ if (!anchor || !anchor.parentNode) return;
15039
+ const parent = anchor.parentNode;
15040
+ let ref = anchor;
15041
+ for (let i6 = orderedKeys.length - 1; i6 >= 0; i6--) {
15042
+ const node = finalByKey.get(orderedKeys[i6]);
15043
+ if (!node) continue;
15044
+ const element = context.getNodeElement(node);
15045
+ if (!element || element.parentNode !== parent) continue;
15046
+ if (element.nextSibling !== ref) {
15047
+ parent.insertBefore(element, ref);
15048
+ }
15049
+ ref = element;
15050
+ }
15051
+ }
14621
15052
  /**
14622
15053
  * Completes an update pass. If another update() arrived while a chunked
14623
15054
  * render was streaming, run exactly one more pass now from the latest store
@@ -14708,13 +15139,23 @@ var AreDirectiveFor = class extends AreDirective {
14708
15139
  * Supports both plain key lookups and function-call expressions:
14709
15140
  * items → store.get('items')
14710
15141
  * filter(items) → store.get('filter')(store.get('items'))
14711
- */
14712
- resolveArray(store, arrayExpr, fullContent) {
15142
+ *
15143
+ * `contextScope` carries item-scoped variables introduced by an enclosing
15144
+ * directive (e.g. the `row` of an outer `$for`). It is consulted BEFORE the
15145
+ * store so a nested `$for="cell in row.cells"` resolves `row` from the
15146
+ * parent iteration instead of looking for a (non-existent) top-level store
15147
+ * key. Leading identifiers not present in the context fall back to the store.
15148
+ */
15149
+ resolveArray(store, arrayExpr, fullContent, contextScope = {}) {
15150
+ const getRoot = /* @__PURE__ */ __name((rawKey) => {
15151
+ const k4 = rawKey.replace(/\?$/, "");
15152
+ return k4 in contextScope ? contextScope[k4] : store.get(k4);
15153
+ }, "getRoot");
14713
15154
  let result;
14714
15155
  const callMatch = arrayExpr.match(/^([^(]+)\((.+)\)$/);
14715
15156
  if (callMatch) {
14716
15157
  const fnName = callMatch[1].trim();
14717
- const fn = store.get(fnName);
15158
+ const fn = getRoot(fnName);
14718
15159
  if (typeof fn !== "function")
14719
15160
  throw new AreCompilerError({
14720
15161
  title: 'Invalid "for" Directive Function',
@@ -14728,25 +15169,25 @@ var AreDirectiveFor = class extends AreDirective {
14728
15169
  const stripped = arg.replace(/\?$/, "");
14729
15170
  if (stripped.includes(".")) {
14730
15171
  const parts = stripped.split(".").map((p5) => p5.replace(/\?$/, ""));
14731
- let val = store.get(parts[0]);
15172
+ let val = getRoot(parts[0]);
14732
15173
  for (let j5 = 1; j5 < parts.length; j5++) {
14733
15174
  if (val == null) return void 0;
14734
15175
  val = val[parts[j5]];
14735
15176
  }
14736
15177
  return val ?? void 0;
14737
15178
  }
14738
- return store.get(stripped);
15179
+ return getRoot(stripped);
14739
15180
  });
14740
15181
  result = fn(...resolvedArgs);
14741
15182
  } else if (arrayExpr.includes(".")) {
14742
15183
  const parts = arrayExpr.split(".").map((p5) => p5.replace(/\?$/, ""));
14743
- result = store.get(parts[0]);
15184
+ result = getRoot(parts[0]);
14744
15185
  for (let i6 = 1; i6 < parts.length; i6++) {
14745
15186
  if (result == null) break;
14746
15187
  result = result[parts[i6]];
14747
15188
  }
14748
15189
  } else {
14749
- result = store.get(arrayExpr.replace(/\?$/, ""));
15190
+ result = getRoot(arrayExpr);
14750
15191
  }
14751
15192
  if (result == null) return [];
14752
15193
  if (!Array.isArray(result))