@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
@@ -65,11 +65,56 @@ declare class AreStaticAttribute extends AreHTMLAttribute {
65
65
  }
66
66
 
67
67
  declare class AreHTMLNode extends AreNode {
68
+ /**
69
+ * When set, this node is a *static island* root: its entire inner subtree
70
+ * was detected (at tokenize time) to contain no ARE-reactive constructs —
71
+ * no interpolations, no dynamic attributes and only standard HTML tags.
72
+ *
73
+ * Instead of being exploded into one child AreNode per element/text node,
74
+ * the inner markup is preserved verbatim here and materialised in a single
75
+ * pass by the interpreter (browser-parsed `innerHTML` / cached `<template>`
76
+ * clone). The node's OWN attributes (including any dynamic `:`/`@`/`$` on
77
+ * the island root) still compile and stay reactive as usual.
78
+ */
79
+ protected _staticInnerHTML?: string;
68
80
  /**
69
81
  * Actual node type.
70
82
  * By default it's a tag name
71
83
  */
72
84
  get tag(): string;
85
+ /**
86
+ * The verbatim inner markup captured when this node was identified as a
87
+ * static island, or `undefined` for ordinary (per-node) nodes.
88
+ */
89
+ get staticInnerHTML(): string | undefined;
90
+ /**
91
+ * Whether this node is a static-island root (see `_staticInnerHTML`).
92
+ */
93
+ get isStaticIsland(): boolean;
94
+ /**
95
+ * Marks this node as a static-island root, capturing the verbatim inner
96
+ * markup to be materialised in one shot by the interpreter. Called by the
97
+ * tokenizer when the node's inner content is detected to be fully static.
98
+ */
99
+ markStatic(innerHTML: string): void;
100
+ /**
101
+ * Deep-clone the node. Overridden to carry over the static-island marker
102
+ * (`_staticInnerHTML`), which lives on AreHTMLNode and is therefore NOT
103
+ * copied by the base AreNode.clone(). Without this, cloning a directive
104
+ * template ($if/$for) that wraps a static island (e.g. `<span $if>★</span>`)
105
+ * would drop the captured inner markup and render an empty element. The
106
+ * base clone() recurses via each child's polymorphic clone(), so nested
107
+ * island children are preserved automatically through this override.
108
+ */
109
+ clone<T extends AreNode = AreNode>(this: T): T;
110
+ /**
111
+ * Clone the node while transferring its existing scope to the clone (used by
112
+ * the $if/$for directives to turn the original node into a lightweight group
113
+ * container). Overridden for the same reason as `clone()`: the static-island
114
+ * marker must survive so a directive applied to an island root keeps its
115
+ * inner markup.
116
+ */
117
+ cloneWithScope<T extends AreNode = AreNode>(this: T): T;
73
118
  /**
74
119
  * 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.
75
120
  *
@@ -65,11 +65,56 @@ declare class AreStaticAttribute extends AreHTMLAttribute {
65
65
  }
66
66
 
67
67
  declare class AreHTMLNode extends AreNode {
68
+ /**
69
+ * When set, this node is a *static island* root: its entire inner subtree
70
+ * was detected (at tokenize time) to contain no ARE-reactive constructs —
71
+ * no interpolations, no dynamic attributes and only standard HTML tags.
72
+ *
73
+ * Instead of being exploded into one child AreNode per element/text node,
74
+ * the inner markup is preserved verbatim here and materialised in a single
75
+ * pass by the interpreter (browser-parsed `innerHTML` / cached `<template>`
76
+ * clone). The node's OWN attributes (including any dynamic `:`/`@`/`$` on
77
+ * the island root) still compile and stay reactive as usual.
78
+ */
79
+ protected _staticInnerHTML?: string;
68
80
  /**
69
81
  * Actual node type.
70
82
  * By default it's a tag name
71
83
  */
72
84
  get tag(): string;
85
+ /**
86
+ * The verbatim inner markup captured when this node was identified as a
87
+ * static island, or `undefined` for ordinary (per-node) nodes.
88
+ */
89
+ get staticInnerHTML(): string | undefined;
90
+ /**
91
+ * Whether this node is a static-island root (see `_staticInnerHTML`).
92
+ */
93
+ get isStaticIsland(): boolean;
94
+ /**
95
+ * Marks this node as a static-island root, capturing the verbatim inner
96
+ * markup to be materialised in one shot by the interpreter. Called by the
97
+ * tokenizer when the node's inner content is detected to be fully static.
98
+ */
99
+ markStatic(innerHTML: string): void;
100
+ /**
101
+ * Deep-clone the node. Overridden to carry over the static-island marker
102
+ * (`_staticInnerHTML`), which lives on AreHTMLNode and is therefore NOT
103
+ * copied by the base AreNode.clone(). Without this, cloning a directive
104
+ * template ($if/$for) that wraps a static island (e.g. `<span $if>★</span>`)
105
+ * would drop the captured inner markup and render an empty element. The
106
+ * base clone() recurses via each child's polymorphic clone(), so nested
107
+ * island children are preserved automatically through this override.
108
+ */
109
+ clone<T extends AreNode = AreNode>(this: T): T;
110
+ /**
111
+ * Clone the node while transferring its existing scope to the clone (used by
112
+ * the $if/$for directives to turn the original node into a lightweight group
113
+ * container). Overridden for the same reason as `clone()`: the static-island
114
+ * marker must survive so a directive applied to an island root keeps its
115
+ * inner markup.
116
+ */
117
+ cloneWithScope<T extends AreNode = AreNode>(this: T): T;
73
118
  /**
74
119
  * 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.
75
120
  *
@@ -1,4 +1,4 @@
1
- export { A as AreBindingAttribute } from '../AreBinding.attribute-doUvtOjc.mjs';
1
+ export { A as AreBindingAttribute } from '../AreBinding.attribute-BWzEIw6H.mjs';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.mjs';
@@ -1,4 +1,4 @@
1
- export { A as AreBindingAttribute } from '../AreBinding.attribute-Bm5LlOyE.js';
1
+ export { A as AreBindingAttribute } from '../AreBinding.attribute-GpT-5Qmf.js';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.js';
@@ -1,4 +1,4 @@
1
- export { b as AreDirectiveAttribute } from '../AreBinding.attribute-doUvtOjc.mjs';
1
+ export { b as AreDirectiveAttribute } from '../AreBinding.attribute-BWzEIw6H.mjs';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.mjs';
@@ -1,4 +1,4 @@
1
- export { b as AreDirectiveAttribute } from '../AreBinding.attribute-Bm5LlOyE.js';
1
+ export { b as AreDirectiveAttribute } from '../AreBinding.attribute-GpT-5Qmf.js';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.js';
@@ -1,4 +1,4 @@
1
- export { c as AreEventAttribute } from '../AreBinding.attribute-doUvtOjc.mjs';
1
+ export { c as AreEventAttribute } from '../AreBinding.attribute-BWzEIw6H.mjs';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.mjs';
@@ -1,4 +1,4 @@
1
- export { c as AreEventAttribute } from '../AreBinding.attribute-Bm5LlOyE.js';
1
+ export { c as AreEventAttribute } from '../AreBinding.attribute-GpT-5Qmf.js';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.js';
@@ -1,4 +1,4 @@
1
- export { f as AreStaticAttribute } from '../AreBinding.attribute-doUvtOjc.mjs';
1
+ export { f as AreStaticAttribute } from '../AreBinding.attribute-BWzEIw6H.mjs';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.mjs';
@@ -1,4 +1,4 @@
1
- export { f as AreStaticAttribute } from '../AreBinding.attribute-Bm5LlOyE.js';
1
+ export { f as AreStaticAttribute } from '../AreBinding.attribute-GpT-5Qmf.js';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.js';
@@ -1,6 +1,6 @@
1
1
  import { A_Scope } from '@adaas/a-concept';
2
2
  import { A_Logger } from '@adaas/a-utils/a-logger';
3
- import { a as AreDirective, b as AreDirectiveAttribute } from '../AreBinding.attribute-doUvtOjc.mjs';
3
+ import { a as AreDirective, b as AreDirectiveAttribute } from '../AreBinding.attribute-BWzEIw6H.mjs';
4
4
  import { AreStore, AreScene } from '@adaas/are';
5
5
  import '../lib/AreStyle/AreStyle.context.mjs';
6
6
 
@@ -38,6 +38,17 @@ declare class AreDirectiveFor extends AreDirective {
38
38
  * pass for the same `$for` is in flight (see `update`).
39
39
  */
40
40
  private performUpdate;
41
+ /**
42
+ * Repositions the item nodes' DOM elements so the rendered order matches the
43
+ * source array order. The keyed diff (steps 1–4) reuses existing nodes in
44
+ * place and mounts new ones at the end; without this pass a `prepend` or
45
+ * `shuffle` would leave reused rows where they were and pile new rows at the
46
+ * bottom. We walk the desired order RIGHT-TO-LEFT, keeping a `ref` pointer to
47
+ * the element each item must precede (starting at the `$for` anchor comment),
48
+ * and only call `insertBefore` when an element is not already in position —
49
+ * so a plain `append` (already-correct order) performs ZERO DOM moves.
50
+ */
51
+ private reconcileOrder;
41
52
  /**
42
53
  * Completes an update pass. If another update() arrived while a chunked
43
54
  * render was streaming, run exactly one more pass now from the latest store
@@ -75,6 +86,12 @@ declare class AreDirectiveFor extends AreDirective {
75
86
  * Supports both plain key lookups and function-call expressions:
76
87
  * items → store.get('items')
77
88
  * filter(items) → store.get('filter')(store.get('items'))
89
+ *
90
+ * `contextScope` carries item-scoped variables introduced by an enclosing
91
+ * directive (e.g. the `row` of an outer `$for`). It is consulted BEFORE the
92
+ * store so a nested `$for="cell in row.cells"` resolves `row` from the
93
+ * parent iteration instead of looking for a (non-existent) top-level store
94
+ * key. Leading identifiers not present in the context fall back to the store.
78
95
  */
79
96
  private resolveArray;
80
97
  /**
@@ -1,6 +1,6 @@
1
1
  import { A_Scope } from '@adaas/a-concept';
2
2
  import { A_Logger } from '@adaas/a-utils/a-logger';
3
- import { a as AreDirective, b as AreDirectiveAttribute } from '../AreBinding.attribute-Bm5LlOyE.js';
3
+ import { a as AreDirective, b as AreDirectiveAttribute } from '../AreBinding.attribute-GpT-5Qmf.js';
4
4
  import { AreStore, AreScene } from '@adaas/are';
5
5
  import '../lib/AreStyle/AreStyle.context.js';
6
6
 
@@ -38,6 +38,17 @@ declare class AreDirectiveFor extends AreDirective {
38
38
  * pass for the same `$for` is in flight (see `update`).
39
39
  */
40
40
  private performUpdate;
41
+ /**
42
+ * Repositions the item nodes' DOM elements so the rendered order matches the
43
+ * source array order. The keyed diff (steps 1–4) reuses existing nodes in
44
+ * place and mounts new ones at the end; without this pass a `prepend` or
45
+ * `shuffle` would leave reused rows where they were and pile new rows at the
46
+ * bottom. We walk the desired order RIGHT-TO-LEFT, keeping a `ref` pointer to
47
+ * the element each item must precede (starting at the `$for` anchor comment),
48
+ * and only call `insertBefore` when an element is not already in position —
49
+ * so a plain `append` (already-correct order) performs ZERO DOM moves.
50
+ */
51
+ private reconcileOrder;
41
52
  /**
42
53
  * Completes an update pass. If another update() arrived while a chunked
43
54
  * render was streaming, run exactly one more pass now from the latest store
@@ -75,6 +86,12 @@ declare class AreDirectiveFor extends AreDirective {
75
86
  * Supports both plain key lookups and function-call expressions:
76
87
  * items → store.get('items')
77
88
  * filter(items) → store.get('filter')(store.get('items'))
89
+ *
90
+ * `contextScope` carries item-scoped variables introduced by an enclosing
91
+ * directive (e.g. the `row` of an outer `$for`). It is consulted BEFORE the
92
+ * store so a nested `$for="cell in row.cells"` resolves `row` from the
93
+ * parent iteration instead of looking for a (non-existent) top-level store
94
+ * key. Leading identifiers not present in the context fall back to the store.
78
95
  */
79
96
  private resolveArray;
80
97
  /**
@@ -8,6 +8,7 @@ var AddComment_instruction = require('@adaas/are-html/instructions/AddComment.in
8
8
  var AreDirective_context = require('@adaas/are-html/directive/AreDirective.context');
9
9
  var core = require('@adaas/a-frame/core');
10
10
  var AreScheduler_helper = require('@adaas/are-html/helpers/AreScheduler.helper');
11
+ var context = require('@adaas/are-html/context');
11
12
 
12
13
  var __defProp = Object.defineProperty;
13
14
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -33,7 +34,8 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
33
34
  node.init();
34
35
  attribute.template = forTemplate;
35
36
  const { key, index, arrayExpr } = this.parseExpression(attribute.content);
36
- const array = this.resolveArray(store, arrayExpr, attribute.content);
37
+ const contextScope = attribute.owner.scope.resolve(AreDirective_context.AreDirectiveContext)?.scope || {};
38
+ const array = this.resolveArray(store, arrayExpr, attribute.content, contextScope);
37
39
  attribute.value = array;
38
40
  for (let i = 0; i < array.length; i++) {
39
41
  this.spawnItemNode(attribute.template, attribute.owner, key, index, array[i], i);
@@ -67,8 +69,9 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
67
69
  */
68
70
  performUpdate(attribute, store, scene, state) {
69
71
  const { key, index, arrayExpr, trackExpr } = this.parseExpression(attribute.content);
70
- const newArray = this.resolveArray(store, arrayExpr, attribute.content);
71
72
  const owner = attribute.owner;
73
+ const contextScope = owner.scope.resolve(AreDirective_context.AreDirectiveContext)?.scope || {};
74
+ const newArray = this.resolveArray(store, arrayExpr, attribute.content, contextScope);
72
75
  const currentChildren = [...owner.children];
73
76
  attribute.value = newArray;
74
77
  const attached = this.isAttached(owner);
@@ -83,12 +86,16 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
83
86
  remaining.add(child);
84
87
  }
85
88
  const toCreate = [];
89
+ const finalByKey = /* @__PURE__ */ new Map();
90
+ const orderedKeys = new Array(newArray.length);
86
91
  for (let i = 0; i < newArray.length; i++) {
87
92
  const item = newArray[i];
88
93
  const k = computeKey(item, i);
94
+ orderedKeys[i] = k;
89
95
  const existing = childByKey.get(k);
90
96
  if (existing) {
91
97
  remaining.delete(existing);
98
+ finalByKey.set(k, existing);
92
99
  let directiveContext = existing.scope.resolveFlat(AreDirective_context.AreDirectiveContext);
93
100
  if (!directiveContext) {
94
101
  directiveContext = new AreDirective_context.AreDirectiveContext(existing.aseid);
@@ -100,7 +107,7 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
100
107
  [index || "index"]: i
101
108
  };
102
109
  } else {
103
- toCreate.push({ item, idx: i });
110
+ toCreate.push({ item, idx: i, key: k });
104
111
  }
105
112
  }
106
113
  for (const child of remaining) {
@@ -109,12 +116,14 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
109
116
  }
110
117
  const createItem = (desc) => {
111
118
  const child = this.spawnItemNode(attribute.template, owner, key, index, desc.item, desc.idx);
119
+ finalByKey.set(desc.key, child);
112
120
  child.transform();
113
121
  child.compile();
114
122
  if (attached) child.mount();
115
123
  };
116
124
  if (toCreate.length <= exports.AreDirectiveFor.SYNC_THRESHOLD) {
117
125
  for (const desc of toCreate) createItem(desc);
126
+ if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);
118
127
  return this.finishUpdate(attribute, store, scene, state);
119
128
  }
