arrmatura 6.3.3 → 6.5.1

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 (117) hide show
  1. package/README.md +62 -22
  2. package/dist/index.js +4 -6
  3. package/docs/architecture.md +151 -0
  4. package/docs/built-ins.md +223 -0
  5. package/docs/cml.md +342 -0
  6. package/index.ts +1 -22
  7. package/package.json +22 -18
  8. package/src/compiler/index.test.ts +152 -0
  9. package/src/{registry → compiler}/index.ts +22 -18
  10. package/src/controls/Iterative.test.ts +116 -0
  11. package/src/controls/Iterative.ts +127 -0
  12. package/src/controls/block.test.ts +44 -0
  13. package/src/controls/block.ts +25 -0
  14. package/src/controls/composition.test.ts +56 -0
  15. package/src/{registry → controls}/composition.ts +13 -22
  16. package/src/controls/conditionals.test.ts +103 -0
  17. package/src/controls/conditionals.ts +79 -0
  18. package/src/controls/connector.test.ts +62 -0
  19. package/src/controls/connector.ts +75 -0
  20. package/src/controls/elementary.test.ts +37 -0
  21. package/src/{registry → controls}/elementary.ts +3 -6
  22. package/src/controls/fragment.test.ts +26 -0
  23. package/src/{registry → controls}/fragment.ts +3 -4
  24. package/src/controls/index.ts +10 -0
  25. package/src/controls/root.test.ts +50 -0
  26. package/src/{registry → controls}/root.ts +8 -10
  27. package/src/controls/routing.test.ts +64 -0
  28. package/src/controls/routing.ts +45 -0
  29. package/src/controls/selection.test.ts +62 -0
  30. package/src/{registry → controls}/selection.ts +4 -9
  31. package/src/controls/slot.test.ts +62 -0
  32. package/src/{registry → controls}/slot.ts +12 -10
  33. package/src/core/Arrmatron.test.ts +680 -0
  34. package/src/core/Arrmatron.ts +470 -236
  35. package/src/core/Component.test.ts +203 -0
  36. package/src/core/Component.ts +106 -49
  37. package/src/core/ManifestNode.test.ts +225 -0
  38. package/src/core/ManifestNode.ts +213 -55
  39. package/src/core/NativeElement.test.ts +173 -0
  40. package/src/core/NativeElement.ts +89 -0
  41. package/src/core/Platform.test.ts +127 -0
  42. package/src/core/Platform.ts +116 -0
  43. package/src/core/Registry.test.ts +171 -0
  44. package/src/core/Registry.ts +162 -0
  45. package/src/core/consts.ts +3 -0
  46. package/src/core/launch.test.ts +37 -0
  47. package/src/core/launch.ts +18 -0
  48. package/src/expr/Expression.test.ts +132 -0
  49. package/src/expr/Expression.ts +62 -0
  50. package/src/expr/ExpressionParser.test.ts +353 -0
  51. package/src/expr/ExpressionParser.ts +216 -0
  52. package/src/expr/ExpressionParserBase.test.ts +292 -0
  53. package/src/expr/ExpressionParserBase.ts +175 -0
  54. package/src/expr/ParserContext.ts +17 -0
  55. package/src/expr/compileEmitterExpression.test.ts +142 -0
  56. package/src/expr/compileEmitterExpression.ts +64 -0
  57. package/src/expr/compilePlaceholder.test.ts +44 -0
  58. package/src/expr/compilePlaceholder.ts +14 -0
  59. package/src/expr/operations.test.ts +138 -0
  60. package/src/expr/operations.ts +53 -0
  61. package/src/expr/prepareConnectedProps.test.ts +44 -0
  62. package/src/expr/prepareConnectedProps.ts +16 -0
  63. package/src/expr/resolveExpression.test.ts +96 -0
  64. package/src/expr/resolveExpression.ts +66 -0
  65. package/src/index.ts +8 -0
  66. package/src/types.ts +199 -0
  67. package/src/utils/applyStateChangedToImpl.test.ts +28 -0
  68. package/src/utils/applyStateChangedToImpl.ts +12 -16
  69. package/src/utils/applyTemplate.test.ts +53 -0
  70. package/src/utils/applyTemplate.ts +20 -0
  71. package/src/utils/asyncValueCall.test.ts +20 -0
  72. package/src/utils/asyncValueCall.ts +11 -0
  73. package/src/utils/capitalize.test.ts +21 -0
  74. package/src/utils/capitalize.ts +7 -0
  75. package/src/utils/createRegisterTypes.test.ts +70 -0
  76. package/src/utils/createRegisterTypes.ts +67 -0
  77. package/src/utils/defineCalculatedProperty.test.ts +58 -0
  78. package/src/utils/defineCalculatedProperty.ts +52 -0
  79. package/src/utils/defineObjectRef.test.ts +30 -0
  80. package/src/utils/defineObjectRef.ts +18 -0
  81. package/src/utils/hashCodeOf.test.ts +124 -0
  82. package/src/utils/hashCodeOf.ts +147 -0
  83. package/src/utils/index.ts +9 -0
  84. package/src/utils/isEquals.test.ts +76 -0
  85. package/src/utils/isEquals.ts +74 -0
  86. package/src/utils/mapEntries.test.ts +22 -0
  87. package/src/utils/mapEntries.ts +9 -0
  88. package/src/utils/mergeObject.test.ts +34 -0
  89. package/src/utils/mergeObject.ts +25 -0
  90. package/src/utils/narrowData.test.ts +51 -0
  91. package/src/utils/narrowData.ts +27 -12
  92. package/src/utils/scalarParse.test.ts +38 -0
  93. package/src/utils/scalarParse.ts +41 -0
  94. package/src/utils/splitTopLevel.test.ts +34 -0
  95. package/src/utils/splitTopLevel.ts +28 -0
  96. package/src/utils/stringify.test.ts +59 -0
  97. package/src/utils/stringify.ts +26 -21
  98. package/src/utils/toNativeTree.test.ts +53 -0
  99. package/src/utils/toNativeTree.ts +24 -0
  100. package/src/utils/xmlParse.test.ts +78 -0
  101. package/src/utils/xmlParse.ts +120 -0
  102. package/dist/index.cjs +0 -6
  103. package/dist/index.cjs.map +0 -7
  104. package/dist/index.js.map +0 -7
  105. package/docs/components.md +0 -89
  106. package/docs/getstarted.md +0 -63
  107. package/docs/manual.md +0 -9
  108. package/docs/templates.md +0 -222
  109. package/src/core/resolveExpression.ts +0 -29
  110. package/src/registry/Iterative.ts +0 -129
  111. package/src/registry/conditionals.ts +0 -80
  112. package/src/registry/connector.ts +0 -48
  113. package/src/registry/routing.ts +0 -37
  114. package/src/utils/FingerprintMashine.ts +0 -126
  115. package/src/utils/compileExpression.ts +0 -136
  116. package/src/utils/objectFingerprint.spec.ts +0 -70
  117. package/types.ts +0 -90
package/README.md CHANGED
@@ -1,35 +1,75 @@
1
1
  # Arrmatura
2
2
 
3
- > ![](assets/logo.svg)
3
+ ## What is Arrmatura?
4
4
 
5
- `Arrmatura` is an universal framework, which promotes ideas of declarative functional programming.
6
-
7
- It comprises of
8
-
9
- - formal notation to define components and its composition.
10
- - Typescript runtime, enabling development of applications of any kind.
11
- - Implementation for web clients.
12
- - UI kit with plenty of web components, services, forms, editors etc.
13
-
14
- The framework is designed to facilitate the creation of applications
15
- by allowing developers to define components and specify how they are composed and connected.
5
+ **Arrmatura** is a lightweight framework designed to facilitate the creation of applications by allowing developers to compose components and to specify how they are interconnected with data.
16
6
 
17
7
  This component composition is achieved through a declarative syntax,
18
8
  which describes how data flows between components and how it is propagated across the composition.
19
9
 
20
- This streamlines the development process and makes it easier for developers
21
- to create complex and dynamic applications that can respond to changes in data in real-time.
10
+ This streamlines the development process and makes it easier for developers to create complex and dynamic applications that can respond to changes in data in real-time.
22
11
 
23
12
  Overall, the framework aims to provide a flexible and intuitive way
24
- to build web applications that are scalable, maintainable, and responsive.
13
+ to build web applications that are scalable, maintainable, and easy to use.
25
14
 
26
- ## Documentation
15
+ > Arrmatura is ideal for developers seeking simplicity and power in building applications.
16
+
17
+ ## Key features
18
+
19
+ ### Declarative Component Composition with Reactive data flow
20
+
21
+ Define reusable components using pure declarative **CML(Component Markup Language) notation** syntax.
22
+
23
+ Arrmatura allows you to write complex logic directly in your templates without the typical boilerplate of "hooks" or "state
24
+ management" libraries:
25
+
26
+ - Inline Actions: click="-> count = count + 1"
27
+ - Pipes: {@api.data | upper | slice:0:10}
28
+ - Reactive References: Using @ref allows components to communicate effortlessly (e.g., `<Consumer data="{@api.result}" />`).
29
+
30
+ ### AI-Friendly Design
31
+
32
+ The framework is built from the ground up to be "AI-friendly".
33
+ Its declarative CML syntax is specifically designed to be easily generated, read, and modified by Large Language Models (LLMs).
34
+
35
+ By treating the application definition as a clear specification (XML-like templates), it bridges the gap between human requirements and executable code.
27
36
 
28
- - [Manual](./docs/manual.md)
37
+ Arrmatura is exciting because it feels like a glimpse into a future where code is written for both humans and AI, stripped of unnecessary complexity, and focused on pure architectural elegance.
29
38
 
30
- ## Examples
39
+ ### Performance
40
+
41
+ What Makes It Fast
42
+
43
+ 1. **No Virtual DOM**: Direct DOM updates, no diffing overhead
44
+ 2. **Cached Expressions**: Expressions compiled once, reused
45
+ 3. **Selective Updates**: Only changed components redraw
46
+ 4. **Lazy Property Resolution**: Properties resolved on-demand
47
+ 5. **Manifest Node Caching**: Template compilation cached
48
+
49
+ Unlike React, Arrmatura rejects the Virtual DOM diffing process. Instead, it uses a direct-update mechanism.
50
+
51
+ - No VDOM overhead: It doesn't need to rebuild a virtual tree on every change.
52
+ - Selective Redraws: It tracks dependencies and updates only the specific parts of the real DOM that need to change.
53
+ - Runtime Interpretation: The "Runtime Engine" evaluates templates at runtime, allowing for dynamic component composition that is often faster and lighter than complex build-time transpilation.
54
+
55
+ ### Unopinionated
56
+
57
+ Arrmatura doesn't force you into a specific architectural pattern.
58
+
59
+ - Flexible State Management: You can choose to manage state locally within components or use external stores without any framework-imposed structure.
60
+ - Pluggable Ecosystem: It can integrate with various libraries and tools for routing, data fetching, and more, without being tied to a specific way of doing things.
61
+ - Component Autonomy: Each component is an independent unit that can manage its own state and logic, making it easy to reuse and compose without worrying about global state or context.
62
+
63
+ ### Extreme Minimalism
64
+
65
+ It strips away the "toolchain bloat" of modern JavaScript.
66
+
67
+ - Lightweight Runtime: The core engine is small and efficient.
68
+ - Platform Abstraction: It separates logic from the DOM, meaning the "Soul" (Arrmatura logic) can theoretically be ported to other "Bodies" (Native, Console, etc.).
69
+ - "Excellence through simplicity,": focusing on doing more with less code.
70
+
71
+ ## Documentation
31
72
 
