tina4-nodejs 3.13.101 → 3.13.103
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/CLAUDE.md +2 -2
- package/package.json +2 -2
- package/packages/cli/dist/bin.js +117 -112
- package/packages/core/dist/index.js +117 -112
- package/packages/core/public/js/tina4-dev-admin.min.js +2 -2
- package/packages/core/src/metrics.ts +1 -1
- package/packages/frond/dist/index.js +113 -109
- package/packages/frond/src/engine.ts +128 -130
- package/packages/orm/dist/index.js +117 -112
- package/types/frond/src/engine.d.ts +4 -2
|
@@ -234,11 +234,11 @@ ${t.traceback||""}`;window.__switchTab("chat"),setTimeout(()=>{window.__prefillC
|
|
|
234
234
|
</tr>`).join("")}
|
|
235
235
|
</tbody>
|
|
236
236
|
</table>
|
|
237
|
-
`)}function Xy(i,e){const t=[],n={},r={};i.forEach((s,o)=>{n[Rd(s)]=o,r[s]=o});for(const[s,o]of Object.entries(e)){const a=r[s];if(a!==void 0)for(const l of o){const O=r[l];if(O!==void 0){O!==a&&t.push([a,O]);continue}const c=l.replace(/^\.\//,"").replace(/^\.\.\//,"").split(/[./]/);let d;for(let h=c.length-1;h>=0;h--){const f=c[h].toLowerCase();if(f&&f!=="js"&&f!=="py"&&f!=="rb"&&f!=="ts"&&f!=="index"&&(d=n[f],d!==void 0))break}d===void 0&&(d=n[Rd(l)]),d!==void 0&&a!==d&&t.push([a,d])}}return t}function Rd(i){const e=i.replace(/\\/g,"/").split("/").pop()||"",t=e.lastIndexOf(".");return(t>0?e.substring(0,t):e).toLowerCase()}function _y(i,e,t,n){var gi,$n,ps;const r=e.offsetWidth||900,s=Math.max(450,Math.min(650,r*.45)),o=Math.max(...i.map(G=>G.loc))||1,a=Math.max(...i.map(G=>G.dep_count||0))||1,l=Math.max(...i.map(G=>G.coupling_afferent||0))||1,O=i.some(G=>(G.coupling_afferent||0)>0),c=14,d=Math.min(70,r/10);function h(G){const I=Math.min((G.avg_complexity||0)/10,1),D=G.
|
|
237
|
+
`)}function Xy(i,e){const t=[],n={},r={};i.forEach((s,o)=>{n[Rd(s)]=o,r[s]=o});for(const[s,o]of Object.entries(e)){const a=r[s];if(a!==void 0)for(const l of o){const O=r[l];if(O!==void 0){O!==a&&t.push([a,O]);continue}const c=l.replace(/^\.\//,"").replace(/^\.\.\//,"").split(/[./]/);let d;for(let h=c.length-1;h>=0;h--){const f=c[h].toLowerCase();if(f&&f!=="js"&&f!=="py"&&f!=="rb"&&f!=="ts"&&f!=="index"&&(d=n[f],d!==void 0))break}d===void 0&&(d=n[Rd(l)]),d!==void 0&&a!==d&&t.push([a,d])}}return t}function Rd(i){const e=i.replace(/\\/g,"/").split("/").pop()||"",t=e.lastIndexOf(".");return(t>0?e.substring(0,t):e).toLowerCase()}function _y(i,e,t,n){var gi,$n,ps;const r=e.offsetWidth||900,s=Math.max(450,Math.min(650,r*.45)),o=Math.max(...i.map(G=>G.loc))||1,a=Math.max(...i.map(G=>G.dep_count||0))||1,l=Math.max(...i.map(G=>G.coupling_afferent||0))||1,O=i.some(G=>(G.coupling_afferent||0)>0),c=14,d=Math.min(70,r/10);function h(G){const I=Math.min((G.avg_complexity||0)/10,1),D=G.has_referencing_test?0:1,de=Math.min((G.dep_count||0)/5,1),j=I*.4+D*.4+de*.2,W=Math.max(0,Math.min(1,j)),H=Math.round(120*(1-W)),te=Math.round(70+W*30),pe=Math.round(42+18*(1-W));return`hsl(${H},${te}%,${pe}%)`}function f(G){return G.loc/o*.4+(G.avg_complexity||0)/10*.4+(G.dep_count||0)/a*.2}const p=[...i].sort((G,I)=>f(G)-f(I)),m=r/2,$=s/2,g=[];let y=0,S=0;for(const G of p){const I=c+Math.sqrt(f(G))*(d-c),D=h(G);let de=!1;for(let j=0;j<800;j++){const W=m+S*Math.cos(y),H=$+S*Math.sin(y);let te=!1;for(const pe of g){const he=W-pe.x,ke=H-pe.y;if(Math.sqrt(he*he+ke*ke)<I+pe.r+2){te=!0;break}}if(!te&&W>I+2&&W<r-I-2&&H>I+25&&H<s-I-2){g.push({x:W,y:H,vx:0,vy:0,r:I,color:D,f:G}),de=!0;break}y+=.2,S+=.04}de||g.push({x:m+(Math.random()-.5)*r*.3,y:$+(Math.random()-.5)*s*.3,vx:0,vy:0,r:I,color:D,f:G})}const x=Xy(g.map(G=>G.f.path),t),w=document.createElement("canvas");w.width=r,w.height=s,w.style.cssText="display:block;border:1px solid var(--border);border-radius:8px;cursor:pointer;background:#0f172a";const R=n==="framework"?'<span style="color:#cba6f7;font-weight:600">(Framework)</span> Add code to src/ to see your project':"";e.innerHTML=`<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:0.5rem"><h3 style="margin:0;font-size:0.85rem">Code Landscape ${R}</h3><span style="font-size:0.65rem;color:var(--muted)">Drag bubbles | Dbl-click to drill down</span></div><div style="position:relative" id="metrics-canvas-wrap"></div><div style="margin-top:0.4rem;font-size:0.62rem;color:var(--muted);display:flex;gap:0.9rem;flex-wrap:wrap">${O?'<span>Centre = most depended-on</span><span><span style="color:hsl(210,85%,60%)">●</span> stable ring</span><span><span style="color:hsl(30,85%,60%)">●</span> unstable ring</span>':""}<span id="metrics-hover-info" class="text-mono"></span></div>`,document.getElementById("metrics-canvas-wrap").appendChild(w);const _=document.getElementById("metrics-hover-info"),A=document.createElement("div");A.style.cssText="position:absolute;top:8px;left:8px;z-index:2;display:flex;gap:4px;flex-direction:column",A.innerHTML=`
|
|
238
238
|
<button class="btn btn-sm" id="metrics-zoom-in" style="width:28px;height:28px;padding:0;font-size:14px;font-weight:700;line-height:1">+</button>
|
|
239
239
|
<button class="btn btn-sm" id="metrics-zoom-out" style="width:28px;height:28px;padding:0;font-size:14px;font-weight:700;line-height:1">−</button>
|
|
240
240
|
<button class="btn btn-sm" id="metrics-zoom-fit" style="width:28px;height:28px;padding:0;font-size:10px;font-weight:700;line-height:1">Fit</button>
|
|
241
|
-
`,document.getElementById("metrics-canvas-wrap").appendChild(A),(gi=document.getElementById("metrics-zoom-in"))==null||gi.addEventListener("click",()=>{le=Math.min(5,le*1.3)}),($n=document.getElementById("metrics-zoom-out"))==null||$n.addEventListener("click",()=>{le=Math.max(.3,le*.7)}),(ps=document.getElementById("metrics-zoom-fit"))==null||ps.addEventListener("click",()=>{le=1,U=0,oe=0});const P=w.getContext("2d");let v=-1,k=-1,X=0,z=0,U=0,oe=0,le=1,rt=!1,mn=0,Gt=0,us=0,ft=0;function De(){for(let j=0;j<g.length;j++){if(j===k)continue;const W=g[j],H=m-W.x,te=$-W.y,pe=.3+W.r/d*.7,he=O?.3+(W.f.coupling_afferent||0)/l*.7:pe,ke=.008*pe*he;W.vx+=H*ke,W.vy+=te*ke}for(const[j,W]of x){const H=g[j],te=g[W],pe=te.x-H.x,he=te.y-H.y,ke=Math.sqrt(pe*pe+he*he)||1,Dt=H.r+te.r+20,Bt=(ke-Dt)*.002,ms=pe/ke*Bt,$s=he/ke*Bt;j!==k&&(H.vx+=ms,H.vy+=$s),W!==k&&(te.vx-=ms,te.vy-=$s)}for(let j=0;j<g.length;j++)for(let W=j+1;W<g.length;W++){const H=g[j],te=g[W],pe=te.x-H.x,he=te.y-H.y,ke=Math.sqrt(pe*pe+he*he)||1,Dt=H.r+te.r+20;if(ke<Dt){const Bt=40*(Dt-ke)/Dt,ms=pe/ke*Bt,$s=he/ke*Bt;j!==k&&(H.vx-=ms,H.vy-=$s),W!==k&&(te.vx+=ms,te.vy+=$s)}}for(let j=0;j<g.length;j++){if(j===k)continue;const W=g[j];W.vx*=.65,W.vy*=.65;const H=2;W.vx=Math.max(-H,Math.min(H,W.vx)),W.vy=Math.max(-H,Math.min(H,W.vy)),W.x+=W.vx,W.y+=W.vy,W.x=Math.max(W.r+2,Math.min(r-W.r-2,W.x)),W.y=Math.max(W.r+25,Math.min(s-W.r-2,W.y))}}function st(){var G;De(),P.clearRect(0,0,r,s),P.save(),P.translate(U,oe),P.scale(le,le),P.strokeStyle="rgba(255,255,255,0.03)",P.lineWidth=1/le;for(let I=0;I<r/le;I+=50)P.beginPath(),P.moveTo(I,0),P.lineTo(I,s/le),P.stroke();for(let I=0;I<s/le;I+=50)P.beginPath(),P.moveTo(0,I),P.lineTo(r/le,I),P.stroke();for(const[I,D]of x){const de=g[I],j=g[D],W=j.x-de.x,H=j.y-de.y,te=Math.sqrt(W*W+H*H)||1,pe=v===I||v===D;P.beginPath(),P.moveTo(de.x+W/te*de.r,de.y+H/te*de.r);const he=j.x-W/te*j.r,ke=j.y-H/te*j.r;P.lineTo(he,ke),P.strokeStyle=pe?"rgba(139,180,250,0.9)":"rgba(255,255,255,0.15)",P.lineWidth=pe?3:1,P.stroke();const Dt=pe?12:6,Bt=Math.atan2(H,W);P.beginPath(),P.moveTo(he,ke),P.lineTo(he-Dt*Math.cos(Bt-.4),ke-Dt*Math.sin(Bt-.4)),P.lineTo(he-Dt*Math.cos(Bt+.4),ke-Dt*Math.sin(Bt+.4)),P.closePath(),P.fillStyle=P.strokeStyle,P.fill()}for(let I=0;I<g.length;I++){const D=g[I],de=I===v,j=de?D.r+4:D.r;de&&(P.beginPath(),P.arc(D.x,D.y,j+8,0,Math.PI*2),P.fillStyle="rgba(255,255,255,0.08)",P.fill()),P.beginPath(),P.arc(D.x,D.y,j,0,Math.PI*2),P.fillStyle=D.color,P.globalAlpha=de?1:.85,P.fill(),P.globalAlpha=1,P.strokeStyle=de?"rgba(255,255,255,0.6)":"rgba(255,255,255,0.25)",P.lineWidth=de?2.5:1.5,P.stroke();const W=D.f.instability;O&&typeof W=="number"&&(D.f.coupling_afferent||0)+(D.f.coupling_efferent||0)>0&&(P.beginPath(),P.arc(D.x,D.y,j+3,0,Math.PI*2),P.strokeStyle=`hsl(${Math.round(210-180*W)},85%,60%)`,P.lineWidth=1+W*2.5,P.stroke());const H=((G=D.f.path.split("/").pop())==null?void 0:G.replace(/\.\w+$/,""))||"?";if(j>16){const he=Math.max(8,Math.min(13,j*.38));P.fillStyle="#fff",P.font=`600 ${he}px monospace`,P.textAlign="center",P.fillText(H,D.x,D.y-2),P.fillStyle="rgba(255,255,255,0.65)",P.font=`${he-1}px monospace`,P.fillText(`${D.f.loc} LOC`,D.x,D.y+he)}const te=Math.max(9,j*.3),pe=te*.7;if(j>14&&D.f.dep_count>0){const he=D.y-j+pe+3;P.beginPath(),P.arc(D.x,he,pe,0,Math.PI*2),P.fillStyle="#ea580c",P.fill(),P.fillStyle="#fff",P.font=`bold ${te}px sans-serif`,P.textAlign="center",P.fillText("D",D.x,he+te*.35)}if(j>14&&D.f.
|
|
241
|
+
`,document.getElementById("metrics-canvas-wrap").appendChild(A),(gi=document.getElementById("metrics-zoom-in"))==null||gi.addEventListener("click",()=>{le=Math.min(5,le*1.3)}),($n=document.getElementById("metrics-zoom-out"))==null||$n.addEventListener("click",()=>{le=Math.max(.3,le*.7)}),(ps=document.getElementById("metrics-zoom-fit"))==null||ps.addEventListener("click",()=>{le=1,U=0,oe=0});const P=w.getContext("2d");let v=-1,k=-1,X=0,z=0,U=0,oe=0,le=1,rt=!1,mn=0,Gt=0,us=0,ft=0;function De(){for(let j=0;j<g.length;j++){if(j===k)continue;const W=g[j],H=m-W.x,te=$-W.y,pe=.3+W.r/d*.7,he=O?.3+(W.f.coupling_afferent||0)/l*.7:pe,ke=.008*pe*he;W.vx+=H*ke,W.vy+=te*ke}for(const[j,W]of x){const H=g[j],te=g[W],pe=te.x-H.x,he=te.y-H.y,ke=Math.sqrt(pe*pe+he*he)||1,Dt=H.r+te.r+20,Bt=(ke-Dt)*.002,ms=pe/ke*Bt,$s=he/ke*Bt;j!==k&&(H.vx+=ms,H.vy+=$s),W!==k&&(te.vx-=ms,te.vy-=$s)}for(let j=0;j<g.length;j++)for(let W=j+1;W<g.length;W++){const H=g[j],te=g[W],pe=te.x-H.x,he=te.y-H.y,ke=Math.sqrt(pe*pe+he*he)||1,Dt=H.r+te.r+20;if(ke<Dt){const Bt=40*(Dt-ke)/Dt,ms=pe/ke*Bt,$s=he/ke*Bt;j!==k&&(H.vx-=ms,H.vy-=$s),W!==k&&(te.vx+=ms,te.vy+=$s)}}for(let j=0;j<g.length;j++){if(j===k)continue;const W=g[j];W.vx*=.65,W.vy*=.65;const H=2;W.vx=Math.max(-H,Math.min(H,W.vx)),W.vy=Math.max(-H,Math.min(H,W.vy)),W.x+=W.vx,W.y+=W.vy,W.x=Math.max(W.r+2,Math.min(r-W.r-2,W.x)),W.y=Math.max(W.r+25,Math.min(s-W.r-2,W.y))}}function st(){var G;De(),P.clearRect(0,0,r,s),P.save(),P.translate(U,oe),P.scale(le,le),P.strokeStyle="rgba(255,255,255,0.03)",P.lineWidth=1/le;for(let I=0;I<r/le;I+=50)P.beginPath(),P.moveTo(I,0),P.lineTo(I,s/le),P.stroke();for(let I=0;I<s/le;I+=50)P.beginPath(),P.moveTo(0,I),P.lineTo(r/le,I),P.stroke();for(const[I,D]of x){const de=g[I],j=g[D],W=j.x-de.x,H=j.y-de.y,te=Math.sqrt(W*W+H*H)||1,pe=v===I||v===D;P.beginPath(),P.moveTo(de.x+W/te*de.r,de.y+H/te*de.r);const he=j.x-W/te*j.r,ke=j.y-H/te*j.r;P.lineTo(he,ke),P.strokeStyle=pe?"rgba(139,180,250,0.9)":"rgba(255,255,255,0.15)",P.lineWidth=pe?3:1,P.stroke();const Dt=pe?12:6,Bt=Math.atan2(H,W);P.beginPath(),P.moveTo(he,ke),P.lineTo(he-Dt*Math.cos(Bt-.4),ke-Dt*Math.sin(Bt-.4)),P.lineTo(he-Dt*Math.cos(Bt+.4),ke-Dt*Math.sin(Bt+.4)),P.closePath(),P.fillStyle=P.strokeStyle,P.fill()}for(let I=0;I<g.length;I++){const D=g[I],de=I===v,j=de?D.r+4:D.r;de&&(P.beginPath(),P.arc(D.x,D.y,j+8,0,Math.PI*2),P.fillStyle="rgba(255,255,255,0.08)",P.fill()),P.beginPath(),P.arc(D.x,D.y,j,0,Math.PI*2),P.fillStyle=D.color,P.globalAlpha=de?1:.85,P.fill(),P.globalAlpha=1,P.strokeStyle=de?"rgba(255,255,255,0.6)":"rgba(255,255,255,0.25)",P.lineWidth=de?2.5:1.5,P.stroke();const W=D.f.instability;O&&typeof W=="number"&&(D.f.coupling_afferent||0)+(D.f.coupling_efferent||0)>0&&(P.beginPath(),P.arc(D.x,D.y,j+3,0,Math.PI*2),P.strokeStyle=`hsl(${Math.round(210-180*W)},85%,60%)`,P.lineWidth=1+W*2.5,P.stroke());const H=((G=D.f.path.split("/").pop())==null?void 0:G.replace(/\.\w+$/,""))||"?";if(j>16){const he=Math.max(8,Math.min(13,j*.38));P.fillStyle="#fff",P.font=`600 ${he}px monospace`,P.textAlign="center",P.fillText(H,D.x,D.y-2),P.fillStyle="rgba(255,255,255,0.65)",P.font=`${he-1}px monospace`,P.fillText(`${D.f.loc} LOC`,D.x,D.y+he)}const te=Math.max(9,j*.3),pe=te*.7;if(j>14&&D.f.dep_count>0){const he=D.y-j+pe+3;P.beginPath(),P.arc(D.x,he,pe,0,Math.PI*2),P.fillStyle="#ea580c",P.fill(),P.fillStyle="#fff",P.font=`bold ${te}px sans-serif`,P.textAlign="center",P.fillText("D",D.x,he+te*.35)}if(j>14&&D.f.has_referencing_test){const he=D.y+j-pe-3;P.beginPath(),P.arc(D.x,he,pe,0,Math.PI*2),P.fillStyle="#16a34a",P.fill(),P.fillStyle="#fff",P.font=`bold ${te}px sans-serif`,P.textAlign="center",P.fillText("T",D.x,he+te*.35)}}if(P.restore(),v>=0){const I=g[v].f,D=I.coupling_afferent??0,de=I.coupling_efferent??0,j=typeof I.instability=="number"?I.instability.toFixed(2):"-";_.textContent=`${I.path} ca ${D} / ce ${de} / I ${j} / MI ${I.maintainability}`}else _.textContent&&(_.textContent="");requestAnimationFrame(st)}w.addEventListener("mousemove",G=>{const I=w.getBoundingClientRect(),D=(G.clientX-I.left-U)/le,de=(G.clientY-I.top-oe)/le;if(rt){U=us+(G.clientX-mn),oe=ft+(G.clientY-Gt);return}if(k>=0){ut=!0,g[k].x=D+X,g[k].y=de+z,g[k].vx=0,g[k].vy=0;return}v=-1;for(let j=g.length-1;j>=0;j--){const W=g[j],H=D-W.x,te=de-W.y;if(Math.sqrt(H*H+te*te)<W.r+4){v=j;break}}w.style.cursor=v>=0?"grab":"default"}),w.addEventListener("mousedown",G=>{const I=w.getBoundingClientRect(),D=(G.clientX-I.left-U)/le,de=(G.clientY-I.top-oe)/le;if(G.button===2){rt=!0,mn=G.clientX,Gt=G.clientY,us=U,ft=oe,w.style.cursor="move";return}v>=0&&(k=v,X=g[k].x-D,z=g[k].y-de,ut=!1,w.style.cursor="grabbing")});let ut=!1;w.addEventListener("mouseup",G=>{if(rt){rt=!1,w.style.cursor="default";return}if(k>=0){ut||Xa(g[k].f.path),w.style.cursor="grab",k=-1,ut=!1;return}}),w.addEventListener("mouseleave",()=>{v=-1,k=-1,rt=!1}),w.addEventListener("dblclick",G=>{const I=w.getBoundingClientRect(),D=(G.clientX-I.left-U)/le,de=(G.clientY-I.top-oe)/le;for(let j=g.length-1;j>=0;j--){const W=g[j],H=D-W.x,te=de-W.y;if(Math.sqrt(H*H+te*te)<W.r+4){Xa(W.f.path);break}}}),w.addEventListener("contextmenu",G=>G.preventDefault()),requestAnimationFrame(st)}async function Xa(i){const e=document.getElementById("metrics-detail");if(!e)return;e.innerHTML='<p class="text-muted">Loading file analysis...</p>';const t=await L("/metrics/file?path="+encodeURIComponent(i));if(t.error){e.innerHTML=`<p style="color:var(--danger)">${Q(t.error)}</p>`;return}const n=t.functions||[],r=Math.max(1,...n.map(s=>s.complexity));e.innerHTML=`
|
|
242
242
|
<div style="background:var(--surface);border:1px solid var(--border);border-radius:0.5rem;padding:1rem">
|
|
243
243
|
<div class="flex items-center" style="justify-content:space-between;margin-bottom:0.75rem">
|
|
244
244
|
<h3 style="font-size:0.9rem">${Q(t.path)}</h3>
|
|
@@ -15,7 +15,7 @@ export class MetricsEngineError extends Error {
|
|
|
15
15
|
}
|
|
16
16
|
const INSTALL_HINT = "update the native tina4 CLI: https://tina4.com/cli";
|
|
17
17
|
const SUMMARY_KEYS = ["files_analyzed", "total_functions", "avg_complexity", "avg_maintainability"];
|
|
18
|
-
const FILE_KEYS = ["path", "loc", "avg_complexity", "maintainability", "
|
|
18
|
+
const FILE_KEYS = ["path", "loc", "avg_complexity", "maintainability", "has_referencing_test"];
|
|
19
19
|
const FUNCTION_KEYS = ["name", "file", "line", "complexity", "loc"];
|
|
20
20
|
|
|
21
21
|
function containsTypeScript(directory: string): boolean {
|
|
@@ -501,62 +501,59 @@ function splitOutsideQuotes(expr, sep2) {
|
|
|
501
501
|
parts.push(expr.slice(currentStart));
|
|
502
502
|
return parts;
|
|
503
503
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
if (expr.length
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
504
|
+
var EXPR_NOT_MATCHED = Symbol("frond-expression-not-matched");
|
|
505
|
+
function parenthesizedInner(expr) {
|
|
506
|
+
if (expr.length < 2 || expr[0] !== "(" || !expr.endsWith(")")) return null;
|
|
507
|
+
let depth = 0;
|
|
508
|
+
for (let index = 0; index < expr.length; index++) {
|
|
509
|
+
if (expr[index] === "(") depth++;
|
|
510
|
+
else if (expr[index] === ")") depth--;
|
|
511
|
+
if (depth === 0 && index < expr.length - 1) return null;
|
|
511
512
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (depth === 0 && pi < expr.length - 1) {
|
|
519
|
-
matched = false;
|
|
520
|
-
break;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
if (matched) {
|
|
524
|
-
return evalExpr(expr.slice(1, -1), context);
|
|
525
|
-
}
|
|
513
|
+
return expr.slice(1, -1);
|
|
514
|
+
}
|
|
515
|
+
function evalPrimary(expr, context) {
|
|
516
|
+
const quote = expr[0];
|
|
517
|
+
if (expr.length >= 2 && (quote === '"' || quote === "'") && expr.endsWith(quote) && !expr.slice(1, -1).includes(quote)) {
|
|
518
|
+
return expr.slice(1, -1);
|
|
526
519
|
}
|
|
520
|
+
const inner = parenthesizedInner(expr);
|
|
521
|
+
if (inner !== null) return evalExpr(inner, context);
|
|
522
|
+
return EXPR_NOT_MATCHED;
|
|
523
|
+
}
|
|
524
|
+
function evalTernaryExpression(expr, context) {
|
|
527
525
|
const ternaryIdx = findTernary(expr);
|
|
528
|
-
if (ternaryIdx
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
}
|
|
526
|
+
if (ternaryIdx === -1) return EXPR_NOT_MATCHED;
|
|
527
|
+
const rest = expr.slice(ternaryIdx + 1);
|
|
528
|
+
const colonIdx = findColon(rest);
|
|
529
|
+
if (colonIdx === -1) return EXPR_NOT_MATCHED;
|
|
530
|
+
const condition = evalExpr(expr.slice(0, ternaryIdx).trim(), context);
|
|
531
|
+
const branch = condition ? rest.slice(0, colonIdx) : rest.slice(colonIdx + 1);
|
|
532
|
+
return evalExpr(branch.trim(), context);
|
|
533
|
+
}
|
|
534
|
+
function evalInlineIfExpression(expr, context) {
|
|
539
535
|
const ifIdx = findOutsideQuotes(expr, " if ");
|
|
540
|
-
if (ifIdx
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
}
|
|
549
|
-
}
|
|
536
|
+
if (ifIdx < 0) return EXPR_NOT_MATCHED;
|
|
537
|
+
const elseIdx = findOutsideQuotes(expr, " else ");
|
|
538
|
+
if (elseIdx < 0 || elseIdx <= ifIdx) return EXPR_NOT_MATCHED;
|
|
539
|
+
const condition = evalExpr(expr.slice(ifIdx + 4, elseIdx).trim(), context);
|
|
540
|
+
const branch = condition ? expr.slice(0, ifIdx) : expr.slice(elseIdx + 6);
|
|
541
|
+
return evalExpr(branch.trim(), context);
|
|
542
|
+
}
|
|
543
|
+
function evalCoalesceExpression(expr, context) {
|
|
550
544
|
const qqIdx = findOutsideQuotes(expr, "??");
|
|
551
|
-
if (qqIdx
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
return
|
|
545
|
+
if (qqIdx === -1) return EXPR_NOT_MATCHED;
|
|
546
|
+
const value = evalExpr(expr.slice(0, qqIdx).trim(), context);
|
|
547
|
+
return value === null || value === void 0 ? evalExpr(expr.slice(qqIdx + 2).trim(), context) : value;
|
|
548
|
+
}
|
|
549
|
+
function evalConditional(expr, context) {
|
|
550
|
+
for (const evaluator of [evalTernaryExpression, evalInlineIfExpression, evalCoalesceExpression]) {
|
|
551
|
+
const result = evaluator(expr, context);
|
|
552
|
+
if (result !== EXPR_NOT_MATCHED) return result;
|
|
559
553
|
}
|
|
554
|
+
return EXPR_NOT_MATCHED;
|
|
555
|
+
}
|
|
556
|
+
function evalConcatOrComparison(expr, context) {
|
|
560
557
|
if (findOutsideQuotes(expr, "~") >= 0) {
|
|
561
558
|
const parts = splitOutsideQuotes(expr, "~");
|
|
562
559
|
if (parts.length > 1) {
|
|
@@ -574,6 +571,18 @@ function evalExpr(expr, context) {
|
|
|
574
571
|
return evalComparison(expr, context);
|
|
575
572
|
}
|
|
576
573
|
}
|
|
574
|
+
return EXPR_NOT_MATCHED;
|
|
575
|
+
}
|
|
576
|
+
var ARITHMETIC_OPERATIONS = {
|
|
577
|
+
"+": (left, right) => left + right,
|
|
578
|
+
"-": (left, right) => left - right,
|
|
579
|
+
"*": (left, right) => left * right,
|
|
580
|
+
"//": (left, right) => right !== 0 ? Math.floor(left / right) : 0,
|
|
581
|
+
"/": (left, right) => right !== 0 ? left / right : 0,
|
|
582
|
+
"%": (left, right) => right !== 0 ? left % right : 0,
|
|
583
|
+
"**": (left, right) => left ** right
|
|
584
|
+
};
|
|
585
|
+
function evalArithmeticExpression(expr, context) {
|
|
577
586
|
for (const op of [" + ", " - ", " * ", " // ", " / ", " % ", " ** "]) {
|
|
578
587
|
const pos = findOutsideQuotes(expr, op);
|
|
579
588
|
if (pos >= 0) {
|
|
@@ -586,40 +595,15 @@ function evalExpr(expr, context) {
|
|
|
586
595
|
let rNum = rVal != null ? Number(rVal) : 0;
|
|
587
596
|
if (isNaN(lNum)) lNum = 0;
|
|
588
597
|
if (isNaN(rNum)) rNum = 0;
|
|
589
|
-
|
|
590
|
-
const bothInt = Number.isInteger(lNum) && Number.isInteger(rNum) && opS !== "/";
|
|
591
|
-
let result;
|
|
592
|
-
switch (opS) {
|
|
593
|
-
case "+":
|
|
594
|
-
result = lNum + rNum;
|
|
595
|
-
break;
|
|
596
|
-
case "-":
|
|
597
|
-
result = lNum - rNum;
|
|
598
|
-
break;
|
|
599
|
-
case "*":
|
|
600
|
-
result = lNum * rNum;
|
|
601
|
-
break;
|
|
602
|
-
case "//":
|
|
603
|
-
result = rNum !== 0 ? Math.floor(lNum / rNum) : 0;
|
|
604
|
-
break;
|
|
605
|
-
case "/":
|
|
606
|
-
result = rNum !== 0 ? lNum / rNum : 0;
|
|
607
|
-
break;
|
|
608
|
-
case "%":
|
|
609
|
-
result = rNum !== 0 ? lNum % rNum : 0;
|
|
610
|
-
break;
|
|
611
|
-
case "**":
|
|
612
|
-
result = lNum ** rNum;
|
|
613
|
-
break;
|
|
614
|
-
default:
|
|
615
|
-
result = 0;
|
|
616
|
-
}
|
|
617
|
-
return bothInt && Number.isInteger(result) ? result : result;
|
|
598
|
+
return ARITHMETIC_OPERATIONS[op.trim()](lNum, rNum);
|
|
618
599
|
} catch {
|
|
619
600
|
return null;
|
|
620
601
|
}
|
|
621
602
|
}
|
|
622
603
|
}
|
|
604
|
+
return EXPR_NOT_MATCHED;
|
|
605
|
+
}
|
|
606
|
+
function evalFilterExpression(expr, context) {
|
|
623
607
|
if (findOutsideQuotes(expr, "|") >= 0) {
|
|
624
608
|
const [baseExpr, filters] = parseFilterChain(expr);
|
|
625
609
|
if (filters.length > 0) {
|
|
@@ -638,38 +622,58 @@ function evalExpr(expr, context) {
|
|
|
638
622
|
return value;
|
|
639
623
|
}
|
|
640
624
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
625
|
+
return EXPR_NOT_MATCHED;
|
|
626
|
+
}
|
|
627
|
+
function evaluateCallArgs(rawArgs, context) {
|
|
628
|
+
return rawArgs.trim() ? splitArgs(rawArgs).map((arg) => evalExpr(arg.trim(), context)) : [];
|
|
629
|
+
}
|
|
630
|
+
function evalDottedFunction(name, rawArgs, context) {
|
|
631
|
+
const lastDot = name.lastIndexOf(".");
|
|
632
|
+
const owner = resolveVar(name.slice(0, lastDot), context);
|
|
633
|
+
const member = name.slice(lastDot + 1);
|
|
634
|
+
if (!owner || typeof owner !== "object" || !(member in owner)) {
|
|
635
|
+
return EXPR_NOT_MATCHED;
|
|
636
|
+
}
|
|
637
|
+
const method = owner[member];
|
|
638
|
+
return typeof method === "function" ? method.apply(owner, evaluateCallArgs(rawArgs, context)) : EXPR_NOT_MATCHED;
|
|
639
|
+
}
|
|
640
|
+
function evalFunctionExpression(expr, context) {
|
|
641
|
+
const match = expr.match(FN_CALL_RE);
|
|
642
|
+
if (!match) return EXPR_NOT_MATCHED;
|
|
643
|
+
const name = match[1];
|
|
644
|
+
const rawArgs = match[2] || "";
|
|
645
|
+
if (name.includes(".")) return evalDottedFunction(name, rawArgs, context);
|
|
646
|
+
const fn = context[name] ?? resolveVar(name, context);
|
|
647
|
+
if (typeof fn === "function") return fn(...evaluateCallArgs(rawArgs, context));
|
|
648
|
+
return EXPR_NOT_MATCHED;
|
|
649
|
+
}
|
|
650
|
+
var EXPR_EVALUATORS = [
|
|
651
|
+
evalPrimary,
|
|
652
|
+
evalConditional,
|
|
653
|
+
evalConcatOrComparison,
|
|
654
|
+
evalArithmeticExpression,
|
|
655
|
+
evalFilterExpression,
|
|
656
|
+
evalFunctionExpression
|
|
657
|
+
];
|
|
658
|
+
var expressionFormCache = /* @__PURE__ */ new Map();
|
|
659
|
+
function evalExpr(expr, context) {
|
|
660
|
+
expr = expr.trim();
|
|
661
|
+
const cachedForm = expressionFormCache.get(expr);
|
|
662
|
+
if (cachedForm !== void 0) {
|
|
663
|
+
if (cachedForm === -1) return resolveVar(expr, context);
|
|
664
|
+
const result = EXPR_EVALUATORS[cachedForm](expr, context);
|
|
665
|
+
return result === EXPR_NOT_MATCHED ? resolveVar(expr, context) : result;
|
|
666
|
+
}
|
|
667
|
+
for (let index = 0; index < EXPR_EVALUATORS.length; index++) {
|
|
668
|
+
const result = EXPR_EVALUATORS[index](expr, context);
|
|
669
|
+
if (result !== EXPR_NOT_MATCHED) {
|
|
670
|
+
capCache(expressionFormCache, MEMO_CACHE_MAX);
|
|
671
|
+
expressionFormCache.set(expr, index);
|
|
672
|
+
return result;
|
|
671
673
|
}
|
|
672
674
|
}
|
|
675
|
+
capCache(expressionFormCache, MEMO_CACHE_MAX);
|
|
676
|
+
expressionFormCache.set(expr, FN_CALL_RE.test(expr) ? EXPR_EVALUATORS.length - 1 : -1);
|
|
673
677
|
return resolveVar(expr, context);
|
|
674
678
|
}
|
|
675
679
|
function findTernary(expr) {
|