120
129
  state.running = true;
@@ -137,10 +146,39 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
137
146
  AreScheduler_helper.AreSchedulerHelper.scheduleMacrotask(() => resolve(processChunk()));
138
147
  });
139
148
  }
149
+ if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);
140
150
  return this.finishUpdate(attribute, store, scene, state);
141
151
  };
142
152
  return processChunk();
143
153
  }
154
+ /**
155
+ * Repositions the item nodes' DOM elements so the rendered order matches the
156
+ * source array order. The keyed diff (steps 1–4) reuses existing nodes in
157
+ * place and mounts new ones at the end; without this pass a `prepend` or
158
+ * `shuffle` would leave reused rows where they were and pile new rows at the
159
+ * bottom. We walk the desired order RIGHT-TO-LEFT, keeping a `ref` pointer to
160
+ * the element each item must precede (starting at the `$for` anchor comment),
161
+ * and only call `insertBefore` when an element is not already in position —
162
+ * so a plain `append` (already-correct order) performs ZERO DOM moves.
163
+ */
164
+ reconcileOrder(owner, orderedKeys, finalByKey) {
165
+ const context$1 = owner.scope.resolve(context.AreHTMLEngineContext);
166
+ if (!context$1) return;
167
+ const anchor = context$1.getNodeElement(owner);
168
+ if (!anchor || !anchor.parentNode) return;
169
+ const parent = anchor.parentNode;
170
+ let ref = anchor;
171
+ for (let i = orderedKeys.length - 1; i >= 0; i--) {
172
+ const node = finalByKey.get(orderedKeys[i]);
173
+ if (!node) continue;
174
+ const element = context$1.getNodeElement(node);
175
+ if (!element || element.parentNode !== parent) continue;
176
+ if (element.nextSibling !== ref) {
177
+ parent.insertBefore(element, ref);
178
+ }
179
+ ref = element;
180
+ }
181
+ }
144
182
  /**
145
183
  * Completes an update pass. If another update() arrived while a chunked
146
184
  * render was streaming, run exactly one more pass now from the latest store
@@ -231,13 +269,23 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
231
269
  * Supports both plain key lookups and function-call expressions:
232
270
  * items → store.get('items')
233
271
  * filter(items) → store.get('filter')(store.get('items'))
272
+ *
273
+ * `contextScope` carries item-scoped variables introduced by an enclosing
274
+ * directive (e.g. the `row` of an outer `$for`). It is consulted BEFORE the
275
+ * store so a nested `$for="cell in row.cells"` resolves `row` from the
276
+ * parent iteration instead of looking for a (non-existent) top-level store
277
+ * key. Leading identifiers not present in the context fall back to the store.
234
278
  */