32
- - [Emoji List](https://emojis-list.web.app/)
33
- - [Focusator](https://focusator.web.app/)
34
- - [Countries List](https://countries-list.web.app/)
35
- - [Game Solver](https://dlitskevich.github.io/solver/) ([source](https://github.com/dlitskevich/solver/tree/master/app))
73
+ - the runtime model [architecture.md](./docs/architecture.md).
74
+ - the CML syntax reference [cml.md](./docs/cml.md);
75
+ - the semantics of each built-in element [built-ins.md](./docs/built-ins.md);
package/dist/index.js CHANGED
@@ -1,6 +1,4 @@
1
- var _=(r,t)=>{if(!(!r||!t||typeof r!="object")){if(typeof t=="function")return t(r);if(typeof t=="string"){if(!t.includes("."))return r[t];t=t.split(".")}return t.reduce((e,n)=>e?e[n]:void 0,r)}};function tt(r){return r==null?"":String(r)}function S(r){let t=r==null?"":String(r);return t.length?t[0].toUpperCase()+t.slice(1):""}var At=1,et=(r="")=>r+String(At++);var rt=r=>r;var Tt=(r,t)=>({id:r,value:t});var nt=(r,t=Tt)=>r?Object.entries(r).map(([e,n])=>t(e,n)):[];var y=r=>{if(r==="")return"";if(r==="true")return!0;if(r==="false")return!1;if(r==="0")return 0;if(r==="1")return 1;if(r&&"1234567890+-".includes(r[0])&&r.length<=17){let t=+r;return isNaN(t)?r:t}if(r==="null")return null;if(r!=="undefined")return r};var Mt=/&#?[0-9a-z]{2,5};/g,It={amp:"&",gt:">",lt:"<",quot:'"',nbsp:" "};var Pt=function(r){let t=r.substring(1,r.length-1);return t[0]==="#"?String.fromCharCode(+t.slice(1)):It[t]||" "},ot=(r,t='"',e=t)=>r[0]===t&&r[r.length-1]===e?r.slice(1,-1):r,R=r=>String(r??"").replace(Mt,Pt);var Ot={RE_ATTRS:/([a-zA-Z][a-zA-Z0-9-:]*)(="[^"]*")?/g,RE_EMPTY:/^\s*$/,RE_XML_COMMENT:/<!--((?!-->)[\s\S])*-->/g,RE_XML_TAG:/(<)(\/?)([a-z][a-z0-9\-_:.]*)((?:\s+[a-zA-Z][a-z0-9-:]*(?:="[^"]*")?)*)\s*(\/?)>/gi,SINGLE_TAGS:{img:1,input:1,br:1,hr:1,col:1,source:1}};function st(r){let{RE_XML_TAG:t,RE_EMPTY:e,RE_XML_COMMENT:n,RE_ATTRS:o,SINGLE_TAGS:s}={...Ot,...r},i=(p,c,m)=>{let h={tag:c,id:m};return(p.nodes??(p.nodes=[])).push(h),h},a=(p,c)=>{if(c){p.attrs={};for(let m=o.exec(c);m;m=o.exec(c)){let h=m[2]?y(R(ot(m[2].slice(1)))):!0;p.attrs[m[1]]=h}}};return p=>{let c=0,m=[{tag:"#root",id:String(c)}],h=String(p).trim().replace(n,""),g=0;for(let f=t.exec(h);f;f=t.exec(h)){let N=f.index&&h.slice(g,f.index);if(f[2]){if(N&&!m[0].nodes?.length&&(N===" "||!N.match(e))&&(m[0].text=R(N.trim())),m[0].tag!==f[3])throw new Error(` XML Parse closing tag does not match at: ${String(f.index)} near ${f.input.slice(Math.max(f.index-150,0),f.index)}^^^^${f.input.slice(f.index,Math.min(f.index+150,f.input.length))}`);m.shift()}else{let V=i(m[0],f[3],`X${String(++c)}`);if(a(V,f[4].trim()),!(f[5]||f[3]in s)&&(m.unshift(V),m.length===1))throw new Error(`Parse error at: ${f[0]}`)}g=t.lastIndex}return m[0].nodes??[]}}var it=st();var H=new Map;function pt(r,t){let e=(r<<5)-r+t;return e&e}var at=":".charCodeAt(0);function Dt(r){let t=0;if(H.has(r))t=H.get(r);else{for(let e=0;e<r.length;e++)t=pt(t,r.charCodeAt(e));H.set(r,t)}return t}var F=class{r=0;x;$path;get path(){return this.$path??(this.$path=new Set)}upNumber(t){return this.r=pt(this.r,t)}upString(t){return this.upNumber(Dt(t))}upObject(t){if(t.uid)return t.uid;if(t instanceof Date)return this.upNumber(t.getTime());if(!(this.path.size>10||this.path.has(t))){if(this.path.add(t),t instanceof Map)this.upString("$$$Map$$$"),t.forEach((e,n)=>{this.upString(n),this.upNumber(at),this.upAny(e)});else if(t instanceof Set)this.upString("$$$Set$$$"),t.forEach(e=>this.upAny(e));else if(Array.isArray(t))this.upString("$$$Array$$$"),t.forEach(e=>this.upAny(e));else{this.upString("$$$Object$$$");for(let e in t)Object.prototype.hasOwnProperty.call(t,e)&&e[0]!=="$"&&(this.upString(e),this.upNumber(at),this.upAny(t[e]))}return this.path.delete(t),this.r}}upAny(t){t==null?this.upString(String(t)):typeof t=="number"?isNaN(t)?this.upString("$$$NaN$$$"):this.upNumber(t):typeof t=="object"?this.upObject(t):typeof t=="string"?this.upString(t):typeof t=="boolean"?this.upString(`$$$Boolean$$$${t}`):typeof t=="function"||this.upString(String(t))}do(t){return t==null||typeof t!="object"||Object.isFrozen(t)?t:t.uid?t.uid:t.$ctx?t.$ctx.uid:(this.r=0,this.x=t,this.$path&&this.$path?.clear(),this.upObject(t),this.r)}},_t=new F,x=r=>_t.do(r);function ct(r,t){t.__stateChanged?t.__stateChanged(r):r.forEach((e,n)=>{let o=t[`set${S(n)}`];typeof o=="function"?o.call(t,e):t[n]=e})}var ut=r=>typeof r=="object"?Array.isArray(r)?r.length?`[${ut(r[0])??""} x${String(r.length)}]`:"[]":`{${Object.keys(r).toString()}}`:r??"",Rt=r=>{if(!r)return"";let t=[];return Object.entries(r).forEach(([e,n])=>{n&&e[0]!=="$"&&t.push(` ${e}="${ut(n)}"`)}),t.length?t.join(""):""},X=(r,t="")=>{let{uid:e,displayName:n=e,component:o,children:s}=r,i=s?.size?[...s.values()].map(a=>X(a,` ${t}`)).join(`
2
- `):"";return`${t}<${n}${Rt(o)}${i?`>
3
- ${i}
4
- ${t}</${n}>`:"/>"}`};var u=class{constructor(t,e,n,o){this.platform=t;this.manifest=e;this._parent=n;this.scope=o??this,this.root=o?.root??this,this.#n=this.manifest.resolveInitialProps(this),this.$component=this.createComponent(this.#n),this.refId&&this.refId.split(",").forEach(s=>this.scope.addReference(s,this))}$component;root;scope;$children;#t=!1;#e=!1;#r={};#n={};#s;#o=[];#a;#i;#p;get parent(){return this._parent}get uid(){return this.manifest.uid}get displayName(){return"\u{1F539}"}createComponent(t){return{...t}}up(t,e=!1){if(!t||this.#t)return;if(t instanceof Promise){let o=this.raceCondition("set");t.then(s=>o(()=>this.up(s)));return}let n=new Map;Object.entries(t).forEach(([o,s])=>{if(s instanceof Promise){let i=!o||o.startsWith("..."),a=this.raceCondition(`set:${o}`);s.then(p=>a(()=>this.up(i?p:{[o]:p})))}else if(o&&typeof s<"u")if(o[0]==="$")this.$component[o]!==s&&n.set(o.slice(1),s);else{let i=x(s);(!(o in this.#r)||i!==this.#r[o])&&(this.#r[o]=i,n.set(o,s))}}),(n.size||e)&&(ct(n,this.$component),this.touch())}touch(){this.recontent(),this.notify()}get(t){let e=this.#p??(this.#p=new Map);if(e.has(t))return e.get(t)();let n=null,o=this.$component,s=o[t];if(s&&typeof s=="function"){let i=s.bind(o);n=()=>i}else if(o.__getStateProperty)n=()=>o.__getStateProperty(t);else{let[i,...a]=t.split(".");if(i==="R"){let p=this.platform.getResource(a)??null;n=()=>p}else{let p=o[`get${S(i)}`],c=typeof p=="function"?()=>p.call(o)??null:()=>o[i]??null;n=a.length?()=>a.reduce((m,h)=>m?.[h]??null,c()):c}}return e.set(t,n),n()}getFromScope(t){return this.scope.get(t)}notify(){this.#s?.forEach(t=>t(this))}subscribe(t){let e=this.#s??(this.#s=new Set);return e.add(t),()=>{e.delete(t)}}emit(t,e){if(!this.#t)try{if(t.endsWith(")")){let[n,o]=t.split("(")[0].split("."),s=this.getByRef(n);if(!s)throw new Error(`No such reference: ${n}`);let i=s.$component,a=i[o];if(typeof a!="function")throw new Error(`Not a method: ${n}.${o}()`);let p=a.call(i,e);s.up(p)}else{let[n,o]=t.split(".");o||(o=n,n="this");let s=this.getByRef(n);if(!s)throw new Error(`No such reference: ${n}`);s.up(o==="*"?e:{[o]:e})}}catch(n){this.logError(`emit ${t}:`,n)}}done(){this.#t||(this.#t=!0,this.children?.forEach(t=>t.done()),this.#o?.forEach(t=>t(this)),this.#o=void 0,this._parent?.children?.delete(this.uid),this._parent=void 0)}defer(t){t&&typeof t=="function"&&(this.#o??(this.#o=[])).push(t)}settleAsChild(){this.#e?this.up(this.manifest.resolveProps(this),!0):(this.#e=!0,this.initConnectors(),this.up(this.#n,!0),this.up(this.$component.__init?.(this)))}get children(){return this.$children}get contentManifests(){return this.manifest.getSubNodes(this.platform)}get recontentScope(){return this.scope}recontent(){let t=this.contentManifests;this.platform.redraw(this,this.root),this.children?.forEach((n,o)=>{t?.get(o)||n.done()});let e=new Map;t?.forEach((n,o)=>{let s=this.$children?.get(o)??n.createArrmatron(this.platform,this,this.recontentScope);e.set(s.uid,s)}),this.$children=e;for(let n of e.values())n.settleAsChild()}get refId(){return this.manifest.refId}getByRef(t){if(!t)return;if(t==="this")return this;let e=this.#i?.[t];return e||this.parent?.getByRef(t)}addReference(t,e){(this.#i??(this.#i={}))[t]=e}initConnectors(){if(this.manifest.connectors)for(let[t,e]of this.manifest.connectors.entries()){let[n,o]=t.split("|"),[s,i]=n.split("."),a=this.parent?.getByRef(s);if(!a){this.logError(`Connect: No such ref: ${s}`);continue}if(!i){this.logError(`Connect: No Source Property Name: ${s}`);continue}let p=o?c=>({[o]:c}):rt;this.defer((()=>{let c;return a.subscribe(async m=>{try{let h=await m.get(i),g=x(h);if(c!==void 0&&c===g)return;c=g;let f=p(e(this,h));this.up(f)}catch(h){m.logError("Notify ",h)}})})()),Object.assign(this.#n,p(e(a,i?a.get(i):a.$component)))}}raceCondition(t){let e=this.#a??(this.#a=new Map),n=1+(e.get(t)??0);return e.set(t,n),o=>{if(n===e.get(t))return n=0,o()}}log(t,...e){return this.platform.log({level:"log",source:`${this.displayName}:${this.uid}`,message:t,params:e}),t}logError(t,...e){this.platform.log({level:"error",source:`${this.displayName}:${this.uid}`,error:t,message:`${String(t.message||t)}
5
- ${this.toString().slice(0,120)}`,params:e})}toast=t=>{this.platform.toast(t,this)};toString(){return X(this)}};var Ht={"&&":"|and:","==":"|equals:","!=":"|notEquals:","||":"|or:","<":"|less:","[":"|dot:","]":"",">":"|greater:","??":"|coalesce:","?":"|then:"},Ft=(r,t)=>t,mt=r=>"'+-0123456789".includes(r[0])||r==="true"||r==="false";function b(r){let t=y(r[0]==="'"?r.slice(1,r.length-1):r);return()=>t}var lt=r=>t=>t.getFromScope(r),ft=(r,t)=>{if(r[0]==="!"){let e=lt(r.slice(1));return n=>!e(n)}if(r[0]==="#"&&r[1]==="#"){let e=t?.[Number(r.slice(2))];return()=>e}return mt(r)?b(r):lt(r)},ht=r=>r.trim().replace(/(&&|!=|==|>|<|\[|\]|\|\||\?\??)/g,(t,e)=>`${Ht[e]}`).split("|").map(t=>t.trim());function dt(r){if(!r.length)return Ft;let t=r.map(e=>{let n=[],o=e.replaceAll(/'[^']*'/g,a=>(n.push(a.slice(1,-1)),`##${n.length-1}`)),[s,...i]=o.split(":").map(a=>a.trim());return{pipeId:s,args:i.map(a=>ft(a,n))}});return(e,n)=>t.reduce((o,{pipeId:s,args:i})=>{try{let a=e.platform.getFunction(s);if(typeof a!="function")throw new Error(`${a!=null?"must be a function":"not found"}`);if(o instanceof Promise)return o.then(p=>{let c=[p,...i.map(h=>h(e))];return a.apply(e,c)});{let p=[o,...i.map(c=>c(e))];return a.apply(e,p)}}catch(a){return e.logError(`ERROR: Function ${s||"<empty>"}`,a),o}},n)}function Xt(r,t){if(!r||r==="it")return t;if(r.startsWith("it.")){let n=r.slice(3);return(o,s)=>t(o,_(s,n))}let e=mt(r)?b(r):d(r);return n=>t(n,e(n))}function E(r){if(r.endsWith(")")){let s=r.slice(0,-1).split("(");return E(`${s[0]}=${s[1]}`)}let[t,...e]=ht(r),[n,o]=t.split("=").map(s=>s.trim());return{key:n,pipec:Xt(o,dt(e))}}function d(r){let[t,...e]=ht(r),n=dt(e),o=ft(t);return s=>n(s,o(s))}function gt(r){return new Function("c",r.replaceAll(/[^'a-z]([a-z][a-z0-9-.]+)/gi,'c.prop("$1")'))}var kt=/\{([^}]+)\}/g;function yt(r,t){let e=[],n=r.replace(kt,(i,a)=>(e.push(t(a.trim())),"{$$$}"));if(!e.length)return()=>n;let o=n.split(/\{\$\$\$\}/g),s=o.pop();return(...i)=>o.map((a,p)=>`${a}${tt(e[p](...i))}`).join("")+s}var k=new Map;function Ut(r){if(typeof r!="string")return()=>r;if(r.startsWith("js:"))return gt(r.slice(3));if(r.includes("{")){let t=r.slice(1,-1);return r[0]==="{"&&r[r.length-1]==="}"&&!t.includes("{")?d(t):yt(r.replace(/\s+/g," "),d)}return b(r)}var C=r=>k.has(r)?k.get(r):k.set(r,Ut(r)).get(r);var l=class{$content;uid;refId;propertyResolvers=[];initialState={};connectors;tag;constructor(t){this.uid=`${et("N")}:${t}`}createArrmatron(t,e,n){return new this.EntitronConstructor(t,this,e,n)}getSubNodes(t){return this.$content}addPropertyResolver(t,e){return this.propertyResolvers.push((n,o)=>(o[e]=t(n),o)),this}addDataPropertyResolver(t,e){return this.propertyResolvers.push((n,o)=>{let s=t(n);return o.data=Object.assign(o.data||{},{[e]:s}),o}),this}addPropertiesResolver(t){return this.propertyResolvers.push((e,n)=>{let o=t(e);return o&&typeof o=="object"&&Object.entries(o).forEach(([s,i])=>{n[s]=i}),n}),this}addConnector(t,e){let{key:n,pipec:o}=E(t);(this.connectors??(this.connectors=new Map)).set(`${n}|${e}`,o)}addEmitter(t,e){let{key:n,pipec:o}=E(t),s=n+(t.endsWith(")")?"()":"");this.initialState[e]=i=>a=>i.scope?.emit(s,o(i,a))}resolveInitialProps(t){return this.propertyResolvers.reduce((e,n)=>n(t,e),Object.entries(this.initialState).reduce((e,[n,o])=>(e[n]=o(t),e),{}))}resolveProps(t){return this.propertyResolvers.reduce((e,n)=>n(t,e),{})}compileAttribute(t,e){if(t.startsWith("data-"))this.addDataPropertyResolver(C(e),t.slice(5));else if(t==="Ref")this.refId=String(e);else if(t==="Props"){let n=String(e);n.startsWith("<-")?this.addConnector(n.slice(2),""):this.addPropertiesResolver(C(n))}else if(typeof e!="string")this.initialState[t]=()=>e;else if(e.includes("{"))this.addPropertyResolver(C(e),t);else if(e.startsWith("<-"))if(e.endsWith(".This")){let n=e.slice(2,-5).trim();this.initialState[t]=o=>o.getByRef(n)?.$component}else this.addConnector(e.slice(2),t);else e.startsWith("->")?this.addEmitter(e.slice(2).trim()||"*",t):this.initialState[t]=()=>e}compileAttributes(t){t&&Object.entries(t).forEach(e=>this.compileAttribute(...e))}};var U=class extends u{get displayName(){return"ROOT"}get recontentScope(){return this}slotContent(t){}createComponent(t){return this.platform.createComponent({},t,this)}},$=class extends l{constructor(e){super("R0");this.template=e;this.slotContent={}}slotContent;nodes;getSubNodes(e){return e.getCompiledNodes(it(this.template))}getSlotContent(e,n){}get EntitronConstructor(){return U}};var xt=r=>r?typeof r=="string"?r.split(",").map(t=>({id:t,name:t})):Array.isArray(r)?r:typeof r[Symbol.iterator]=="function"?[...r]:typeof r=="object"?nt(r,(t,e)=>typeof e=="object"?{...e,id:t}:{id:t,name:String(e)}):[{id:String(r),name:String(r)}]:[];var v=class extends u{pkHash={};get displayName(){return"\u{1F539}each"}get contentManifests(){let t=xt(this.$component.each),e=this.manifest.getItemCtxNode(this.platform),n=new Map;if(this.pkHash={},t?.length){if(!t.forEach)throw new Error(`[each] Items has no forEach() ${t.toString()}`);t.forEach((o,s)=>{let i=typeof o=="string"?{id:o}:o,a=i.id;a==null&&(this.logError("ERROR: empty item id: ",i),a=String(s));let p=String(a);if(this.pkHash[p]){this.logError(`ERROR: duplicate item id : ${p} (skipped)`,i);return}this.pkHash[p]=i;let c=e.cloneWithDatum(i,this.platform).addPropertyResolver(()=>this.pkHash[p],e.itemName);n.set(c.uid,c)})}return n}},A=class extends l{#t;#e;#r;constructor(t,[e,n,o=n]){super(t.id),this.#r=t,this.#e=e.startsWith("@")?e.slice(1):e,o[0]==="<"&&o[1]==="-"?this.addConnector(o.slice(2),"each"):this.addPropertyResolver(d(o),"each")}getItemCtxNode(t){return this.#t??(this.#t=new z(this.#r.id,this.#e,t.getCompiledNodes([this.#r])))}get EntitronConstructor(){return v}},L=class extends u{get recontentScope(){return this}get displayName(){return"\u{1F539}ui:for:item"}emit(t,e){return this.scope.emit(t,e)}get(t){let e=this.manifest.itemName;return t.startsWith(`${e}.`)||t===e?super.get(t):this.scope.get(t)}},z=class r extends l{itemName;constructor(t,e,n){super(t),this.itemName=e,this.$content=n}cloneWithDatum(t,e){let n=`${String(this.uid)}#${t.id}`,o=new r(n,this.itemName,this.getSubNodes(e));return o.uid=n,o}get EntitronConstructor(){return L}};var B=class extends u{get displayName(){return`\u{1F4A0}${this.manifest.tag}`}get recontentScope(){return this}slotContent(t){return this.manifest.getSlotContent(this.platform,t)}createComponent(t){return this.platform.createComponent(this.manifest.tag,t,this)}},T=class extends l{slotContent=null;tag;nodes;#t;constructor({id:t,tag:e,attrs:n,nodes:o}){super(t),this.tag=e,this.nodes=o,this.compileAttributes(n)}getSlotContent(t,e){if(!this.slotContent){let n=this.nodes?.reduce((o,s)=>{let i="default",a=!1;return s.tag.startsWith(`${this.tag}:`)&&(i=s.tag.split(":")[1],a=!0),o[i]=(o[i]||[]).concat(a?s.nodes??[]:s),o},{});this.slotContent=Object.entries(n??{}).reduce((o,[s,i])=>(o[s]=t.getCompiledNodes(i),o),{})}return this.slotContent[e||"default"]??void 0}getSubNodes(t){return this.#t??(this.#t=t.getCompiledNodes(this.tag))}get EntitronConstructor(){return B}};var W=class extends u{get contentManifests(){let t=this.manifest.getBranches(this.platform);return this.$component.condition?t.then:t.else}get displayName(){return"\u{1F539}if"}},w=class extends l{#t;#e;constructor(t,e){super(t.id),this.#t=t,this.compileCondition(e)}get EntitronConstructor(){return W}getBranches(t){if(this.#e)return this.#e;this.#e={};let{nodes:e}=this.#t,n=[this.#t];if(e?.length){let o=e.find(i=>i.tag==="Else"),s=e.find(i=>i.tag==="Then");o?(this.#e.else=o.nodes?t.getCompiledNodes(o.nodes):void 0,n=s?s.nodes:[Object.assign(this.#t,{nodes:e.filter(i=>i!==o)})]):s&&(n=s.nodes)}return this.#e.then=n?t.getCompiledNodes(n):void 0,this.#e}compileCondition(t){if(t[0]==="<"&&t[1]==="-")this.addConnector(t.slice(2),"condition");else if(t.slice(0,5)==="slot("){let e=t.slice(5,-1);this.addPropertyResolver(n=>!!n.scope.slotContent?.(e)?.size,"condition")}else{let e=d(t);this.addPropertyResolver(n=>!!e(n),"condition")}}};var J=class extends u{prevkey;touch(){super.touch();let{trigger:t,data:e,change:n}=this.$component,o="trigger"in this.$component;if(o&&t==null)return;let i=x(o?t:e);(!("prevkey"in this)||this.prevkey!=i)&&(this.prevkey=i,n?.(e))}get displayName(){return"\u{1F539}Connector"}},M=class extends l{constructor({attrs:t,id:e}){super(e),this.compileAttributes(t)}get EntitronConstructor(){return J}};var G=class extends u{get displayName(){return this.manifest.tag}createComponent(t){return this.platform.createComponent({tag:this.manifest.tag,native:!0},t,this)}},I=class extends l{#t;constructor({id:t,tag:e,attrs:n,nodes:o,text:s}){super(t),this.tag=e,this.compileAttributes(n),s&&this.compileAttribute("#text",s),this.#t=o}getSubNodes(t){return this.#t?t.getCompiledNodes(this.#t):void 0}get EntitronConstructor(){return G}};var P=class extends l{#t;constructor({id:t,nodes:e}){super(t),this.#t=e}getSubNodes(t){return this.#t?t.getCompiledNodes(this.#t):void 0}get EntitronConstructor(){return u}};var Y=class extends u{get displayName(){return"dynamics"}get contentManifests(){let t=this.$component.tag,e=`${this.uid}:${String(t??"")}`;return new Map([[e,Object.assign(this.platform.getCompiledNodes({...this.manifest.xml,tag:t}),{uid:e})]])}},O=class extends l{xml;constructor(t,e){super(t.id),this.compileAttribute("tag",e),this.xml=t}get EntitronConstructor(){return Y}};var Z=class extends u{get displayName(){return"\u{1F4A0}Selector"}get contentManifests(){let t=this.manifest.getCases(this.platform),e=this.$component.On,n=t?.[e];return n||this.log("unmatched case",e,t),n??t?.default??Object.values(t)[0]}},j=class extends l{cases=null;key="";nodes;constructor({id:t,tag:e,attrs:n,nodes:o}){super(t),this.tag=e,this.nodes=o,this.compileAttributes(n)}getCases(t){return this.cases??(this.cases=(this.nodes??[]).reduce((e,n)=>(e[n.attrs?.When]=t.getCompiledNodes(n.nodes??[]),e),{}))}get EntitronConstructor(){return Z}};var q=class extends u{get displayName(){return"\u{1F538}"}get compositeScope(){return this.scope}get contentManifests(){return this.compositeScope.slotContent(this.manifest.key)}get recontentScope(){return this.compositeScope.scope}},D=class extends l{key;constructor(t){super(t.id),this.key=t.attrs?.Key}get EntitronConstructor(){return q}};var K=(r,t)=>{let{[t]:e,...n}=r.attrs??{};return{text:r.text,tag:r.tag,id:r.id,attrs:n,nodes:r.nodes?.map(o=>o)}},vt=r=>{let{tag:t="div",attrs:e}=r;return e?.Each?new A(K(r,"Each"),String(e.Each).trim().split(" ")):e?.If?new w(K(r,"If"),String(e.If)):t==="div"||t[0]?.match(/[A-Z0-9]/)==null?new I(r):t==="Dynamic"?new O(K(r,"As"),String(e?.As??"Error.Dynamic")):t==="Slot"?new D(r):t==="Selector"?new j(r):t==="Connector"?new M(r):t==="Fragment"||t==="Then"||t==="Else"?new P(r):new T(r)},Q=new Map,Lt=r=>Q.has(r)?Q.get(r):Q.set(r,vt(r)).get(r),oo=r=>new Map(r?.map(Lt).map(t=>[t.uid,t])??[]);var $t=class{$ctx;constructor(t,e){this.$ctx=e,this.__created(t)}__created(t){}get refId(){return this.$ctx.refId}get platform(){return this.$ctx.platform}__init(t){}get(t){return this.$ctx.get(t)}up(t){return this.$ctx.up(t)}touch(){return this.$ctx.touch()}emit(t,e={}){return this.$ctx.emit(t,e)}defer(t){this.$ctx.defer(t)}defineCalculatedProperty(t,e,n){let o="",s;Object.defineProperty(this,t,{get(){let i=n?.length?n.map(p=>this.$ctx.get(p)):[],a=i.join(":");return o!==a&&(o=a,s=e.apply(this,i)),s}})}toast(t){this.$ctx.toast(t)}log(t,...e){this.$ctx.log(t,...e)}logError(t,...e){this.$ctx.logError(t,...e)}toString(){return`${this.$ctx.toString()}${this.name?`(${this.name})`:""}`}};var po=(r,t)=>{let e=new $(t).createArrmatron(r);return e.touch(),e};export{$ as CRootNode,$t as Component,Lt as compileManifestNode,oo as compileManifestNodes,po as launch};
6
- //# sourceMappingURL=index.js.map
1
+ "use strict";var K=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var oe=Object.prototype.hasOwnProperty;var i=(n,t)=>K(n,"name",{value:t,configurable:!0});var Tt=(n,t)=>{for(var e in t)K(n,e,{get:t[e],enumerable:!0})},ie=(n,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of se(t))!oe.call(n,s)&&s!==e&&K(n,s,{get:()=>t[s],enumerable:!(r=re(t,s))||r.enumerable});return n};var ae=n=>ie(K({},"__esModule",{value:!0}),n);var Ze={};Tt(Ze,{CRootNode:()=>$,Component:()=>A,Platform:()=>z,applyTemplate:()=>Ye,compileManifestNode:()=>V,compileManifestNodes:()=>tt,createRegisterTypes:()=>Rt,launch:()=>We});module.exports=ae(Ze);var pt={};Tt(pt,{HASH_SYMBOL:()=>y,asyncValueCall:()=>_,capitalize:()=>q,defineObjectHashCodeProperty:()=>P,defineObjectRef:()=>st,hashCodeOf:()=>C,isEquals:()=>w,mapEntries:()=>x,mergeObject:()=>b,scalarParse:()=>O,stringHashCode:()=>h,xmlParse:()=>at});function _(n,t){return n instanceof Promise?n.then(t):t(n)}i(_,"asyncValueCall");var pe=i(n=>n==null?"":String(n),"str");function q(n){let t=pe(n);return t.length?t[0].toUpperCase()+t.slice(1):""}i(q,"capitalize");function st(n,t,e){return Object.defineProperty(n,t,{get(){return e},enumerable:!1,configurable:!1}),n}i(st,"defineObjectRef");var E=i(()=>Math.floor((1+Math.random())*65536).toString(16).substring(1),"s4"),ce=i(()=>`${E()}${E()}-${E()}-${E()}-${E()}-${E()}${E()}${E()}`,"guid"),y=Symbol("HASH_SYMBOL"),bt=new Map([["",ot("__empty__")]]),Ot=new Map([[void 0,h("__undefined__")],[null,h("__null__")],["",h("__empty__")],[NaN,h("__NaN__")],[!1,h("__false__")],[!0,h("__true__")]]),I={Object:h("__Object__"),Date:h("__Date__"),Array:h("__Array__"),Map:h("__Map__"),Set:h("__Set__")};function ot(n){let t=-2128831035;for(let e=0;e<n.length;e++)t^=n.charCodeAt(e),t=Math.imul(t,16777619);return t|0}i(ot,"__stringHashCode");function g(n,t){return Math.imul(t^n,16777619)|0}i(g,"numberHashCode");function h(n){let t=bt.get(n);if(t)return t;let e=ot(n);return bt.set(n,e),e}i(h,"stringHashCode");function P(n,t){if(!n||Object.hasOwn(n,y))return n;let e=t??ot(ce());return Object.defineProperty(n,y,{get(){return e},configurable:!1,enumerable:!1}),n}i(P,"defineObjectHashCodeProperty");function fe(n,t){if(t>10)return-1;let e=t+1,r=0;if(n instanceof Map){r=g(I.Map,r);for(let[s,o]of n)r=g(h(s),r),r=g(I.Map,r),r=g(C(o,e),r)}else if(n instanceof Set){r=g(I.Set,r);for(let s of n)r=g(C(s,e),r)}else if(Array.isArray(n)){r=g(I.Array,r);for(let s of n)r=g(C(s,e),r)}else{r=g(I.Object,r);for(let s in n)Object.hasOwn(n,s)&&s[0]!=="$"&&(r=g(h(s),r),r=g(I.Object,r),r=g(C(n[s],e),r))}return r}i(fe,"containerHashCode");var C=i((n,t=0)=>Ot.has(n)?Ot.get(n):typeof n=="string"?h(n):typeof n=="number"?n:typeof n=="bigint"?h(n.toString(16)):typeof n=="symbol"?h(n.description??n.toString()):typeof n=="function"?P(n)[y]:y in n?n[y]:n instanceof Date?g(n.getTime(),I.Date):n instanceof Promise?P(n)[y]:fe(n,t),"hashCodeOf");function w(n,t,e=0){if(Number.isNaN(n))return!1;if(n===null||typeof n!="object")return n===t;if(t===null||typeof t!="object")return!1;if(y in n&&y in t)return n[y]===t[y];if(e>10)return!0;let r=e+1;if(Array.isArray(n)){if(!Array.isArray(t)||n.length!==t.length)return!1;for(let o=0;o<n.length;o++)if(!w(n[o],t[o],r))return!1;return!0}if(n instanceof Map){if(!(t instanceof Map)||n.size!==t.size)return!1;for(let[o,a]of n){let p=t.get(o);if(p===void 0&&!t.has(o)||!w(a,p,r))return!1}return!0}if(n instanceof Set){if(!(t instanceof Set)||n.size!==t.size)return!1;for(let o of n)if(!t.has(o))return!1;return!0}if(Array.isArray(t)||t instanceof Map||t instanceof Set)return!1;if(n instanceof Date||t instanceof Date)return n instanceof Date&&t instanceof Date&&n.getTime()===t.getTime();let s=0;for(let o in n)if(!(o[0]==="$"||!Object.hasOwn(n,o))&&(s++,!Object.hasOwn(t,o)||!w(n[o],t[o],r)))return!1;for(let o in t)if(o[0]!=="$"&&Object.hasOwn(t,o)&&--s<0)return!1;return s===0}i(w,"isEquals");var le=i((n,t)=>({id:n,value:t}),"DEFAULT_ENTRY_HANDLER"),x=i((n,t=le)=>n?Object.entries(n).map(([e,r])=>t(e,r)):[],"mapEntries");var b=i((n,t)=>{if(!n||typeof n!="object")return t;if(!t||typeof t!="object")return n;for(let[e,r]of Object.entries(t))typeof r=="object"&&typeof n[e]=="object"?Array.isArray(n[e])?n[e]=r:n[e]=b(n[e],r):n[e]=r;return n},"mergeObject");var O=i(n=>{if(n==="")return"";if(n==="true")return!0;if(n==="false")return!1;if(n==="0")return 0;if(n==="1")return 1;if(n&&"1234567890+-".includes(n[0])&&n.length<=17){let t=+n;return Number.isNaN(t)?n:t}if(n==="null")return null;if(n!=="undefined")return n},"scalarParse");var ue=/&#?[0-9a-zA-Z]{2,5};/g,me={amp:"&",gt:">",lt:"<",quot:'"',nbsp:" "},he=i(n=>{let t=n.slice(1,-1);if(t[0]!=="#")return me[t]||" ";let e=t.slice(1),r=e[0]==="x"||e[0]==="X"?parseInt(e.slice(1),16):parseInt(e,10);return Number.isFinite(r)?String.fromCharCode(r):" "},"FN_XML_ENTITY"),kt=i(n=>String(n??"").replaceAll(ue,he),"decodeXmlEntities"),de=i((n,t='"',e=t)=>n[0]===t&&n[n.length-1]===e?n.slice(1,-1):n,"skipQuotes"),it=/([a-zA-Z][a-zA-Z0-9-:$]*)(="[^"]*")?/g,Dt=/^\s*$/,ge=/<!--((?!-->)[\s\S])*-->/g,G=/(<)(\/?)([a-z][a-z0-9\-_:.]*)((?:\s+[a-zA-Z][a-z0-9-:$]*(?:="[^"]*")?)*)\s*(\/?)>/gi,ye={img:1,input:1,br:1,hr:1,col:1,source:1},Ce="Text",Xt=i((n,t,e)=>{let r={tag:t,id:e};return(n.nodes??=[]).push(r),r},"addNode"),_e=i((n,t)=>{if(t){n.attrs={},it.lastIndex=0;for(let e=it.exec(t);e;e=it.exec(t)){let r=e[2]?O(kt(de(e[2].slice(1)))):!0;n.attrs[e[1]]=r}}},"parseAttrs"),at=i(n=>{let t=0,e=[{tag:"#root",id:String(t)}],r=String(n).trim().replace(ge,""),s=0;G.lastIndex=0;for(let o=G.exec(r);o;o=G.exec(r)){let a=o.index&&r.slice(s,o.index);if(o[2]){if(a&&!e[0].nodes?.length&&(a===" "||!a.match(Dt))&&(e[0].text=kt(a.trim())),e[0].tag!==o[3])throw new Error(` XML Parse closing tag does not match at: ${String(o.index)} near ${o.input.slice(Math.max(o.index-150,0),o.index)}^^^^${o.input.slice(o.index,Math.min(o.index+150,o.input.length))}`);e.shift()}else{if(a&&!a.match(Dt)){let c=Xt(e[0],Ce,`T${String(++t)}`);c.attrs={text:a}}let p=Xt(e[0],o[3],`X${String(++t)}`);if(_e(p,o[4].trim()),!(o[5]||o[3]in ye)&&(e.unshift(p),e.length===1))throw new Error(`Parse error at: ${o[0]}`)}s=G.lastIndex}return e[0].nodes??[]},"xmlParse");function Ht(n,t){t.__stateChanged?t.__stateChanged(n):n.forEach((e,r)=>{t[r]=e})}i(Ht,"applyStateChangedToImpl");var jt=i(n=>typeof n=="object"?Array.isArray(n)?n.length?`[${jt(n[0])??""} x${String(n.length)}]`:"[]":`{${Object.keys(n).toString()}}`:n??"","attributesToString"),Ne=i(n=>{if(!n)return"";let t=[];return Object.entries(n).forEach(([e,r])=>{r!=null&&e[0]!=="$"&&t.push(` ${e}="${jt(r)}"`)}),t.length?t.join(""):""},"stringifyState"),ct=i((n,t="")=>{let{uid:e,displayName:r=e,$component:s,children:o}=n,a=o?.size?[...o.values()].map(p=>ct(p,` ${t}`)).join(`
2
+ `):"";return`${t}<${r}${Ne(s)}${a?`>
3
+ ${a}
4
+ ${t}</${r}>`:"/>"}`},"stringify");function Ft(n,t,e,r){let s,o,a=0;Object.defineProperty(n,t,{get(){let p=r?.length?r.map(l=>n.get(l)):[],c=p.some(l=>l instanceof Promise),f=i(l=>{let N=l.map(d=>C(d));return(!s||N.some((d,T)=>d!==s[T]))&&(s=N,o=e.apply(n,l)),o},"functor");return c?(a++,new Promise((l,N)=>{Promise.all(p.map(d=>d instanceof Promise?d:Promise.resolve(d))).then(f).then((d=>T=>{d===a&&l(T)})(a)).catch(d=>{N(d)})})):f(p)}})}i(Ft,"defineCalculatedProperty");var Y="$$$ARRMATURA";var A=class{static{i(this,"Component")}name;get $$(){return this[Y]}get $uid(){return this.$$.uid}get $parentComponent(){return this.$$.parent?.$component}get refId(){return this.$$.refId}get platform(){return this.$$.platform}__init(){}get(t){return this.$$.get(t)}up(t){return this.$$.up(t)}touch(){return this.$$.touch()}getComponentByRef(t){return this.$$.getByRef(t)?.$component}emit(t,e={}){return this.$$.emit(t,e)}subscribeTo(t,e){return this.$$.subscribeTo(t,e)}defer(t){this.$$.defer(t)}__defineCalculatedProperty(t,e,r){Ft(this,t,e,r)}__defineCalculatedProperties(t){Object.entries(t).forEach(([e,r])=>{let[s,o]=e.split("("),a=o?.slice(0,-1).split(",").map(p=>p.trim());this.__defineCalculatedProperty(s,r,a)})}log(t,...e){return this.$$.log(t,...e),t}logError(t,...e){return this.$$.logError(t,...e),t}toString(){return`${this.$$.toString()}${this.name?`(${this.name})`:""}`}};var D="outer$",vt=new Map,Ae=[],ft=0,u=class{constructor(t,e,r,s){this.platform=t;this.manifest=e;this.__parent=r;P(this),this.scope=s??this,this.root=s?.root??this;let o=this.createComponent();P(o),st(o,Y,this),this.$component=o,this.refId&&this.scope.addReference(this.refId,this)}platform;manifest;__parent;static{i(this,"Arrmatron")}$component;root;scope;__children;__propCache;__localScope;__isDone=!1;__isInited=!1;__fprints={};__listeners;__defered=[];__propFnMap;#t;#e=10;#n;get parent(){return this.__parent}get uid(){return this.manifest.uid}get hashCode(){return C(this)}get refId(){return this.manifest.refId}get displayName(){return`\u{1F539}${this.manifest.tag}`}createComponent(){return new A}up(t,e=!1){if(!t||this.__isDone)return;if(this.#n){Object.assign(this.#n,t);return}if(t instanceof Promise){this.platform.applyAsyncValue(`${this.hashCode}:set`,t,s=>this.up(s));return}let r=new Map;Object.entries(t).forEach(([s,o])=>{if(o instanceof Promise){let a=!s||s.startsWith("...");this.platform.applyAsyncValue(`${this.hashCode}:set:${s}`,o,p=>{this.up(a?p:{[s]:p})});return}!s||s[0]==="$"||(!(s in this.__fprints)||!w(this.__fprints[s],o))&&(this.__fprints[s]=o,r.set(s,o),this.__propCache?.delete(s))}),(r.size||e)&&(Ht(r,this.$component),this.#e&&(this.#e--,this.touch(),this.#e++))}get(t){if(!t)return null;let e,r;if(t.indexOf(".")===-1)e=t,r=Ae;else{let l=vt.get(t);l||(l=t.split("."),vt.set(t,l)),[e,...r]=l}let s=r.length===0;if(s&&this.__propCache?.has(t))return this.__propCache.get(t);let o=this.__propFnMap??=new Map;if(o.has(t)){let l=o.get(t)();return!s&&l===null&&this.log("ERROR: Nested key",t),s&&(this.__propCache??=new Map).set(t,l),l}let a=null,p=this.$component,c=p[t];if(c&&typeof c=="function"){let l=c.bind(p);a=i(()=>l,"fn")}else if(t==="This")a=i(()=>this.$component,"fn");else if(e==="R"){let l=this.platform.getResource(r)??null;a=i(()=>l,"fn")}else if(`get${q(e)}`in p){let l=p[`get${q(e)}`],N=typeof l=="function"?()=>l.call(p)??null:()=>p[e]??null;a=r.length?()=>r.reduce((d,T)=>d?.[T]??null,N()):N}else if(p.__getStateProperty)a=i(()=>p.__getStateProperty?.(t),"fn");else{let l=i(()=>p[e]??null,"fn0");a=r.length?()=>r.reduce((N,d)=>N?.[d]??null,l()):l}o.set(t,a);let f=a();return!s&&f===null&&this.log("ERROR: Nested key",t),s&&(this.__propCache??(this.__propCache=new Map)).set(t,f),f}putInLocalScope(t,e){(this.__localScope??(this.__localScope=new Map)).set(t,e)}getFromScope(t){return t.startsWith(D)?this.__localScope?.get(t):this.scope.get(t)}notify(){this.__listeners?.forEach(t=>{try{t(this)}catch(e){this.logError("Notification:",e)}})}addListener(t){this.__listeners??=new Set;let e=this.__listeners;return e.add(t),()=>{e.delete(t)}}subscribeTo(t,e){let r=typeof t=="string"?this.getByRef(t):t;if(!r)throw new Error(`No such reference: ${t}`);this.defer(r.addListener(e))}emit(t,...e){if(!this.__isDone)try{if(t.endsWith(")")){let[r,s]=t.split("(")[0].split(".");s||(s=r,r="This");let o=this.getByRef(r);if(!o)throw new Error(`No such reference: ${r}`);let a=o.$component,p=a[s];if(typeof p!="function")throw new Error(`Not a method: ${r}.${s}()`);return e.length>1?p.apply(a,e):this.platform.applyAsyncValue(`${this.hashCode}:emit:${t}`,e[0],c=>p.call(a,c))}else{let r=e[0],[s,o]=t.split(".");o||(o=s||"*",s="this");let a=this.getByRef(s);if(!a)throw new Error(`No such reference: ${s}`);return a.up(o==="*"?r:{[o]:r})}}catch(r){this.logError(`emit ${t}:`,r)}}touch(){ft++;try{this.__propCache?.clear();let t=this.__isDone?null:this.contentManifests;if(t?.size||this.__children?.size)if(this.__children?.forEach(e=>{t?.has(e.uid)||e.done()}),t?.size){let e=this.__children;this.__children=new Map,t.forEach((r,s)=>{let o=e?.get(s),a=o&&!o?.__isDone?o:r.createArrmatron(this.platform,this,this.scopeForChildren);this.__children?.set(a.uid,a)});for(let r of this.__children.values())r.settle()}else this.__children=this.__children?.size===0?this.__children:new Map;this.__isDone?(this.__defered?.forEach(e=>e(this)),this.__defered=void 0,this.__parent=void 0,this.refId&&this.scope.#t&&delete this.scope.#t[this.refId],this.__listeners=void 0,this.__propFnMap=void 0,this.__propCache=void 0,this.__localScope=void 0):this.notify()}finally{ft--,ft||this.platform.redraw(this,this.root)}}done(){this.__isDone=!0,this.touch()}settle(){if(!this.__isDone)if(this.__isInited){let t=this.manifest.resolveProps(this);this.up(t,!!this.children?.size)}else{if(this.__isInited=!0,this.#n=this.manifest.resolveInitialProps(this),this.manifest.connectors)for(let[e,r]of this.manifest.connectors.entries()){let[s,o]=e.split("."),a=this.parent?.getByRef(s);if(!a){this.logError(`Connect: No such ref: ${s}`);continue}this.defer(a.addListener(()=>{this.platform.applyAsyncValue(`${this.hashCode}:connect:${e}`,a.get(o),p=>r(this,p))})),this.platform.applyAsyncValue(`${this.hashCode}:connect:${e}`,a.get(o),p=>r(this,p))}let t=this.#n;this.#n=null,this.up(t,!0),this.$component.__init?.()}}defer(t){t&&typeof t=="function"&&(this.__defered??=[],this.__defered.push(t))}get children(){return this.__children}get contentManifests(){return this.manifest.getSubNodes(this.platform)}get scopeForChildren(){return this.scope}getByRef(t){if(!t)return;if(t==="this"||t==="This")return this;let e=this.#t?.[t];return e||this.parent?.getByRef(t)}addReference(t,e){this.#t??={},this.#t[t]=e}log(t,...e){return this.platform.log({level:"log",source:`${this.displayName}:${this.uid}`,message:t,params:e}),t}logError(t,...e){return this.platform.log({level:"error",source:`${this.displayName}:${this.uid}`,error:t,message:`${String(t.message||t)}`,params:e}),t}toString(){return ct(this)}};function lt(n,t){let e=[],r=0,s=!1,o="",a=0;for(let c=0;c<n.length;c++){let f=n[c];s?f===o&&(s=!1):f==='"'||f==="'"?(s=!0,o=f):f==="("||f==="["?r++:f===")"||f==="]"?r--:f===t&&r===0&&(e.push(n.slice(a,c).trim()),a=c+1)}let p=n.slice(a).trim();return p&&e.push(p),e}i(lt,"splitTopLevel");var Kt=i(()=>!1,"fnFalse"),qt=i(()=>!0,"fnTrue"),Gt=i(()=>null,"fnNull"),Lt=i((n,t)=>n&&t,"fnAnd"),Ut=i((n,t)=>n||t,"fnOr"),Ee=i((n,t)=>n+t,"fnPlus"),Yt=i(()=>{},"fnUndefined"),$e=i(n=>!n,"fnNot"),Ie=i((n,t)=>n===t,"fnEqual"),xe=i((n,t)=>n!==t,"fnNotEqual"),Bt=i((n,t)=>n<t,"fnLessThan"),Pe=i((n,t)=>n<=t,"fnLessThanOrEqual"),Wt=i((n,t)=>n>t,"fnGreaterThan"),zt=i((n,t)=>n>=t,"fnGreaterThanOrEqual"),we=i((n,t)=>n-t,"fnMinus"),Se=i((n,t)=>n*t,"fnMultiply"),Me=i((n,t)=>n/t,"fnDivide"),Re=i((n,t)=>n%t,"fnModulo"),Te=i((n,t)=>n**t,"fnExponent"),be=i((n,t)=>n===t,"fnStrictEqual"),Oe=i((n,t)=>n??t,"fnCoalesce");var Zt={"&&":Lt,AND:Lt,"||":Ut,OR:Ut,"!":$e,"==":Ie,"===":be,"!=":xe,"<":Bt,LT:Bt,">":Wt,GT:Wt,GTE:zt,"<=":Pe,">=":zt,"+":Ee,"-":we,"*":Se,"/":Me,"%":Re,"**":Te,"??":Oe};var ut=/[a-zA-Z0-9_$-]/,Z=class{constructor(t,e){this.ctx=e;this.input=t.trim(),this.length=this.input.length}ctx;static{i(this,"ExpressionParserBase")}input;position=0;length=0;checkIf(t){return this.input.startsWith(t,this.position)}checkIfNum(){return/[0-9+-]/.test(this.input[this.position])}checkExceeded(){this.position!==this.length&&this.throw("Unexpected end of expression")}consumeIf(t){return this.checkIf(t)?(this.position+=t.length,!0):!1}consumeKeyword(t){if(!this.checkIf(t))return!1;let e=this.input[this.position+t.length];return e!==void 0&&ut.test(e)?!1:this.consumeIf(t)}throw(t){throw new Error(`${t} in '${this.input.slice(0,this.position)}<<<<${this.input.slice(this.position)}'`)}consume(t){return this.consumeIf(t)||this.throw(`Expected expression '${t}' at position ${this.position}`),!0}consumeWhitespace(){for(;this.position<this.length&&/\s/.test(this.input[this.position]);)this.position++}parseLeftRight(t,e,r){let s=this[e]();for(let o=0;o<t.length;o++){let a=t[o];if(this.consumeWhitespace(),this.consumeIf(a)){this.consumeWhitespace();let p=this[r](),c=Zt[a];return{type:a,left:s,right:p,fn:i((f,l)=>c(s.fn(f,l),p.fn(f,l)),"fn")}}}return s}parseString(){let t=this.input[this.position],e="";for(this.position++;this.position<this.input.length&&this.input[this.position]!==t;)e+=this.input[this.position],this.position++;return this.input[this.position]===t&&this.position++,{type:"String",value:e,fn:i(()=>e,"fn")}}parseNumber(){let t="";for(this.position<this.input.length&&/[0-9.+-]/.test(this.input[this.position])&&(t+=this.input[this.position],this.position++);this.position<this.input.length&&/[0-9.]/.test(this.input[this.position]);)t+=this.input[this.position],this.position++;let e=parseFloat(t);return{type:"Number",value:e,fn:i(()=>e,"fn")}}parseIdentifier(){let t="";for(;this.position<this.input.length&&ut.test(this.input[this.position]);)t+=this.input[this.position],this.position++;if(t==="it"||t==="event")return{type:"Event",name:t,fn:i((e,r)=>r,"fn")};if(t==="R"&&this.input[this.position]==="."){for(;this.position<this.input.length&&(this.input[this.position]==="."||ut.test(this.input[this.position]));)t+=this.input[this.position],this.position++;return{type:"Resource",name:t,fn:i(e=>this.ctx.resolveIdentifier(e,t),"fn")}}return this.ctx.propNames.add(t),{type:"Identifier",name:t,fn:i(e=>this.ctx.resolveIdentifier(e,t),"fn")}}};var S=class extends Z{static{i(this,"ExpressionParser")}parse(){let t=this.parsePipesExpression(this.parseExpression());return this.checkExceeded(),t}parsePipesExpression(t){if(this.consumeIf("|")){this.consumeWhitespace();let e=this.parseIdentifier().name;this.consumeWhitespace();let r=[];for(;this.consumeIf(":");)this.consumeWhitespace(),r.push(this.parseExpression());return this.parsePipesExpression({type:"Pipe",args:r,pipeId:e,from:t,fn:i((s,o)=>{let a=this.ctx.getFunction(s,e);a||s.logError(`Undefined pipe function:${e}`);let p=t.fn(s,o);return _(p,c=>a?.call(s,c,...r.map(f=>f.fn(s,o))))},"fn")})}return t}parseExpression(){let t=this.parseTernary();return this.consumeWhitespace(),t}parseTernary(){let t=this.parseCoalesce();if(this.consumeIf("?")){this.consumeWhitespace();let e=this.parseExpression(),r;return this.consumeIf(":")&&(this.consumeWhitespace(),r=this.parseExpression()),{type:"Ternary",condition:t,trueExpr:e,falseExpr:r,fn:i((s,o)=>t.fn(s,o)?e.fn(s,o):r?r.fn(s,o):void 0,"fn")}}return t}parseCoalesce(){return this.parseLeftRight(["??"],"parseOr","parseCoalesce")}parseOr(){return this.parseLeftRight(["||","OR"],"parseAnd","parseOr")}parseAnd(){return this.parseLeftRight(["&&","AND"],"parseCompare","parseAnd")}parseCompare(){return this.parseLeftRight(["===","==","!="],"parseEquality","parseEquality")}parseEquality(){return this.parseLeftRight([">=",">","GTE","GT","<=","<","LT"],"parsePlus","parsePlus")}parsePlus(){return this.parseLeftRight(["+","-"],"parseMulti","parsePlus")}parseMulti(){return this.parseLeftRight(["*","/","%"],"parseUnary","parseMulti")}parseUnary(){if(this.consumeIf("!")){let t=this.parseUnary();return{type:"Not",expr:t,fn:i((e,r)=>!t.fn(e,r),"fn")}}return this.parsePrimary()}parsePrimary(){if(this.consumeIf("(")){this.consumeWhitespace();let t=this.parseExpression();return this.consume(")"),this.parsePropertyAccess(t)}return this.consumeKeyword("true")?{type:"Boolean",value:!0,fn:qt}:this.consumeKeyword("false")?{type:"Boolean",value:!1,fn:Kt}:this.consumeKeyword("null")?{type:"Null",fn:Gt}:this.consumeKeyword("undefined")?{type:"Undefined",fn:Yt}:this.checkIf("'")||this.checkIf('"')?this.parseString():this.checkIfNum()?this.parseNumber():this.parsePropertyAccess(this.parseIdentifier())}parsePropertyAccess(t){if(this.consumeWhitespace(),this.consumeIf(".")){let e=this.parseIdentifier();return this.parsePropertyAccess({type:"PropertyAccess",object:t,property:e.name,fn:i((r,s)=>t.fn(r,s)?.[e.name],"fn")})}if(this.consumeIf("(")){this.consumeWhitespace();let e=t.name,r=[];if(!this.consumeIf(")")){for(r.push(this.parseExpression());this.consumeIf(",");)this.consumeWhitespace(),r.push(this.parseExpression());this.consumeWhitespace(),this.consume(")")}return this.parsePropertyAccess({type:"Function",args:r,pipeId:e,fn:i((s,o)=>{if(!e)return s.logError(`Empty pipe function: ${e}`),r[0]?.fn(s,o);let a=this.ctx.getFunction(s,e);return a||s.logError(`Undefined pipe function: ${e}`),a?.apply(s,r.map(p=>p.fn(s,o)))},"fn")})}if(this.consumeIf("[")){this.consumeWhitespace();let e=this.parseExpression();return this.consumeWhitespace(),this.consume("]"),this.parsePropertyAccess({type:"ComputedPropertyAccess",object:t,property:e,fn:i((r,s)=>t.fn(r,s)?.[e.fn(r,s)],"fn")})}return t}};var Qt=new Map,De=1,Xe=i((n,t)=>t,"identityFunc"),ke={$RefProp:i(n=>(t,e)=>n.getByRef(t)?.get(e),"$RefProp")},M=class n{static{i(this,"Expression")}static from(t){let e=Qt.get(t);if(e)return e;let r=new n,s=t.replaceAll(/@(\w+)\.(\w+)/g,o=>{let a=`${D}C${De++}`;return r.connectedPropsNames.set(a,o.slice(1)),a});try{let o=new S(s,r).parse();r.functor=o.fn}catch(o){throw console.error(`Expression: ${t}`,o),o}return Qt.set(t,r),r}functor=Xe;connectedPropsNames=new Map;propNames=new Set;resolveIdentifier(t,e){return t.getFromScope(e)}getFunction(t,e){return e[0]==="$"?ke[e](t):t.platform.getFunction(e)}};function R(n){let t=new M;try{let e=new S(n,t).parse();t.functor=e.fn}catch(e){throw console.error(`Expression: ${n}`,e),e}return t}i(R,"compilePlaceholder");function Jt(n,t){n.startsWith("@")&&(n=n.slice(1));let e=n.indexOf("="),r=n.indexOf("(");if(e>0&&(r===-1||e<r)){let s=n.slice(0,e).trim();n=n.slice(e+1).replaceAll(/@(\w+)\.(\w+)/g,(a,p,c)=>`$RefProp('${p}', '${c}')`);let o=R(n).functor;return a=>p=>a.scope?.emit(s,o(a,p))}if(r>0){let s=`${n.slice(0,r)}()`,o=n.slice(r+1,-1).trim();if(o=o.replaceAll(/@(\w+)\.(\w+)/g,(p,c,f)=>`$RefProp('${c}', '${f}')`),!o)return p=>c=>p.scope?.emit(s,c);let a=lt(o,",").map(p=>R(p).functor);return p=>c=>p.scope?.emit(s,...a.map(f=>f(p,c)))}return n=n.trim(),s=>o=>s.scope?.emit(n,o)}i(Jt,"compileSingleExpression");function Vt(n,t){let e=lt(n,";").filter(Boolean);if(e.length<=1)return Jt(e[0]??n,t);let r=e.map(s=>Jt(s,t));return s=>async o=>{for(let a of r)await a(s)(o)}}i(Vt,"compileEmitterExpression");var He=i(n=>n==null?"":String(n),"str"),je=/\{([^}]+)\}/g,te="{$$$}",mt=new Map;function Fe(n,t){let e=[],r=n.replace(je,(a,p)=>(e.push(t(p.trim())),te));if(!e.length)return()=>r;let s=r.split(te),o=s.pop();return(...a)=>s.map((p,c)=>`${p}${He(e[c](...a))}`).join("")+o}i(Fe,"createStringInterpolator");function ve(n){let t=O(n);return()=>t}i(ve,"compileConstant");function Le(n){if(typeof n!="string")return()=>n;if(n.includes("{")){let t=n.slice(1,-1);return n[0]==="{"&&n.at(-1)==="}"&&!t.includes("{")?R(t).functor:Fe(n,e=>R(e).functor)}return ve(n)}i(Le,"_resolveExpression");var Q=i(n=>mt.has(n)?mt.get(n):mt.set(n,Le(n)).get(n),"resolveExpression");var Ue=1;function J(n){let t={};return n=n.replaceAll(/@(\w+)\.(\w+)/g,r=>{let s=D+Ue++;return t[s]=r.slice(1),s}),{expr:Q(n),props:Object.entries(t)}}i(J,"prepareConnectedProps");var ee=1;var m=class{static{i(this,"ManifestNode")}$content;uid;refId;propertyResolvers=[];initialState={};connectors;tag;constructor(t){this.uid=`N${String(ee++)}:${t}`}createArrmatron(t,e,r){return new this.EntitronConstructor(t,this,e,r)}getSubNodes(t){return this.$content}bindExpression(t,e){let{functor:r,connectedPropsNames:s}=M.from(t);if(s?.size)for(let[o,a]of s.entries())this.addConnector(a,(p,c)=>{p.putInLocalScope(o,c),p.up(e(r(p)))});this.propertyResolvers.push((o,a)=>(Object.assign(a,e(r(o))),a))}addConnectedPropertyResolver(t,e){let{expr:r,props:s}=J(t);s.forEach(([o,a])=>{this.addConnector(a,(p,c)=>(p.putInLocalScope(o,c),_(r(p),f=>{p.up({[e]:f})})))}),this.propertyResolvers.push((o,a)=>(a[e]=r(o),a))}addConnectedDataPropertyResolver(t,e){if(typeof t!="string")this.addDataPropertyResolver(Q(t),e);else{let{expr:r,props:s}=J(t);s.forEach(([o,a])=>{this.addConnector(a,(p,c)=>(p.putInLocalScope(o,c),_(r(p),f=>{p.up({data:{...p.get("data"),[e]:f}})})))}),this.addDataPropertyResolver(r,e)}}addDataPropertyResolver(t,e){this.propertyResolvers.push((r,s)=>{let o=t(r);return s.data=Object.assign(s.data||{},{[e]:o}),s})}addConnectedPropertiesResolver(t){let{expr:e,props:r}=J(t);r.forEach(([s,o])=>{this.addConnector(o,(a,p)=>(a.putInLocalScope(s,p),_(e(a),c=>{let f=a.$$propKeys??(a.$$propKeys={});Object.keys(c??{}).forEach(l=>f[l]=null),a.up({...f,...c})})))}),this.propertyResolvers.push((s,o)=>{let a=e(s);if(a&&typeof a=="object"){let p=s.$$propKeys??(s.$$propKeys={});Object.keys(p).forEach(c=>o[c]=null),Object.entries(a).forEach(([c,f])=>{p[c]=null,o[c]=f})}return o})}addConnector(t,e){let r=this.connectors??(this.connectors=new Map),[s,o]=t.trim().split("."),a=`${s}.${o||"This"}.${ee++}`;r.set(a,e)}resolveInitialProps(t){let e=Object.entries(this.initialState).reduce((r,[s,o])=>(r[s]=o(t),r),{});return this.resolveProps(t,e)}resolveProps(t,e={}){return this.propertyResolvers.reduce((r,s)=>s(t,r),e)}compileAttribute(t,e){if(t.startsWith("data-")&&t!=="data-theme")if(typeof e=="string"&&e[0]==="'"&&e.at(-1)==="'"){let r=e.slice(1,-1);this.addDataPropertyResolver(()=>r,t.slice(5))}else this.addConnectedDataPropertyResolver(e,t.slice(5));else if(t==="Ref")this.refId=String(e);else if(t==="Props")this.addConnectedPropertiesResolver(String(e));else if(typeof e!="string")this.initialState[t]=()=>e;else if(e[0]==="'"&&e.at(-1)==="'"){let r=e.slice(1,-1);this.initialState[t]=()=>r}else if(e.includes("{"))this.addConnectedPropertyResolver(e,t);else if(e.startsWith("@")){let[r,s]=e.slice(1).trim().split(".");s?this.bindExpression(e,o=>({[t]:o})):this.initialState[t]=o=>o.getByRef(r)?.$component}else e.startsWith("->")?this.initialState[t]=Vt(e.slice(2).trim(),this):this.initialState[t]=()=>e}compileAttributes(t){t&&Object.entries(t).forEach(e=>this.compileAttribute(...e))}};var ht=class extends u{static{i(this,"Block")}get displayName(){return`\u{1F4A0}${this.manifest.tag}`}get contentManifests(){let t=this.get("Nodes")??[];return this.platform.getCompiledNodes(t)}},X=class extends m{static{i(this,"CBlockNode")}constructor({id:t,attrs:e}){super(t+(e?.Ref?`:${e?.Ref}`:"")),this.compileAttributes(e)}get EntitronConstructor(){return ht}};var dt=class extends u{static{i(this,"Composite")}get displayName(){return`\u{1F4A0}${this.manifest.tag}`}get scopeForChildren(){return this}slotContent(t){return this.manifest.getSlotContent(this.platform,t)}createComponent(){return this.platform.createComponent(this.manifest.tag)}},k=class extends m{static{i(this,"CCompositeNode")}slotContent=null;tag;nodes;constructor({id:t,tag:e,attrs:r,nodes:s}){super(t+(r?.Ref?`:${r?.Ref}`:"")),this.tag=e,this.nodes=s,this.compileAttributes(r)}getSlotContent(t,e){if(!this.slotContent){let r=this.nodes?.reduce((s,o)=>{if(o){let a=String(o.attrs?.Slot??"default");s[a]=(s[a]||[]).concat(o)}return s},{});this.slotContent=Object.entries(r??{}).reduce((s,[o,a])=>(s[o]=t.getCompiledNodes(a),s),{})}return this.slotContent[e||"default"]??void 0}getSubNodes(t){return t.getCompiledNodes(this.tag)}get EntitronConstructor(){return dt}};var gt=class extends u{static{i(this,"Conditional")}get contentManifests(){return this.ifCondition?this.ifBranches.thenBlock:this.ifBranches.elseBlock}get ifCondition(){return this.$component.condition}get ifBranches(){return this.manifest.getBranches(this.platform)}get displayName(){return`\u{1F539}if:${this.manifest.expr}`}},H=class extends m{static{i(this,"CIfNode")}#t;#e;expr;constructor(t,e){super(t.id),this.#t=t,this.expr=e,this.compileCondition(e)}get EntitronConstructor(){return gt}getBranches(t){if(this.#e)return this.#e;this.#e={};let{nodes:e}=this.#t,r=[this.#t];if(e?.length){let s=e.find(a=>a.tag==="Else"),o=e.find(a=>a.tag==="Then");s?(this.#e.elseBlock=s.nodes?t.getCompiledNodes(s.nodes):void 0,r=o?o.nodes:[{...this.#t,nodes:e.filter(a=>a!==s)}]):o&&(r=o.nodes)}return this.#e.thenBlock=r?t.getCompiledNodes(r):void 0,this.#e}compileCondition(t){if(t.startsWith("Slot(")){let e=t.slice(5,-1);this.propertyResolvers.push((r,s)=>(s.condition=!!r.scope.slotContent?.(e)?.size,s))}else t[0]==="{"&&t[t.length-1]==="}"&&(t=t.slice(1,-1)),this.bindExpression(t,e=>({condition:!!e}))}};var yt=class extends A{static{i(this,"DataConnectorComponent")}_data;change;get data(){return this._data}set data(t){this._data=t,_(t,e=>{e!==void 0&&this.change?.(e)})}},Ct=class extends A{static{i(this,"TriggerConnectorComponent")}change;_trigger;data;get trigger(){return this._trigger}set trigger(t){this._trigger=t,_(t,e=>{e!==void 0&&_(this.data,r=>{this.change?.(r)})})}},_t=class extends u{static{i(this,"Connector")}createComponent(){let t=this.manifest.componentClass;return new t}get displayName(){return"\u{1F539}Connector"}},j=class extends m{static{i(this,"ConnectorNode")}componentClass;constructor({attrs:t,id:e}){super(e);let{trigger:r,change:s,...o}=t;this.compileAttributes(r?{change:s,...o,trigger:r}:{change:s,...o}),this.componentClass=r?Ct:yt}get EntitronConstructor(){return _t}};var Nt=class extends u{static{i(this,"NativeElementEntitron")}get displayName(){return this.manifest.tag}createComponent(){return this.platform.createComponent({tag:this.manifest.tag,native:!0})}},F=class extends m{static{i(this,"CElementNode")}#t;constructor({id:t,tag:e,attrs:r,nodes:s,text:o}){super(t),this.tag=e,this.compileAttributes(r),o&&this.compileAttribute("#text",o),this.#t=s}getSubNodes(t){return this.#t?t.getCompiledNodes(this.#t):void 0}get EntitronConstructor(){return Nt}};var v=class extends m{static{i(this,"CFragmentNode")}#t;constructor({id:t,nodes:e,attrs:r}){super(t),this.#t=e,this.compileAttributes(r)}getSubNodes(t){return this.#t?t.getCompiledNodes(this.#t):void 0}get EntitronConstructor(){return u}};var ne=i(n=>n?typeof n=="string"?n.split(",").map(t=>t.trim()).map(t=>({id:t,name:t})):Array.isArray(n)?n.map(t=>typeof t=="object"?{...t,id:String(t.id??"")}:{id:String(t),name:String(t??"--")}):typeof n[Symbol.iterator]=="function"?[...n]:typeof n=="object"?x(n,(t,e)=>typeof e=="object"?{...e,id:t}:{id:t,name:String(e)}):[{id:String(n),name:String(n)}]:[],"narrowData");var At=class extends u{static{i(this,"Iterative")}pkHash={};get displayName(){return`\u{1F7EA}each:${this.manifest.itemName}`}get eachList(){return this.$component.each}get contentManifests(){let t=ne(this.eachList),e=this.manifest.getItemCtxNode(this.platform),r=new Map;return this.pkHash={},t.forEach(s=>{let o=String(s.id||C(s));if(this.pkHash[o]){this.logError(`ERROR: duplicate item id : ${o} (skipped)`,s);return}this.pkHash[o]=s;let a=e.cloneWithDatum(s,this.platform);a.propertyResolvers.push((p,c)=>(c[e.itemName]=this.pkHash[o],c)),r.set(a.uid,a)}),r}},L=class extends m{static{i(this,"CForNode")}#t;itemName;#e;constructor(t,e,r){super(t.id),this.#e=t,this.itemName=e,this.bindExpression(r,s=>({each:s}))}getItemCtxNode(t){return this.#t??=new $t(this.#e.id,this.itemName,t.getCompiledNodes([this.#e])),this.#t}get EntitronConstructor(){return At}},Et=class extends u{static{i(this,"IterativeItem")}get scopeForChildren(){return this}get displayName(){return`\u{1F7EA}each:item:${this.manifest.itemName}`}emit(t,...e){return this.scope.emit(t,...e)}get(t){let e=this.manifest.itemName;return t.startsWith(`${e}.`)||t===e?super.get(t):this.scope.get(t)}},$t=class n extends m{static{i(this,"CItemNode")}itemName;constructor(t,e,r){super(t),this.itemName=e,this.$content=r}cloneWithDatum(t,e){let r=`${String(this.uid)}#${t.id||C(t)}`,s=new n(r,this.itemName,this.getSubNodes(e));return s.uid=r,s}get EntitronConstructor(){return Et}};var It=class extends u{static{i(this,"DynamicTag")}get displayName(){return"dynamics"}get _dynamicTag(){return this.$component._dynamicTag}get contentManifests(){let t=this._dynamicTag,e=`${this.uid}:${String(t??"")}`,r=this.manifest.getCompiledForTag(this.platform,t);return new Map([[e,Object.assign(r,{uid:e})]])}},U=class extends m{static{i(this,"CDynamicTagNode")}xml;#t=new Map;constructor(t,e){super(t.id),this.compileAttribute("_dynamicTag",e),this.xml=t}getCompiledForTag(t,e){let r=String(e??""),s=this.#t.get(r);return s||(s=t.getCompiledNodes({...this.xml,tag:e}),this.#t.set(r,s)),s}get EntitronConstructor(){return It}};var xt=class extends u{static{i(this,"Selector")}get displayName(){return"\u{1F4A0}Selector"}get contentManifests(){let t=this.manifest.getCases(this.platform),e=this.$component.On;return t?.[e]??t?.default}},B=class extends m{static{i(this,"CSelectorNode")}cases=null;key="";nodes;constructor({id:t,tag:e,attrs:r,nodes:s}){super(t),this.tag=e,this.nodes=s,this.compileAttributes(r)}getCases(t){return this.cases??(this.cases=(this.nodes??[]).reduce((e,r)=>(e[r.attrs?.When]=t.getCompiledNodes(r.nodes??[]),e),{}))}get EntitronConstructor(){return xt}};var Pt=class extends u{static{i(this,"Slot")}get displayName(){return"\u{1F538}slot"}getByRef(t){return this.scope?.getByRef(t)}get slotContent(){return this.scope.slotContent(this.manifest.key)}get contentManifests(){return this.slotContent}get scopeForChildren(){return this.scope.scope}},W=class extends m{static{i(this,"CSlotNode")}key;constructor(t){super(t.id),this.key=t.attrs?.Key}get EntitronConstructor(){return Pt}};var wt=i((n,t)=>{let{[t]:e,...r}=n.attrs??{};return{text:n.text,tag:n.tag,id:n.id,attrs:r,nodes:n.nodes?.map(s=>s)}},"cloneNode"),Be=i(n=>{let{tag:t="div",attrs:e}=n;if(e?.Each){let[r,,...s]=String(e.Each).trim().split(" ");return new L(wt(n,"Each"),r,s.join(" "))}return e?.If?new H(wt(n,"If"),String(e.If)):t==="div"||t?.[0]?.match(/[A-Z0-9]/)==null?new F(n):t==="Dynamic"?new U(wt(n,"As"),String(e?.As??"Error.Dynamic")):t==="Slot"?new W(n):t==="Selector"?new B(n):t==="Connector"?new j(n):t==="Fragment"||t==="Then"||t==="Else"?new v(n):t==="Block"?new X(n):new k(n)},"__compileManifestNode"),St=new Map,V=i(n=>St.has(n)?St.get(n):St.set(n,Be(n)).get(n),"compileManifestNode"),tt=i(n=>new Map(n?.map(V).map(t=>[t.uid,t])??[]),"compileManifestNodes");var Mt=class extends u{static{i(this,"RootCtx")}get displayName(){return"ROOT"}get scopeForChildren(){return this}slotContent(t){}createComponent(){return this.platform.createComponent({})}},$=class extends m{constructor(e){super("R0");this.template=e;this.slotContent={}}template;static{i(this,"CRootNode")}slotContent;nodes;#t;getSubNodes(e){return this.#t??=e.xmlParse(this.template),e.getCompiledNodes(this.#t)}getSlotContent(e,r){}get EntitronConstructor(){return Mt}};var We=i((n,t)=>{let e=new $(t).createArrmatron(n);return e.touch(),e},"launch");var ze=1;function Ke(n,t){t({Ctor:n,tag:n.name??Ge(n)})}i(Ke,"registerFromFunction");function qe(n,t){n.replaceAll(/<Component\s+id="([^"]+)"(?:[^>]*)>([\s\S]*?)<\/Component>/gm,(e,r,s)=>(s=s.replaceAll(/<Signature\b[^>]*?\/>/gm,"").replaceAll(/<Signature\b[^>]*?>[\s\S]*?<\/Signature>/gm,"").replaceAll(/<Subcomponent\s+id="([^"]+)"(?:[^>]*)>([\s\S]*?)<\/Subcomponent>/gm,(o,a,p)=>(p=p.replaceAll(/<(\/?)This-/gm,(c,f)=>`<${f}${r}-`).trim(),t({tag:`${r}-${a}`,template:p}),"")).replaceAll(/<(\/?)This-/gm,(o,a)=>`<${a}${r}-`).trim(),t({tag:r,template:s}),""))}i(qe,"registerFromString");var Ge=i(n=>(/^function\s+([\w$]+)\s*\(/.exec(n.toString())??[])[1]??`C${String(ze++)}`,"fnName");function Rt(n){return t=>{if(!t)return;let e=i(r=>{Array.isArray(r)?r.filter(Boolean).forEach(e):typeof r=="string"?qe(r,n):typeof r=="function"?Ke(r,n):r.tag||r.id?n({...r,tag:r.tag??r.id}):x(r,(s,o)=>n({tag:s,template:o}))},"registerType");e(t)}}i(Rt,"createRegisterTypes");var et=class{constructor(t,e){this.tag=t}tag;static{i(this,"NativeElement")}nodes=[];attrs={};native=!0;__stateChanged(t){Object.assign(this.attrs,Object.fromEntries([...t.entries()]))}getName(){return this.tag}getAttribute(t,e=null){return this.attrs[t]??e}getRequiredAttribute(t){let e=this.getAttribute(t);if(!e)throw new Error(`Required attribute '${t}' missing for ${this.getName()}`);return e}getBooleanAttribute(t,e=!1){let r=this.getAttribute(t,e);return typeof r=="boolean"?r:["true","1","yes","on"].includes(String(r).toLowerCase())}getNumericAttribute(t,e=0){let r=this.getAttribute(t,e);return parseFloat(r)||e}getEnumAttribute(t,e,r){let s=this.getAttribute(t,r);return e.includes(s)?s:r}getTextContent(){return this.attrs["#text"]}getChildren(){return this.nodes}};var nt=class{static{i(this,"Registry")}typeRegistry=new Map;compiledTemplates=new Map;NativeElement=et;registerTypes(t){Rt(e=>{let r=String(e.tag);this.compiledTemplates.delete(r),this.typeRegistry.set(r,e)})(t)}getByTag(t){let e=this.typeRegistry.get(t);if(e)return e;let r=t.split(".");for(r.pop();r.length;r.pop()){let s=this.typeRegistry.get(r.join("."));if(s)return s}return{tag:t,unknown:!0}}xmlParse(t){try{return at(t)}catch(e){return[{tag:"div",text:String(e),id:"error"}]}}getCompiledNodes(t){if(typeof t=="string"){let e=this.compiledTemplates;if(e.has(t))return e.get(t);let r=this.getByTag(t);if(!r?.template)return[];let s=r.template,o=typeof s=="string"?this.xmlParse(s):s,a=tt(o);return e.set(t,a),a}return Array.isArray(t)?tt(t):V(t)}createComponent(t){if(typeof t=="string")return this.createComponent(this.getByTag(t));let e=this.NativeElement;try{let{Ctor:r,tag:s,unknown:o,native:a}=t;return a?new e(s??"div",this):r?new r:o?(this.log({level:"info",message:`<-- unknown tag: ${s} -->`,source:"registry"}),new e("div",this)):new A}catch(r){return this.log({level:"error",message:"createComponent failed",error:r,source:"registry"}),new e("div",this)}}log(t){typeof t=="string"?console.log(t):console.log(t.source??"",t.message??"",...t.params??[])}};var z=class extends nt{static{i(this,"Platform")}resources={};constructor(t){super(),this.updateResources(t)}redraw(t,e){}updateResources(t){x(t,(e,r)=>{if(e[0]==="$")return;if(e==="NativeElement"){this.NativeElement=r;return}if(e==="components"){this.registerTypes(r);return}let s=e.split(".");if(s.length>1){let o=s.pop()??"-",a=s.reduce((p,c)=>p[c]??(p[c]={}),this.resources);a[o]=b(a[o],r);return}this.resources[e]=b(this.resources[e],r)})}getResource(t){let[e,...r]=typeof t=="string"?t.split("."):t,{resources:s}=this,o=e?s[e]:s;return!o||r.length===0?o:r.length===1?o[r[0]]:r.reduce((a,p)=>a?a[p]:null,o)}getFunction(t){return pt[t]||this.resources.functions?.[t]}applyAsyncValue(t,e,r){return e instanceof Promise?e.then(s=>r(s)):r(e)}};function rt(n,t){if(n)for(let e of n.values()){let r=e.$component;r.native?(rt(e.children,r),t.nodes.push(r)):rt(e.children,t)}}i(rt,"toNativeTree");function Ye(n,{reflow:t=rt,rootElement:e={tag:"root",nodes:[]},...r}){let s=new z(r),o=new $(n).createArrmatron(s);return s.redraw=()=>{t(o.children,e)},o.touch(),e}i(Ye,"applyTemplate");0&&(module.exports={CRootNode,Component,Platform,applyTemplate,compileManifestNode,compileManifestNodes,createRegisterTypes,launch});
@@ -0,0 +1,151 @@
1
+ ---
2
+ title: "Arrmatura Architecture"
3
+ description: "This document outlines the key architectural and design considerations that guided the development of the Arrmatura framework."
4
+ keywords: [arrmatura, architecture]
5
+ ---
6
+
7
+ Arrmatura is a **runtime-based framework** that enables building complex, dynamic reactive applications with minimal boilerplate and excellent performance.
8
+
9
+ ## Design Principles
10
+
11
+ Arrmatura comes with unique architectural patterns, demonstrates sophisticated design:
12
+
13
+ - Well-structured core architecture with clear separation of concerns
14
+ - manifest-based component compilation.
15
+ - Sophisticated component lifecycle management
16
+ - Dynamic component composition
17
+ - Runtime template evaluation
18
+ - Lightweight bundle sizes
19
+ - Reactive state management and data flow
20
+ - Memory management considerations (cleanup on done())
21
+ - Fingerprinting for change detection - Clever optimization
22
+ - Comprehensive expression parser with extensive test coverage
23
+ - Extend functionality with TypeScript custom service components for complex business logic and integrations.
24
+
25
+ ### Architectural choices
26
+
27
+ **Declarative-first**: should use for implementation the [Arrmatura](../libs/arrmatura/README.md) framework.
28
+ **Testing-driven**: Vitest with globals API (no imports needed)
29
+ **AI-native** integrate with LangChain
30
+ **GCP as target platform** rely on GCP ecosystem services: Auth, Firestore, Functions, Hosting
31
+ **Unified Code Style** see `biome.json` for default convention.
32
+
33
+ ## Abstraction Layers
34
+
35
+ Arrmatura ecosystems separates concerns into three layers: framework, platform, application.
36
+
37
+ - a Arrmatura is a **Runtime Engine** to perform component lifecycle, state management, data flow, event handling.
38
+ - Particular **IPlatform** implementations (e.g.Web DOM manipulation, rendering) use it in specific *tech stack*.
39
+ - Components and Services are built on top and carries *application-level* business logic and UI.
40
+
41
+ ## Runtime Engine core concepts
42
+
43
+ - **Arrmatron**: framework core object that wrapping a component. Has `uid`, `parent`, `children`, `get()`, `up()` (update state), `emit()`.
44
+ - **Component** (`IComponent`): Plain TS class/object with optional lifecycle hooks: `__init()`, `__stateChanged()`.
45
+ - **CML template** (`.xml`): Declarative XML that defines component trees. Loaded as text, parsed into `ManifestNode` trees.
46
+ - **Platform**: Bridges the Arrmatura runtime to the DOM (or other targets). Handles rendering via `redraw`.
47
+ - **Registry**: Maps tag names to component implementations. Apps register their components in `src/components.ts`.
48
+ - **Expression system**: Powerful parser for dynamic bindings in templates (e.g. `{data | upper}`).
49
+
50
+ ### Platform
51
+
52
+ **Platform Interface** (`IPlatform`):
53
+
54
+ - Component creation and lifecycle
55
+ - DOM manipulation and updates
56
+ - Resource and function registries
57
+ - Logging and debugging
58
+
59
+ > This abstraction allows Arrmatura to theoretically target different platforms (Web, Native, etc.) by providing different platform implementations.
60
+
61
+ ### Built-In Components
62
+
63
+ Before creating Arrmatrons, XML templates are compiled into **Manifest Nodes** (`IManifestNode`):
64
+
65
+ Manifest nodes are cached and reused, containing the logic for:
66
+
67
+ - Resolving properties with expressions
68
+ - Creating Arrmatron instances
69
+ - Managing sub-nodes
70
+
71
+ ## Data Flow
72
+
73
+ ### One-Way Binding
74
+
75
+ Arrmatura uses **one-way data binding** from parent to child:
76
+
77
+ ```xml
78
+ <Component id="Parent">
79
+ <Child data="{parentData}" />
80
+ </Component>
81
+ ```
82
+
83
+ 1. Parent's `parentData` changes
84
+ 2. Parent's Arrmatron calls `touch()` to redraw
85
+ 3. Platform resolves `{parentData}` expression
86
+ 4. Child receives updated property
87
+ 5. Child redraws if needed
88
+
89
+ ### Reactive Updates
90
+
91
+ When component state changes:
92
+
93
+ 1. Component calls `up(delta)` with state changes
94
+ 2. Async values (Promises) are handled automatically
95
+ 3. Connected properties (`@ref.prop`) are tracked
96
+ 4. Platform schedules a redraw
97
+ 5. Only affected components redraw
98
+
99
+ ## Component Lifecycle
100
+
101
+ ### Initialization Phase
102
+
103
+ ```
104
+ 1. Platform.getCompiledNodes(template)
105
+ ↓
106
+ 2. ManifestNode.createArrmatron(platform, parent, scope)
107
+ ↓
108
+ 3. Platform.createComponent(descriptor)
109
+ ↓
110
+ 4. Component.__init() [if defined]
111
+ ↓
112
+ 5. ManifestNode.resolveInitialProps(arrmatron)
113
+ ↓
114
+ 6. Create child Arrmatrons recursively
115
+ ↓
116
+ 7. arrmatron.touch() - first render
117
+ ```
118
+
119
+ ### Update Phase
120
+
121
+ ```
122
+ 1. Component.up(delta) or external state change
123
+ ↓
124
+ 2. Apply state changes to component
125
+ ↓
126
+ 3. Handle async values (Promises)
127
+ ↓
128
+ 4. Call Component.__stateChanged(changes) [if defined]
129
+ ↓
130
+ 5. arrmatron.touch()
131
+ ↓
132
+ 6. Platform.redraw(arrmatron, root)
133
+ ↓
134
+ 7. Resolve properties for all children
135
+ ↓
136
+ 8. Update DOM (native elements)
137
+ ↓
138
+ 9. Recursively update affected children
139
+ ```
140
+
141
+ ### Cleanup Phase
142
+
143
+ ```
144
+ 1. root.done()
145
+ ↓
146
+ 2. Execute deferred cleanup functions
147
+ ↓
148
+ 3. Remove event listeners
149
+ ↓
150
+ 4. Cleanup child Arrmatrons
151
+ ```
@@ -0,0 +1,223 @@
1
+ ---
2
+ title: "Built-ins"
3
+ description: "Collection of Intrinsic built-in components."
4
+ keywords: [arrmatura]
5
+ ---
6
+
7
+ Built-ins are the components the runtime implements itself.
8
+
9
+ ## Element — native tags
10
+
11
+ Lowercase tags and `div`.
12
+
13
+ ```xml
14
+ <div class="card {active ? 'active'}" click="-> select(id)">{title}</div>
15
+ ```
16
+
17
+ - All attributes compile through `compileAttribute`; element text compiles into the `#text` property.
18
+ - Children are compiled lazily on first `getSubNodes`.
19
+ - The component is built as `platform.createComponent({ tag, native: true })`.
20
+
21
+ ## Composite — registered components
22
+
23
+ Any other capitalised tag.
24
+
25
+ ```xml
26
+ <Card Ref="card" title="{doc.title}" />
27
+ ```
28
+
29
+ - Content comes from the *registered template* of the tag, not from the children written at the call
30
+ site — those become slot content (see `Slot`).
31
+ - `Ref` is appended to the manifest `uid`, so two usages differing only by `Ref` stay distinct.
32
+ - Tag lookup falls back through dotted prefixes (`Page.Docs.Header` → `Page.Docs` → `Page`).
33
+
34
+ ## If / Then / Else
35
+
36
+ ```xml
37
+ <div If="isReady">ready</div>
38
+
39
+ <Fragment If="count > 0">
40
+ <Then><List items="{items}" /></Then>
41
+ <Else><Empty /></Else>
42
+ </Fragment>
43
+ ```
44
+
45
+ - The expression may be written bare or fully wrapped in `{…}`; a partial template is not supported.
46
+ - The condition is coerced with `!!`.
47
+ - Branches are resolved once and cached:
48
+
49
+ | Children | then-branch | else-branch |
50
+ | --- | --- | --- |
51
+ | neither `Then` nor `Else` | the element itself, minus `If` | nothing |
52
+ | `Else` only | the element minus the `Else` child | children of `Else` |
53
+ | `Then` only | children of `Then` | nothing |
54
+ | both | children of `Then` | children of `Else` |
55
+
56
+ As soon as an explicit `<Then>` is present the host element is dropped and only its children render.
57
+ Use `<Fragment If>` for if/else, and the bare form for a single branch.
58
+
59
+ ### Slot presence check
60
+
61
+ ```xml
62
+ <div If="Slot(header)"><Slot Key="header" /></div>
63
+ ```
64
+
65
+ `Slot(key)` is a compile-time special form, not an expression: the condition becomes "the caller
66
+ passed non-empty content for that slot". It reads the enclosing `Composite` directly and must not be
67
+ placed inside an iteration.
68
+
69
+ ## Each
70
+
71
+ ```xml
72
+ <Item Each="item of items" data="{item}" />
73
+ <Row Each="row of data | mapEntries" row="{row}" />
74
+ ```
75
+
76
+ Syntax is `<name> <word> <expression>`: the first token names the item, the second is ignored (`of`
77
+ by convention), the rest is the expression.
78
+
79
+ The value is normalised by `narrowData`:
80
+
81
+ | Input | Items |
82
+ | --- | --- |
83
+ | falsy | `[]` |
84
+ | `"a, b"` | `{ id, name }` per comma-separated token |
85
+ | array | objects kept, `id` forced to string; scalars become `{ id, name }` |
86
+ | iterable | spread as-is |
87
+ | object | entries become items keyed by their key |
88
+
89
+ Then:
90
+
91
+ - The item key is `datum.id`, falling back to `hashCodeOf(datum)`. A **duplicate key is logged as an
92
+ error and skipped**, so the row disappears.
93
+ - Each item gets a cloned manifest with `uid = <uid>#<key>`, which is what makes reuse across renders
94
+ stable.
95
+ - `IterativeItem` opens a scope: `item` and `item.*` resolve against itself, everything else and all
96
+ `emit` calls delegate to the enclosing scope.
97
+ - Item data is re-read from the live map on every render, so mutating an existing item propagates
98
+ into the reused child.
99
+
100
+ ## Fragment / Then / Else
101
+
102
+ ```xml
103
+ <Fragment If="showContent">
104
+ <Header />
105
+ <Content />
106
+ </Fragment>
107
+ ```
108
+
109
+ Transparent grouping: no component of its own beyond a plain `Component`, no new scope, children
110
+ render in place. `Then` and `Else` compile to the same node and are meaningful only as direct
111
+ children of an `If` element.
112
+
113
+ ## Dynamic
114
+
115
+ ```xml
116
+ <Dynamic As="{viewMode == 'list' ? 'ListView' : 'GridView'}" items="{items}" />
117
+ ```
118
+
119
+ - `As` compiles into the internal `_dynamicTag` property; every other attribute and all children pass
120
+ through to the resolved tag.
121
+ - The resolved element is compiled once per distinct tag value and cached on the node.
122
+ - Missing `As` compiles to the tag `Error.Dynamic`; an expression resolving to `undefined` falls back
123
+ to the compiler default, a `div`.
124
+
125
+ ## Slot
126
+
127
+ Content projection. `Composite` groups the children written at the call site by their `Slot`
128
+ attribute, defaulting to the group `default`.
129
+
130
+ ```xml
131
+ <!-- definition -->
132
+ <Component id="Card">
133
+ <div class="card">
134
+ <Slot Key="header" />
135
+ <Slot />
136
+ <Slot Key="footer" />
137
+ </div>
138
+ </Component>
139
+
140
+ <!-- usage -->
141
+ <Card>
142
+ <Fragment Slot="header"><h1>{title}</h1></Fragment>
143
+ <p>body</p>
144
+ <Fragment Slot="footer"><Btn label="OK" /></Fragment>
145
+ </Card>
146
+ ```
147
+
148
+ - `Key` selects the group; no `Key` means `default`.
149
+ - An unfilled slot renders nothing.
150
+ - Projected content resolves properties and refs **at the call site** (`scopeForChildren` is the
151
+ grandparent scope), so it sees the caller's data, not the component's internals.
152
+ - Slot groups are compiled once per component usage and cached.
153
+
154
+ ## Selector
155
+
156
+ Multi-way switch on one string key.
157
+
158
+ ```xml
159
+ <Selector On="{node | typeOf}">
160
+ <Case When="object"><XmlElement node="{node}" /></Case>
161
+ <Case When="string"><span>{node}</span></Case>
162
+ <Case When="default">—</Case>
163
+ </Selector>
164
+ ```
165
+
166
+ - Children are grouped by their `When` attribute; the child tag name is never inspected, `Case` is
167
+ convention only.
168
+ - Matching is exact string equality against `On`, falling back to the group `default`.
169
+ - No match and no `default` renders nothing.
170
+ - `On` is commonly composed from several fragments (`"{data|typeOf}{isValuable(data) ? '' : '-empty'}"`).
171
+
172
+ ## Connector
173
+
174
+ A component with no output that forwards values. Two modes, chosen by the presence of `trigger`.
175
+
176
+ | Mode | Attributes | Behaviour |
177
+ | --- | --- | --- |
178
+ | data | `data`, `change` | every time `data` resolves to a value other than `undefined`, call `change` with it |
179
+ | trigger | `trigger`, `data`, `change` | every time `trigger` resolves to a value other than `undefined`, resolve `data` and call `change` with the resolved data |
180
+
181
+ ```xml
182
+ <Connector data="@query.data" change="-> options" />
183
+ <Connector data="{find(R.enums[typeSpec], value)}" change="-> item" />
184
+ <Connector trigger="1" change="-> @service.nextQuestion()" />
185
+ <Connector data-value="{value}" trigger="{value}" change="{onChange}" />
186
+ ```
187
+
188
+ - Promises are unwrapped on both `data` and `trigger`, so async sources need no extra handling.
189
+ - `change` is either a `->` emitter or a function-valued expression.
190
+ - Attributes are re-ordered at compile time — `change` first, `trigger` last — because state applies
191
+ in that order and the firing setter must find its target already set.
192
+ - `trigger="1"` is the idiom for "run once when this subtree mounts".
193
+
194
+ ## Block
195
+
196
+ Renders XML nodes produced at runtime.
197
+
198
+ ```xml
199
+ <Block Nodes="{@mdService.nodes}" />
200
+ ```
201
+
202
+ - `Nodes` holds an `XmlNode[]` (as produced by `xmlParse` or by a service, e.g. a markdown renderer).
203
+ - The array is compiled on every render — array input is not memoised, unlike compilation by tag.
204
+ - `Ref` is appended to the manifest `uid`, as for `Composite`.
205
+ - A missing or empty `Nodes` renders nothing.
206
+
207
+ ## Root
208
+
209
+ `CRootNode` / `RootCtx` have no tag and are not reachable from CML. `launch()` and `applyTemplate()`
210
+ wrap a template string in one: it parses the XML, opens the outermost scope, exposes no slots, and
211
+ builds a bare `Component`.
212
+
213
+ ## Gotchas
214
+
215
+ - `Each` and `If` on the same element: the loop is outer, the condition is evaluated per item.
216
+ - An explicit `<Then>` drops the host element; only its children render.
217
+ - Duplicate `id` values inside `Each` silently lose rows (logged as an error).
218
+ - `Slot(key)` in an `If` must not be used inside an iteration.
219
+ - Slot content sees the caller's scope, not the component's — passing data *into* a slot means passing
220
+ it through properties.
221
+ - `Selector` matches strings exactly; a numeric `On` will not match `When="1"` unless it stringifies
222
+ to exactly that.
223
+ - A tag whose first character is a digit is treated as a component and will be reported as unknown.