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.
- package/README.md +62 -22
- package/dist/index.js +4 -6
- package/docs/architecture.md +151 -0
- package/docs/built-ins.md +223 -0
- package/docs/cml.md +342 -0
- package/index.ts +1 -22
- package/package.json +22 -18
- package/src/compiler/index.test.ts +152 -0
- package/src/{registry → compiler}/index.ts +22 -18
- package/src/controls/Iterative.test.ts +116 -0
- package/src/controls/Iterative.ts +127 -0
- package/src/controls/block.test.ts +44 -0
- package/src/controls/block.ts +25 -0
- package/src/controls/composition.test.ts +56 -0
- package/src/{registry → controls}/composition.ts +13 -22
- package/src/controls/conditionals.test.ts +103 -0
- package/src/controls/conditionals.ts +79 -0
- package/src/controls/connector.test.ts +62 -0
- package/src/controls/connector.ts +75 -0
- package/src/controls/elementary.test.ts +37 -0
- package/src/{registry → controls}/elementary.ts +3 -6
- package/src/controls/fragment.test.ts +26 -0
- package/src/{registry → controls}/fragment.ts +3 -4
- package/src/controls/index.ts +10 -0
- package/src/controls/root.test.ts +50 -0
- package/src/{registry → controls}/root.ts +8 -10
- package/src/controls/routing.test.ts +64 -0
- package/src/controls/routing.ts +45 -0
- package/src/controls/selection.test.ts +62 -0
- package/src/{registry → controls}/selection.ts +4 -9
- package/src/controls/slot.test.ts +62 -0
- package/src/{registry → controls}/slot.ts +12 -10
- package/src/core/Arrmatron.test.ts +680 -0
- package/src/core/Arrmatron.ts +470 -236
- package/src/core/Component.test.ts +203 -0
- package/src/core/Component.ts +106 -49
- package/src/core/ManifestNode.test.ts +225 -0
- package/src/core/ManifestNode.ts +213 -55
- package/src/core/NativeElement.test.ts +173 -0
- package/src/core/NativeElement.ts +89 -0
- package/src/core/Platform.test.ts +127 -0
- package/src/core/Platform.ts +116 -0
- package/src/core/Registry.test.ts +171 -0
- package/src/core/Registry.ts +162 -0
- package/src/core/consts.ts +3 -0
- package/src/core/launch.test.ts +37 -0
- package/src/core/launch.ts +18 -0
- package/src/expr/Expression.test.ts +132 -0
- package/src/expr/Expression.ts +62 -0
- package/src/expr/ExpressionParser.test.ts +353 -0
- package/src/expr/ExpressionParser.ts +216 -0
- package/src/expr/ExpressionParserBase.test.ts +292 -0
- package/src/expr/ExpressionParserBase.ts +175 -0
- package/src/expr/ParserContext.ts +17 -0
- package/src/expr/compileEmitterExpression.test.ts +142 -0
- package/src/expr/compileEmitterExpression.ts +64 -0
- package/src/expr/compilePlaceholder.test.ts +44 -0
- package/src/expr/compilePlaceholder.ts +14 -0
- package/src/expr/operations.test.ts +138 -0
- package/src/expr/operations.ts +53 -0
- package/src/expr/prepareConnectedProps.test.ts +44 -0
- package/src/expr/prepareConnectedProps.ts +16 -0
- package/src/expr/resolveExpression.test.ts +96 -0
- package/src/expr/resolveExpression.ts +66 -0
- package/src/index.ts +8 -0
- package/src/types.ts +199 -0
- package/src/utils/applyStateChangedToImpl.test.ts +28 -0
- package/src/utils/applyStateChangedToImpl.ts +12 -16
- package/src/utils/applyTemplate.test.ts +53 -0
- package/src/utils/applyTemplate.ts +20 -0
- package/src/utils/asyncValueCall.test.ts +20 -0
- package/src/utils/asyncValueCall.ts +11 -0
- package/src/utils/capitalize.test.ts +21 -0
- package/src/utils/capitalize.ts +7 -0
- package/src/utils/createRegisterTypes.test.ts +70 -0
- package/src/utils/createRegisterTypes.ts +67 -0
- package/src/utils/defineCalculatedProperty.test.ts +58 -0
- package/src/utils/defineCalculatedProperty.ts +52 -0
- package/src/utils/defineObjectRef.test.ts +30 -0
- package/src/utils/defineObjectRef.ts +18 -0
- package/src/utils/hashCodeOf.test.ts +124 -0
- package/src/utils/hashCodeOf.ts +147 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/isEquals.test.ts +76 -0
- package/src/utils/isEquals.ts +74 -0
- package/src/utils/mapEntries.test.ts +22 -0
- package/src/utils/mapEntries.ts +9 -0
- package/src/utils/mergeObject.test.ts +34 -0
- package/src/utils/mergeObject.ts +25 -0
- package/src/utils/narrowData.test.ts +51 -0
- package/src/utils/narrowData.ts +27 -12
- package/src/utils/scalarParse.test.ts +38 -0
- package/src/utils/scalarParse.ts +41 -0
- package/src/utils/splitTopLevel.test.ts +34 -0
- package/src/utils/splitTopLevel.ts +28 -0
- package/src/utils/stringify.test.ts +59 -0
- package/src/utils/stringify.ts +26 -21
- package/src/utils/toNativeTree.test.ts +53 -0
- package/src/utils/toNativeTree.ts +24 -0
- package/src/utils/xmlParse.test.ts +78 -0
- package/src/utils/xmlParse.ts +120 -0
- package/dist/index.cjs +0 -6
- package/dist/index.cjs.map +0 -7
- package/dist/index.js.map +0 -7
- package/docs/components.md +0 -89
- package/docs/getstarted.md +0 -63
- package/docs/manual.md +0 -9
- package/docs/templates.md +0 -222
- package/src/core/resolveExpression.ts +0 -29
- package/src/registry/Iterative.ts +0 -129
- package/src/registry/conditionals.ts +0 -80
- package/src/registry/connector.ts +0 -48
- package/src/registry/routing.ts +0 -37
- package/src/utils/FingerprintMashine.ts +0 -126
- package/src/utils/compileExpression.ts +0 -136
- package/src/utils/objectFingerprint.spec.ts +0 -70
- package/types.ts +0 -90
package/README.md
CHANGED
|
@@ -1,35 +1,75 @@
|
|
|
1
1
|
# Arrmatura
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## What is Arrmatura?
|
|
4
4
|
|
|
5
|
-
|
|
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
|
|
13
|
+
to build web applications that are scalable, maintainable, and easy to use.
|
|
25
14
|
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
- [
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
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
|
|
2
|
-
`):"";return`${t}<${
|
|
3
|
-
${
|
|
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.
|