235
- resolveArray(store, arrayExpr, fullContent) {
279
+ resolveArray(store, arrayExpr, fullContent, contextScope = {}) {
280
+ const getRoot = (rawKey) => {
281
+ const k = rawKey.replace(/\?$/, "");
282
+ return k in contextScope ? contextScope[k] : store.get(k);
283
+ };
236
284
  let result;
237
285
  const callMatch = arrayExpr.match(/^([^(]+)\((.+)\)$/);
238
286
  if (callMatch) {
239
287
  const fnName = callMatch[1].trim();
240
- const fn = store.get(fnName);
288
+ const fn = getRoot(fnName);
241
289
  if (typeof fn !== "function")
242
290
  throw new are.AreCompilerError({
243
291
  title: 'Invalid "for" Directive Function',
@@ -251,25 +299,25 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
251
299
  const stripped = arg.replace(/\?$/, "");
252
300
  if (stripped.includes(".")) {
253
301
  const parts = stripped.split(".").map((p) => p.replace(/\?$/, ""));
254
- let val = store.get(parts[0]);
302
+ let val = getRoot(parts[0]);
255
303
  for (let j = 1; j < parts.length; j++) {
256
304
  if (val == null) return void 0;
257
305
  val = val[parts[j]];
258
306
  }
259
307
  return val ?? void 0;
260
308
  }
261
- return store.get(stripped);
309
+ return getRoot(stripped);
262
310
  });
263
311
  result = fn(...resolvedArgs);
264
312
  } else if (arrayExpr.includes(".")) {
265
313
  const parts = arrayExpr.split(".").map((p) => p.replace(/\?$/, ""));
266
- result = store.get(parts[0]);
314
+ result = getRoot(parts[0]);
267
315
  for (let i = 1; i < parts.length; i++) {
268
316
  if (result == null) break;
269
317
  result = result[parts[i]];
270
318
  }
271
319
  } else {
272
- result = store.get(arrayExpr.replace(/\?$/, ""));
320
+ result = getRoot(arrayExpr);
273
321
  }
274
322
  if (result == null) return [];
275
323
  if (!Array.isArray(result))
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/directives/AreDirectiveFor.directive.ts"],"names":["AreDirectiveFor","AreDirective","AddCommentInstruction","AreDirectiveContext","AreSchedulerHelper","AreCompilerError","A_Caller","A_Scope","AreStore","AreScene","A_Logger","A_Frame"],"mappings":"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","file":"AreDirectiveFor.directive.js","sourcesContent":["import { A_Caller, A_Inject, A_Scope } from \"@adaas/a-concept\";\nimport { A_Logger } from \"@adaas/a-utils/a-logger\";\nimport { AreDirectiveAttribute } from \"@adaas/are-html/attributes/AreDirective.attribute\";\nimport { AreCompilerError, AreScene, AreStore } from \"@adaas/are\";\nimport { AreDirective } from \"@adaas/are-html/directive/AreDirective.component\";\nimport { AddCommentInstruction } from \"@adaas/are-html/instructions/AddComment.instruction\";\nimport { AreHTMLNode } from \"@adaas/are-html/node\";\nimport { AreDirectiveContext } from \"@adaas/are-html/directive/AreDirective.context\";\nimport { A_Frame } from \"@adaas/a-frame/core\";\nimport { AreSchedulerHelper } from \"@adaas/are-html/helpers/AreScheduler.helper\";\n\n\ntype AreForExpression = {\n key: string;\n index: string | undefined;\n arrayExpr: string;\n /** Optional `track <expr>` clause, e.g. `track row.id` */\n trackExpr: string | undefined;\n};\n\n/**\n * Per-`$for` reentrancy state used to serialize chunked (async) renders.\n * Keyed by the directive attribute instance (one per `$for` in the template).\n */\ntype AreForRenderState = { running: boolean; pending: boolean };\n\n\n@A_Frame.Define({\n namespace: 'a-are-html',\n description: 'Built-in $for directive. Iterates over an array expression resolved from the store and renders a cloned template fragment per item, managing per-item subscopes and comment-node anchors. Supports keyed diffing via an optional track clause to minimise DOM mutations on collection updates.'\n})\n@AreDirective.Priority(1)\nexport class AreDirectiveFor extends AreDirective {\n\n /**\n * Lists whose number of NEW item nodes is at or below this threshold render\n * fully synchronously — byte-for-byte the previous behavior. Typical UIs\n * (menus, small tables) are therefore completely unaffected; only genuinely\n * large lists pay the (tiny) scheduling cost to keep the main thread responsive.\n */\n private static readonly SYNC_THRESHOLD = 100;\n\n /**\n * Per-chunk time budget (ms). During a large-list render we mount item nodes\n * until this much time has elapsed, then yield to the browser so it can paint\n * and process input before the next chunk. ~16ms targets one animation frame.\n */\n private static readonly CHUNK_BUDGET_MS = 16;\n\n /**\n * Per-attribute serialization state. A new update() that arrives while a\n * chunked render of the SAME `$for` is still in flight does NOT start a second\n * concurrent pass (which could interleave mutations on the shared children\n * list); instead it marks `pending` and the in-flight run re-runs once more\n * with the latest data when it finishes. This guarantees the children list is\n * only ever mutated by one pass at a time and the final state always reflects\n * the most recent store value.\n */\n private static readonly renderState = new WeakMap<object, AreForRenderState>();\n\n\n @AreDirective.Transform\n transform(\n @A_Inject(A_Caller) attribute: AreDirectiveAttribute,\n @A_Inject(A_Scope) scope: A_Scope,\n @A_Inject(AreStore) store: AreStore,\n @A_Inject(AreScene) scene: AreScene,\n @A_Inject(A_Logger) logger: A_Logger,\n ...args: any[]\n ) {\n\n logger.debug(`[Transform] directive $FOR for <${attribute.owner.aseid.toString()}>`)\n\n const node = attribute.owner;\n\n /**\n * Transfer the original node's scope (with all registered attributes and children)\n * to the template clone, and give the owner node a fresh empty scope.\n * This mirrors the $if directive's approach, making the owner a lightweight\n * group container whose sole visible presence is a comment placeholder.\n */\n const forTemplate = node.cloneWithScope();\n\n /**\n * Remove the $for attribute from the template so iterative clones do not\n * re-trigger this directive during their own transform phase.\n * Re-register it on the owner so the reactive compile/update pipeline keeps working.\n */\n const forAttr = forTemplate.attributes.find(d => d.name === attribute.name);\n\n if (forAttr) {\n forTemplate.scope.deregister(forAttr);\n node.scope.register(forAttr);\n }\n\n /**\n * Re-initialize the owner node with its fresh scope so it becomes a valid\n * group container that will own the generated item nodes as children.\n */\n node.init();\n\n /**\n * Store the template for use in compile and update.\n */\n attribute.template = forTemplate;\n\n\n /**\n * Parse the $for expression and evaluate the source array.\n */\n const { key, index, arrayExpr } = this.parseExpression(attribute.content);\n const array = this.resolveArray(store, arrayExpr, attribute.content);\n\n attribute.value = array;\n\n /**\n * For each item in the array, spawn a clone of the template with the\n * item's store values pre-set and its scene activated.\n *\n * The children are added to the owner node before the main compiler's\n * children iteration loop runs, so the main cycle will compile them —\n * no explicit child.compile() call is needed here.\n */\n for (let i = 0; i < array.length; i++) {\n this.spawnItemNode(attribute.template!, attribute.owner, key, index, array[i], i);\n }\n }\n\n\n @AreDirective.Compile\n compile(\n @A_Inject(A_Caller) attribute: AreDirectiveAttribute,\n @A_Inject(AreStore) store: AreStore,\n @A_Inject(AreScene) scene: AreScene,\n ...args: any[]\n ): void {\n /**\n * Replace the group node's default host declaration with a comment placeholder\n * so the owner element itself does not render as a DOM element — the item nodes\n * render as its children instead.\n */\n const hostInstruction = scene.host!;\n const commentIdentifier = ` --- for: ${attribute.template!.id} --- `;\n const declaration = new AddCommentInstruction({ content: commentIdentifier });\n\n scene.setHost(declaration);\n scene.planBefore(declaration, hostInstruction);\n scene.unPlan(hostInstruction);\n }\n\n\n @AreDirective.Update\n update(\n @A_Inject(A_Caller) attribute: AreDirectiveAttribute,\n @A_Inject(AreStore) store: AreStore,\n @A_Inject(AreScene) scene: AreScene,\n ...args: any[]\n ): void | Promise<void> {\n /**\n * Serialize chunked renders per `$for`. If a previous large-list render\n * is still streaming item nodes across macrotasks, do NOT start a second\n * concurrent pass — that would interleave two diffs over the same shared\n * children list (and leave half-compiled item nodes that the next diff\n * would wrongly \"reuse\"). Mark a pass as pending instead; the in-flight\n * run re-diffs once more from the latest store value when it completes.\n */\n let state = AreDirectiveFor.renderState.get(attribute);\n if (!state) {\n state = { running: false, pending: false };\n AreDirectiveFor.renderState.set(attribute, state);\n }\n if (state.running) {\n state.pending = true;\n return;\n }\n\n return this.performUpdate(attribute, store, scene, state);\n }\n\n /**\n * Core of the `$for` update: re-diff the source array against the current\n * children, reconcile reused/removed items, then mount the new ones (small\n * lists synchronously, large lists time-sliced). Never called while another\n * pass for the same `$for` is in flight (see `update`).\n */\n private performUpdate(\n attribute: AreDirectiveAttribute,\n store: AreStore,\n scene: AreScene,\n state: AreForRenderState,\n ): void | Promise<void> {\n /**\n * Re-evaluate the source array.\n */\n const { key, index, arrayExpr, trackExpr } = this.parseExpression(attribute.content);\n const newArray = this.resolveArray(store, arrayExpr, attribute.content);\n\n const owner = attribute.owner;\n const currentChildren = [...owner.children] as AreHTMLNode[];\n\n attribute.value = newArray;\n\n /**\n * Is this `$for`'s subtree currently rendered into the DOM?\n *\n * A `$for` can update while its subtree is detached — e.g. it lives\n * inside a `$if` whose condition is currently false (the documented\n * `<div $if><x $for></div>` nesting). The directive still receives the\n * store change and re-diffs, but it must NOT mount/unmount item nodes\n * directly while detached: the `$for` anchor (and its ancestors) are\n * not in the DOM, so the interpreter's mount-point walk would fall\n * through to the nearest *mounted* ancestor (the `$if` comment in the\n * grandparent) and HOIST the items out of their intended container.\n * When the ancestor `$if` later activates, its mount cascade applies\n * the already-compiled item instructions in the correct place.\n *\n * Detached === any ancestor scene is inactive (regular nodes default\n * to an active scene; only structural directives deactivate one).\n */\n const attached = this.isAttached(owner);\n\n const computeKey = this.makeKeyFn(key, index, trackExpr);\n\n // ── 1. Index existing children by stable key ────────────────────────\n const childByKey = new Map<any, AreHTMLNode>();\n const remaining = new Set<AreHTMLNode>();\n\n for (let i = 0; i < currentChildren.length; i++) {\n const child = currentChildren[i];\n const ctx = child.scope.resolveFlat(AreDirectiveContext);\n const k = ctx ? computeKey(ctx.scope[key], ctx.scope[index || 'index']) : Symbol('orphan');\n childByKey.set(k, child);\n remaining.add(child);\n }\n\n // ── 2. Walk desired list; reuse existing or record items to create ──\n // NOTE: new item nodes are NOT spawned here. Spawning (cloneWithScope +\n // subtree init + scene activation) is the dominant cost of a large\n // render, so it is deferred into the time-sliced loop below alongside\n // transform/compile/mount. Existing (keyed) children are reconciled in\n // place synchronously — that is cheap and keeps reused rows stable.\n const toCreate: Array<{ item: any; idx: number }> = [];\n\n for (let i = 0; i < newArray.length; i++) {\n const item = newArray[i];\n const k = computeKey(item, i);\n const existing = childByKey.get(k);\n\n if (existing) {\n remaining.delete(existing);\n\n let directiveContext = existing.scope.resolveFlat(AreDirectiveContext);\n if (!directiveContext) {\n directiveContext = new AreDirectiveContext(existing.aseid);\n existing.scope.register(directiveContext);\n }\n directiveContext.scope = {\n ...directiveContext.scope,\n [key]: item,\n [index || 'index']: i,\n };\n } else {\n toCreate.push({ item, idx: i });\n }\n }\n\n // ── 3. Unmount + detach removed children ─────────────────────────────\n for (const child of remaining) {\n // Only revert DOM if the subtree is live; a detached subtree's item\n // nodes were never mounted (see `attached` rationale above), so\n // unmounting them is a no-op at best and risks reverting stale state.\n if (attached) child.unmount();\n owner.removeChild(child);\n }\n\n // ── 4. Create + mount the new item nodes. ───────────────────────────\n // `spawnItemNode` appends to `owner.children` immediately, so iterating\n // `toCreate` in source order preserves list order (reused children keep\n // their positions, new rows are appended in order) — identical to the\n // previous synchronous behavior.\n const createItem = (desc: { item: any; idx: number }) => {\n const child = this.spawnItemNode(attribute.template!, owner, key, index, desc.item, desc.idx);\n child.transform();\n child.compile();\n // While detached, stop after compile: the item's instructions are\n // planned and the ancestor `$if`'s mount cascade will apply them in\n // the correct container once the condition becomes truthy. Mounting\n // here would hoist the item to the nearest mounted ancestor.\n if (attached) child.mount();\n };\n\n // Small lists → fully synchronous, identical to the previous behavior.\n if (toCreate.length <= AreDirectiveFor.SYNC_THRESHOLD) {\n for (const desc of toCreate) createItem(desc);\n return this.finishUpdate(attribute, store, scene, state);\n }\n\n // Large lists → time-sliced render. Create item nodes until the frame\n // budget elapses, then yield to the browser (zero-delay macrotask) so\n // it can paint and stay responsive instead of blocking for the whole\n // batch. The `state.running` flag (see `update`) prevents any other\n // update() for this `$for` from interleaving while we stream.\n state.running = true;\n let cursor = 0;\n\n const processChunk = (): void | Promise<void> => {\n try {\n const start = AreSchedulerHelper.now();\n while (cursor < toCreate.length) {\n createItem(toCreate[cursor]);\n cursor++;\n if (AreSchedulerHelper.now() - start >= AreDirectiveFor.CHUNK_BUDGET_MS) break;\n }\n } catch (error) {\n // Never leave the `$for` wedged in the running state on failure,\n // or every future update would be silently deferred forever.\n state.running = false;\n state.pending = false;\n throw error;\n }\n\n if (cursor < toCreate.length) {\n return new Promise<void>(resolve => {\n AreSchedulerHelper.scheduleMacrotask(() => resolve(processChunk()));\n });\n }\n\n return this.finishUpdate(attribute, store, scene, state);\n };\n\n return processChunk();\n }\n\n /**\n * Completes an update pass. If another update() arrived while a chunked\n * render was streaming, run exactly one more pass now from the latest store\n * value so the final DOM always reflects the most recent data.\n */\n private finishUpdate(\n attribute: AreDirectiveAttribute,\n store: AreStore,\n scene: AreScene,\n state: AreForRenderState,\n ): void | Promise<void> {\n state.running = false;\n if (state.pending) {\n state.pending = false;\n return this.performUpdate(attribute, store, scene, state);\n }\n }\n\n\n /**\n * Walks the node's ancestor chain (inclusive) and reports whether the\n * whole path is currently active — i.e. the subtree is actually rendered\n * into the DOM. A single inactive ancestor scene (e.g. a `$if` whose\n * condition is false) means the subtree is detached.\n */\n private isAttached(node: AreHTMLNode): boolean {\n let current: AreHTMLNode | undefined = node;\n while (current) {\n if (current.scene?.isInactive) return false;\n current = current.parent as AreHTMLNode | undefined;\n }\n return true;\n }\n\n\n // ─────────────────────────────────────────────────────────────────────────────\n // ── Helpers ──────────────────────────────────────────────────────────────────\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Build a key-function that derives a stable identity from each item.\n * If the user provided a `track <expr>` clause, evaluate it as a path on\n * the item; otherwise fall back to the item identity (reference equality).\n */\n private makeKeyFn(key: string, index: string | undefined, trackExpr: string | undefined): (item: any, i: number) => any {\n if (!trackExpr) {\n return (item, i) => item ?? i;\n }\n\n // Strip any leading `key.` so users can write `track row.id`.\n const path = trackExpr.startsWith(key + '.') ? trackExpr.slice(key.length + 1) : trackExpr;\n\n return (item, i) => {\n if (item == null) return i;\n if (path === key || path === '$index') return path === '$index' ? i : item;\n\n // dotted path lookup\n const parts = path.split('.');\n let v: any = item;\n for (const p of parts) {\n if (v == null) return i;\n v = v[p];\n }\n return v ?? i;\n };\n }\n\n /**\n * Parses the $for expression string into its constituent parts.\n *\n * Supported formats:\n * item in items\n * item, index in items\n * (item, index) in items\n * item in filter(items)\n * item, index in filter(items, 'active')\n * item in items track item.id\n * (item, i) in items track item.id\n */\n private parseExpression(content: string): AreForExpression {\n // Strip optional `track <expr>` suffix first.\n let trackExpr: string | undefined;\n const trackIdx = content.search(/\\s+track\\s+/);\n let body = content;\n if (trackIdx !== -1) {\n const m = content.slice(trackIdx).match(/\\s+track\\s+(.+)$/);\n if (m) {\n trackExpr = m[1].trim();\n body = content.slice(0, trackIdx).trim();\n }\n }\n\n const inIndex = body.lastIndexOf(' in ');\n const keyAndIndex = body.slice(0, inIndex).trim().replace(/^\\(|\\)$/g, '');\n const arrayExpr = body.slice(inIndex + 4).trim();\n const keyParts = keyAndIndex.split(',').map(p => p.trim());\n\n return {\n key: keyParts[0],\n index: keyParts[1] || undefined,\n arrayExpr,\n trackExpr,\n };\n }\n\n /**\n * Resolves the array expression against the store.\n * Supports both plain key lookups and function-call expressions:\n * items → store.get('items')\n * filter(items) → store.get('filter')(store.get('items'))\n */\n private resolveArray(store: AreStore, arrayExpr: string, fullContent: string): any[] {\n let result: any;\n const callMatch = arrayExpr.match(/^([^(]+)\\((.+)\\)$/);\n\n if (callMatch) {\n const fnName = callMatch[1].trim();\n const fn = store.get(fnName as any);\n\n if (typeof fn !== 'function')\n throw new AreCompilerError({\n title: 'Invalid \"for\" Directive Function',\n description: `The expression \"${fnName}\" in the \"for\" directive does not resolve to a function in the store. Received: ${typeof fn}`,\n });\n\n const rawArgs = callMatch[2].split(',').map(a => a.trim());\n const resolvedArgs = rawArgs.map(arg => {\n if (arg.startsWith(\"'\") && arg.endsWith(\"'\")) return arg.slice(1, -1);\n if (arg.startsWith('\"') && arg.endsWith('\"')) return arg.slice(1, -1);\n if (!isNaN(Number(arg))) return Number(arg);\n // Dotted-path / optional-chain: e.g. `record?.embedding` or `record.data`\n const stripped = arg.replace(/\\?$/, '');\n if (stripped.includes('.')) {\n const parts = stripped.split('.').map(p => p.replace(/\\?$/, ''));\n let val: any = store.get(parts[0] as any);\n for (let j = 1; j < parts.length; j++) {\n if (val == null) return undefined;\n val = val[parts[j]];\n }\n return val ?? undefined;\n }\n return store.get(stripped as any);\n });\n\n result = (fn as Function)(...resolvedArgs);\n } else if (arrayExpr.includes('.')) {\n // dotted-path lookup: e.g. \"list.items\" or \"record?.keywords\"\n // Strip optional-chaining `?` suffix from each segment so that\n // `record?.keywords` resolves the same as `record.keywords`.\n const parts = arrayExpr.split('.').map(p => p.replace(/\\?$/, ''));\n result = store.get(parts[0] as any);\n for (let i = 1; i < parts.length; i++) {\n if (result == null) break;\n result = result[parts[i]];\n }\n } else {\n result = store.get(arrayExpr.replace(/\\?$/, '') as any);\n }\n\n // null / undefined from optional-chaining expressions (e.g. `record?.keywords`)\n // means the source object is not yet loaded — treat as empty array so the\n // directive initialises gracefully and fills in when the store updates.\n if (result == null) return [];\n\n if (!Array.isArray(result))\n throw new AreCompilerError({\n title: 'Invalid \"for\" Directive Value',\n description: `The \"for\" directive expects an array but got ${typeof result}. Expression: \"${fullContent}\". Received: ${JSON.stringify(result)}`,\n });\n\n return result;\n }\n\n /**\n * Creates a single item node from the template, registers it as a child of\n * the owner, initialises it, injects item-scoped store values, and activates\n * its scene so the mount/compile cycle will include it.\n *\n * NOTE: This method does NOT call compile() or mount() — the caller is\n * responsible for doing so when the main lifecycle cycle won't cover it\n * (i.e. during update, but not during the initial compile phase).\n */\n private spawnItemNode(\n template: AreHTMLNode,\n owner: AreHTMLNode,\n key: string,\n index: string | undefined,\n item: any,\n i: number,\n ): AreHTMLNode {\n const itemNode = template.clone() as AreHTMLNode;\n\n owner.addChild(itemNode);\n\n const queue = [itemNode];\n\n while (queue.length > 0) {\n const current = queue.shift()!\n\n current.init();\n\n queue.push(...current.children as AreHTMLNode[]);\n }\n\n /**\n * Resolve or create a directive context for the item node. This is needed to hold the item-specific store values (e.g. the \"item\" and \"index\" in a \"for\" loop) that the template's bindings will reference during compile and update. The context is shared among all clones of the same template, but that's fine because each clone gets its own scope values assigned here.\n */\n let directiveContext = itemNode.scope.resolveFlat(AreDirectiveContext);\n\n if (!directiveContext) {\n directiveContext = new AreDirectiveContext(itemNode.aseid);\n itemNode.scope.register(directiveContext);\n }\n\n directiveContext.scope = {\n ...directiveContext.scope,\n [key]: item,\n [index || 'index']: i,\n }\n\n itemNode.scene.activate();\n\n return itemNode;\n }\n}"]}
1
+ {"version":3,"sources":["../../../src/directives/AreDirectiveFor.directive.ts"],"names":["AreDirectiveFor","AreDirective","AreDirectiveContext","AddCommentInstruction","AreSchedulerHelper","context","AreHTMLEngineContext","AreCompilerError","A_Caller","A_Scope","AreStore","AreScene","A_Logger","A_Frame"],"mappings":"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","file":"AreDirectiveFor.directive.js","sourcesContent":["import { A_Caller, A_Inject, A_Scope } from \"@adaas/a-concept\";\nimport { A_Logger } from \"@adaas/a-utils/a-logger\";\nimport { AreDirectiveAttribute } from \"@adaas/are-html/attributes/AreDirective.attribute\";\nimport { AreCompilerError, AreScene, AreStore } from \"@adaas/are\";\nimport { AreDirective } from \"@adaas/are-html/directive/AreDirective.component\";\nimport { AddCommentInstruction } from \"@adaas/are-html/instructions/AddComment.instruction\";\nimport { AreHTMLNode } from \"@adaas/are-html/node\";\nimport { AreDirectiveContext } from \"@adaas/are-html/directive/AreDirective.context\";\nimport { A_Frame } from \"@adaas/a-frame/core\";\nimport { AreSchedulerHelper } from \"@adaas/are-html/helpers/AreScheduler.helper\";\nimport { AreHTMLEngineContext } from \"@adaas/are-html/context\";\n\n\ntype AreForExpression = {\n key: string;\n index: string | undefined;\n arrayExpr: string;\n /** Optional `track <expr>` clause, e.g. `track row.id` */\n trackExpr: string | undefined;\n};\n\n/**\n * Per-`$for` reentrancy state used to serialize chunked (async) renders.\n * Keyed by the directive attribute instance (one per `$for` in the template).\n */\ntype AreForRenderState = { running: boolean; pending: boolean };\n\n\n@A_Frame.Define({\n namespace: 'a-are-html',\n description: 'Built-in $for directive. Iterates over an array expression resolved from the store and renders a cloned template fragment per item, managing per-item subscopes and comment-node anchors. Supports keyed diffing via an optional track clause to minimise DOM mutations on collection updates.'\n})\n@AreDirective.Priority(1)\nexport class AreDirectiveFor extends AreDirective {\n\n /**\n * Lists whose number of NEW item nodes is at or below this threshold render\n * fully synchronously — byte-for-byte the previous behavior. Typical UIs\n * (menus, small tables) are therefore completely unaffected; only genuinely\n * large lists pay the (tiny) scheduling cost to keep the main thread responsive.\n */\n private static readonly SYNC_THRESHOLD = 100;\n\n /**\n * Per-chunk time budget (ms). During a large-list render we mount item nodes\n * until this much time has elapsed, then yield to the browser so it can paint\n * and process input before the next chunk. ~16ms targets one animation frame.\n */\n private static readonly CHUNK_BUDGET_MS = 16;\n\n /**\n * Per-attribute serialization state. A new update() that arrives while a\n * chunked render of the SAME `$for` is still in flight does NOT start a second\n * concurrent pass (which could interleave mutations on the shared children\n * list); instead it marks `pending` and the in-flight run re-runs once more\n * with the latest data when it finishes. This guarantees the children list is\n * only ever mutated by one pass at a time and the final state always reflects\n * the most recent store value.\n */\n private static readonly renderState = new WeakMap<object, AreForRenderState>();\n\n\n @AreDirective.Transform\n transform(\n @A_Inject(A_Caller) attribute: AreDirectiveAttribute,\n @A_Inject(A_Scope) scope: A_Scope,\n @A_Inject(AreStore) store: AreStore,\n @A_Inject(AreScene) scene: AreScene,\n @A_Inject(A_Logger) logger: A_Logger,\n ...args: any[]\n ) {\n\n logger.debug(`[Transform] directive $FOR for <${attribute.owner.aseid.toString()}>`)\n\n const node = attribute.owner;\n\n /**\n * Transfer the original node's scope (with all registered attributes and children)\n * to the template clone, and give the owner node a fresh empty scope.\n * This mirrors the $if directive's approach, making the owner a lightweight\n * group container whose sole visible presence is a comment placeholder.\n */\n const forTemplate = node.cloneWithScope();\n\n /**\n * Remove the $for attribute from the template so iterative clones do not\n * re-trigger this directive during their own transform phase.\n * Re-register it on the owner so the reactive compile/update pipeline keeps working.\n */\n const forAttr = forTemplate.attributes.find(d => d.name === attribute.name);\n\n if (forAttr) {\n forTemplate.scope.deregister(forAttr);\n node.scope.register(forAttr);\n }\n\n /**\n * Re-initialize the owner node with its fresh scope so it becomes a valid\n * group container that will own the generated item nodes as children.\n */\n node.init();\n\n /**\n * Store the template for use in compile and update.\n */\n attribute.template = forTemplate;\n\n\n /**\n * Parse the $for expression and evaluate the source array.\n */\n const { key, index, arrayExpr } = this.parseExpression(attribute.content);\n // Item-scoped variables from an enclosing directive (e.g. the `row` of an\n // outer `$for`) so a nested `$for=\"cell in row.cells\"` resolves correctly.\n // Use resolve() (not resolveFlat) so the ENCLOSING item's context — which\n // lives on an ancestor scope, not on this directive's own node — is found.\n const contextScope = attribute.owner.scope.resolve(AreDirectiveContext)?.scope || {};\n const array = this.resolveArray(store, arrayExpr, attribute.content, contextScope);\n\n attribute.value = array;\n\n /**\n * For each item in the array, spawn a clone of the template with the\n * item's store values pre-set and its scene activated.\n *\n * The children are added to the owner node before the main compiler's\n * children iteration loop runs, so the main cycle will compile them —\n * no explicit child.compile() call is needed here.\n */\n for (let i = 0; i < array.length; i++) {\n this.spawnItemNode(attribute.template!, attribute.owner, key, index, array[i], i);\n }\n }\n\n\n @AreDirective.Compile\n compile(\n @A_Inject(A_Caller) attribute: AreDirectiveAttribute,\n @A_Inject(AreStore) store: AreStore,\n @A_Inject(AreScene) scene: AreScene,\n ...args: any[]\n ): void {\n /**\n * Replace the group node's default host declaration with a comment placeholder\n * so the owner element itself does not render as a DOM element — the item nodes\n * render as its children instead.\n */\n const hostInstruction = scene.host!;\n const commentIdentifier = ` --- for: ${attribute.template!.id} --- `;\n const declaration = new AddCommentInstruction({ content: commentIdentifier });\n\n scene.setHost(declaration);\n scene.planBefore(declaration, hostInstruction);\n scene.unPlan(hostInstruction);\n }\n\n\n @AreDirective.Update\n update(\n @A_Inject(A_Caller) attribute: AreDirectiveAttribute,\n @A_Inject(AreStore) store: AreStore,\n @A_Inject(AreScene) scene: AreScene,\n ...args: any[]\n ): void | Promise<void> {\n /**\n * Serialize chunked renders per `$for`. If a previous large-list render\n * is still streaming item nodes across macrotasks, do NOT start a second\n * concurrent pass — that would interleave two diffs over the same shared\n * children list (and leave half-compiled item nodes that the next diff\n * would wrongly \"reuse\"). Mark a pass as pending instead; the in-flight\n * run re-diffs once more from the latest store value when it completes.\n */\n let state = AreDirectiveFor.renderState.get(attribute);\n if (!state) {\n state = { running: false, pending: false };\n AreDirectiveFor.renderState.set(attribute, state);\n }\n if (state.running) {\n state.pending = true;\n return;\n }\n\n return this.performUpdate(attribute, store, scene, state);\n }\n\n /**\n * Core of the `$for` update: re-diff the source array against the current\n * children, reconcile reused/removed items, then mount the new ones (small\n * lists synchronously, large lists time-sliced). Never called while another\n * pass for the same `$for` is in flight (see `update`).\n */\n private performUpdate(\n attribute: AreDirectiveAttribute,\n store: AreStore,\n scene: AreScene,\n state: AreForRenderState,\n ): void | Promise<void> {\n /**\n * Re-evaluate the source array.\n */\n const { key, index, arrayExpr, trackExpr } = this.parseExpression(attribute.content);\n const owner = attribute.owner;\n // Item-scoped variables from an enclosing directive (see transform()).\n // resolve() walks ancestor scopes to find the enclosing item's context.\n const contextScope = owner.scope.resolve(AreDirectiveContext)?.scope || {};\n const newArray = this.resolveArray(store, arrayExpr, attribute.content, contextScope);\n\n const currentChildren = [...owner.children] as AreHTMLNode[];\n\n attribute.value = newArray;\n\n /**\n * Is this `$for`'s subtree currently rendered into the DOM?\n *\n * A `$for` can update while its subtree is detached — e.g. it lives\n * inside a `$if` whose condition is currently false (the documented\n * `<div $if><x $for></div>` nesting). The directive still receives the\n * store change and re-diffs, but it must NOT mount/unmount item nodes\n * directly while detached: the `$for` anchor (and its ancestors) are\n * not in the DOM, so the interpreter's mount-point walk would fall\n * through to the nearest *mounted* ancestor (the `$if` comment in the\n * grandparent) and HOIST the items out of their intended container.\n * When the ancestor `$if` later activates, its mount cascade applies\n * the already-compiled item instructions in the correct place.\n *\n * Detached === any ancestor scene is inactive (regular nodes default\n * to an active scene; only structural directives deactivate one).\n */\n const attached = this.isAttached(owner);\n\n const computeKey = this.makeKeyFn(key, index, trackExpr);\n\n // ── 1. Index existing children by stable key ────────────────────────\n const childByKey = new Map<any, AreHTMLNode>();\n const remaining = new Set<AreHTMLNode>();\n\n for (let i = 0; i < currentChildren.length; i++) {\n const child = currentChildren[i];\n const ctx = child.scope.resolveFlat(AreDirectiveContext);\n const k = ctx ? computeKey(ctx.scope[key], ctx.scope[index || 'index']) : Symbol('orphan');\n childByKey.set(k, child);\n remaining.add(child);\n }\n\n // ── 2. Walk desired list; reuse existing or record items to create ──\n // NOTE: new item nodes are NOT spawned here. Spawning (cloneWithScope +\n // subtree init + scene activation) is the dominant cost of a large\n // render, so it is deferred into the time-sliced loop below alongside\n // transform/compile/mount. Existing (keyed) children are reconciled in\n // place synchronously — that is cheap and keeps reused rows stable.\n const toCreate: Array<{ item: any; idx: number; key: any }> = [];\n\n // Final identity → node map covering BOTH reused and newly created item\n // nodes, plus the desired key order. After all items are mounted these\n // drive the DOM reorder pass (step 5) so the rendered order always\n // matches the source array — making prepend / shuffle / arbitrary\n // reorders move existing rows instead of only appending at the end.\n const finalByKey = new Map<any, AreHTMLNode>();\n const orderedKeys: any[] = new Array(newArray.length);\n\n for (let i = 0; i < newArray.length; i++) {\n const item = newArray[i];\n const k = computeKey(item, i);\n orderedKeys[i] = k;\n const existing = childByKey.get(k);\n\n if (existing) {\n remaining.delete(existing);\n finalByKey.set(k, existing);\n\n let directiveContext = existing.scope.resolveFlat(AreDirectiveContext);\n if (!directiveContext) {\n directiveContext = new AreDirectiveContext(existing.aseid);\n existing.scope.register(directiveContext);\n }\n directiveContext.scope = {\n ...directiveContext.scope,\n [key]: item,\n [index || 'index']: i,\n };\n } else {\n toCreate.push({ item, idx: i, key: k });\n }\n }\n\n // ── 3. Unmount + detach removed children ─────────────────────────────\n for (const child of remaining) {\n // Only revert DOM if the subtree is live; a detached subtree's item\n // nodes were never mounted (see `attached` rationale above), so\n // unmounting them is a no-op at best and risks reverting stale state.\n if (attached) child.unmount();\n owner.removeChild(child);\n }\n\n // ── 4. Create + mount the new item nodes. ───────────────────────────\n // `spawnItemNode` appends to `owner.children` immediately; new rows are\n // therefore mounted at the end (just before the anchor comment). The\n // reorder pass (step 5) then moves any out-of-position node so the final\n // DOM order matches the source array.\n const createItem = (desc: { item: any; idx: number; key: any }) => {\n const child = this.spawnItemNode(attribute.template!, owner, key, index, desc.item, desc.idx);\n finalByKey.set(desc.key, child);\n child.transform();\n child.compile();\n // While detached, stop after compile: the item's instructions are\n // planned and the ancestor `$if`'s mount cascade will apply them in\n // the correct container once the condition becomes truthy. Mounting\n // here would hoist the item to the nearest mounted ancestor.\n if (attached) child.mount();\n };\n\n // Small lists → fully synchronous, identical to the previous behavior.\n if (toCreate.length <= AreDirectiveFor.SYNC_THRESHOLD) {\n for (const desc of toCreate) createItem(desc);\n // ── 5. Reorder live DOM to match the source array order ──────────\n if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);\n return this.finishUpdate(attribute, store, scene, state);\n }\n\n // Large lists → time-sliced render. Create item nodes until the frame\n // budget elapses, then yield to the browser (zero-delay macrotask) so\n // it can paint and stay responsive instead of blocking for the whole\n // batch. The `state.running` flag (see `update`) prevents any other\n // update() for this `$for` from interleaving while we stream.\n state.running = true;\n let cursor = 0;\n\n const processChunk = (): void | Promise<void> => {\n try {\n const start = AreSchedulerHelper.now();\n while (cursor < toCreate.length) {\n createItem(toCreate[cursor]);\n cursor++;\n if (AreSchedulerHelper.now() - start >= AreDirectiveFor.CHUNK_BUDGET_MS) break;\n }\n } catch (error) {\n // Never leave the `$for` wedged in the running state on failure,\n // or every future update would be silently deferred forever.\n state.running = false;\n state.pending = false;\n throw error;\n }\n\n if (cursor < toCreate.length) {\n return new Promise<void>(resolve => {\n AreSchedulerHelper.scheduleMacrotask(() => resolve(processChunk()));\n });\n }\n\n // ── 5. Reorder live DOM to match the source array order ──────────\n if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);\n return this.finishUpdate(attribute, store, scene, state);\n };\n\n return processChunk();\n }\n\n /**\n * Repositions the item nodes' DOM elements so the rendered order matches the\n * source array order. The keyed diff (steps 1–4) reuses existing nodes in\n * place and mounts new ones at the end; without this pass a `prepend` or\n * `shuffle` would leave reused rows where they were and pile new rows at the\n * bottom. We walk the desired order RIGHT-TO-LEFT, keeping a `ref` pointer to\n * the element each item must precede (starting at the `$for` anchor comment),\n * and only call `insertBefore` when an element is not already in position —\n * so a plain `append` (already-correct order) performs ZERO DOM moves.\n */\n private reconcileOrder(\n owner: AreHTMLNode,\n orderedKeys: any[],\n finalByKey: Map<any, AreHTMLNode>,\n ): void {\n const context = owner.scope.resolve<AreHTMLEngineContext>(AreHTMLEngineContext);\n if (!context) return;\n\n const anchor = context.getNodeElement(owner);\n if (!anchor || !anchor.parentNode) return;\n\n const parent = anchor.parentNode;\n let ref: Node = anchor;\n\n for (let i = orderedKeys.length - 1; i >= 0; i--) {\n const node = finalByKey.get(orderedKeys[i]);\n if (!node) continue;\n\n const element = context.getNodeElement(node);\n // Element may be missing if the item is still detached/unmounted.\n if (!element || element.parentNode !== parent) continue;\n\n // Already immediately before `ref` — no move needed.\n if (element.nextSibling !== ref) {\n parent.insertBefore(element, ref);\n }\n ref = element;\n }\n }\n\n\n /**\n * Completes an update pass. If another update() arrived while a chunked\n * render was streaming, run exactly one more pass now from the latest store\n * value so the final DOM always reflects the most recent data.\n */\n private finishUpdate(\n attribute: AreDirectiveAttribute,\n store: AreStore,\n scene: AreScene,\n state: AreForRenderState,\n ): void | Promise<void> {\n state.running = false;\n if (state.pending) {\n state.pending = false;\n return this.performUpdate(attribute, store, scene, state);\n }\n }\n\n\n /**\n * Walks the node's ancestor chain (inclusive) and reports whether the\n * whole path is currently active — i.e. the subtree is actually rendered\n * into the DOM. A single inactive ancestor scene (e.g. a `$if` whose\n * condition is false) means the subtree is detached.\n */\n private isAttached(node: AreHTMLNode): boolean {\n let current: AreHTMLNode | undefined = node;\n while (current) {\n if (current.scene?.isInactive) return false;\n current = current.parent as AreHTMLNode | undefined;\n }\n return true;\n }\n\n\n // ─────────────────────────────────────────────────────────────────────────────\n // ── Helpers ──────────────────────────────────────────────────────────────────\n // ─────────────────────────────────────────────────────────────────────────────\n\n /**\n * Build a key-function that derives a stable identity from each item.\n * If the user provided a `track <expr>` clause, evaluate it as a path on\n * the item; otherwise fall back to the item identity (reference equality).\n */\n private makeKeyFn(key: string, index: string | undefined, trackExpr: string | undefined): (item: any, i: number) => any {\n if (!trackExpr) {\n return (item, i) => item ?? i;\n }\n\n // Strip any leading `key.` so users can write `track row.id`.\n const path = trackExpr.startsWith(key + '.') ? trackExpr.slice(key.length + 1) : trackExpr;\n\n return (item, i) => {\n if (item == null) return i;\n if (path === key || path === '$index') return path === '$index' ? i : item;\n\n // dotted path lookup\n const parts = path.split('.');\n let v: any = item;\n for (const p of parts) {\n if (v == null) return i;\n v = v[p];\n }\n return v ?? i;\n };\n }\n\n /**\n * Parses the $for expression string into its constituent parts.\n *\n * Supported formats:\n * item in items\n * item, index in items\n * (item, index) in items\n * item in filter(items)\n * item, index in filter(items, 'active')\n * item in items track item.id\n * (item, i) in items track item.id\n */\n private parseExpression(content: string): AreForExpression {\n // Strip optional `track <expr>` suffix first.\n let trackExpr: string | undefined;\n const trackIdx = content.search(/\\s+track\\s+/);\n let body = content;\n if (trackIdx !== -1) {\n const m = content.slice(trackIdx).match(/\\s+track\\s+(.+)$/);\n if (m) {\n trackExpr = m[1].trim();\n body = content.slice(0, trackIdx).trim();\n }\n }\n\n const inIndex = body.lastIndexOf(' in ');\n const keyAndIndex = body.slice(0, inIndex).trim().replace(/^\\(|\\)$/g, '');\n const arrayExpr = body.slice(inIndex + 4).trim();\n const keyParts = keyAndIndex.split(',').map(p => p.trim());\n\n return {\n key: keyParts[0],\n index: keyParts[1] || undefined,\n arrayExpr,\n trackExpr,\n };\n }\n\n /**\n * Resolves the array expression against the store.\n * Supports both plain key lookups and function-call expressions:\n * items → store.get('items')\n * filter(items) → store.get('filter')(store.get('items'))\n *\n * `contextScope` carries item-scoped variables introduced by an enclosing\n * directive (e.g. the `row` of an outer `$for`). It is consulted BEFORE the\n * store so a nested `$for=\"cell in row.cells\"` resolves `row` from the\n * parent iteration instead of looking for a (non-existent) top-level store\n * key. Leading identifiers not present in the context fall back to the store.\n */\n private resolveArray(\n store: AreStore,\n arrayExpr: string,\n fullContent: string,\n contextScope: Record<string, any> = {},\n ): any[] {\n // Resolve a leading identifier from the directive context first, then\n // the store — mirrors how bindings/interpolations evaluate scoped vars.\n const getRoot = (rawKey: string): any => {\n const k = rawKey.replace(/\\?$/, '');\n return (k in contextScope) ? contextScope[k] : store.get(k as any);\n };\n\n let result: any;\n const callMatch = arrayExpr.match(/^([^(]+)\\((.+)\\)$/);\n\n if (callMatch) {\n const fnName = callMatch[1].trim();\n const fn = getRoot(fnName);\n\n if (typeof fn !== 'function')\n throw new AreCompilerError({\n title: 'Invalid \"for\" Directive Function',\n description: `The expression \"${fnName}\" in the \"for\" directive does not resolve to a function in the store. Received: ${typeof fn}`,\n });\n\n const rawArgs = callMatch[2].split(',').map(a => a.trim());\n const resolvedArgs = rawArgs.map(arg => {\n if (arg.startsWith(\"'\") && arg.endsWith(\"'\")) return arg.slice(1, -1);\n if (arg.startsWith('\"') && arg.endsWith('\"')) return arg.slice(1, -1);\n if (!isNaN(Number(arg))) return Number(arg);\n // Dotted-path / optional-chain: e.g. `record?.embedding` or `record.data`\n const stripped = arg.replace(/\\?$/, '');\n if (stripped.includes('.')) {\n const parts = stripped.split('.').map(p => p.replace(/\\?$/, ''));\n let val: any = getRoot(parts[0]);\n for (let j = 1; j < parts.length; j++) {\n if (val == null) return undefined;\n val = val[parts[j]];\n }\n return val ?? undefined;\n }\n return getRoot(stripped);\n });\n\n result = (fn as Function)(...resolvedArgs);\n } else if (arrayExpr.includes('.')) {\n // dotted-path lookup: e.g. \"list.items\" or \"record?.keywords\"\n // Strip optional-chaining `?` suffix from each segment so that\n // `record?.keywords` resolves the same as `record.keywords`.\n const parts = arrayExpr.split('.').map(p => p.replace(/\\?$/, ''));\n result = getRoot(parts[0]);\n for (let i = 1; i < parts.length; i++) {\n if (result == null) break;\n result = result[parts[i]];\n }\n } else {\n result = getRoot(arrayExpr);\n }\n\n // null / undefined from optional-chaining expressions (e.g. `record?.keywords`)\n // means the source object is not yet loaded — treat as empty array so the\n // directive initialises gracefully and fills in when the store updates.\n if (result == null) return [];\n\n if (!Array.isArray(result))\n throw new AreCompilerError({\n title: 'Invalid \"for\" Directive Value',\n description: `The \"for\" directive expects an array but got ${typeof result}. Expression: \"${fullContent}\". Received: ${JSON.stringify(result)}`,\n });\n\n return result;\n }\n\n /**\n * Creates a single item node from the template, registers it as a child of\n * the owner, initialises it, injects item-scoped store values, and activates\n * its scene so the mount/compile cycle will include it.\n *\n * NOTE: This method does NOT call compile() or mount() — the caller is\n * responsible for doing so when the main lifecycle cycle won't cover it\n * (i.e. during update, but not during the initial compile phase).\n */\n private spawnItemNode(\n template: AreHTMLNode,\n owner: AreHTMLNode,\n key: string,\n index: string | undefined,\n item: any,\n i: number,\n ): AreHTMLNode {\n const itemNode = template.clone() as AreHTMLNode;\n\n owner.addChild(itemNode);\n\n const queue = [itemNode];\n\n while (queue.length > 0) {\n const current = queue.shift()!\n\n current.init();\n\n queue.push(...current.children as AreHTMLNode[]);\n }\n\n /**\n * Resolve or create a directive context for the item node. This is needed to hold the item-specific store values (e.g. the \"item\" and \"index\" in a \"for\" loop) that the template's bindings will reference during compile and update. The context is shared among all clones of the same template, but that's fine because each clone gets its own scope values assigned here.\n */\n let directiveContext = itemNode.scope.resolveFlat(AreDirectiveContext);\n\n if (!directiveContext) {\n directiveContext = new AreDirectiveContext(itemNode.aseid);\n itemNode.scope.register(directiveContext);\n }\n\n directiveContext.scope = {\n ...directiveContext.scope,\n [key]: item,\n [index || 'index']: i,\n }\n\n itemNode.scene.activate();\n\n return itemNode;\n }\n}"]}