web_plsql 1.3.1 → 1.3.2

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.
@@ -144,4 +144,4 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
144
144
  <td class="text-right font-mono">${c}</td>
145
145
  </tr>
146
146
  `}).join("")}catch(n){console.error("Failed to load stats history:",n),t&&(t.textContent="Failed to load stats")}}async function tp(){const e=document.getElementById("access-filter"),t=document.getElementById("access-limit"),i=e?.value??"",r=t?Number.parseInt(t.value):100,n=await $e.getAccessLogs(r,i),o=document.getElementById("access-log-view"),a=document.getElementById("access-log-range");if(!o)return;let s=0;Array.isArray(n)?(o.textContent=n.join(`
147
- `),s=n.length):n&&typeof n=="object"&&"message"in n?(o.textContent=n.message,s=0):(o.textContent="No logs available",s=0),a&&(a.textContent=s>0?`Showing last ${s} log entries`:"No logs available"),o.parentElement&&(o.parentElement.scrollTop=o.parentElement.scrollHeight)}async function Xt(){const e=document.getElementById("trace-filter"),t=document.getElementById("trace-limit"),i=document.getElementById("trace-status-toggle"),r=e?.value??"",n=t?Number.parseInt(t.value):100;try{const{enabled:c}=await $e.getTraceStatus();i&&(i.textContent=c?"Tracing: ON":"Tracing: OFF",i.dataset.enabled=String(c),i.className=`px-4 py-2 rounded-lg text-sm font-bold transition-colors ${c?"bg-success text-black hover:bg-success/80":"bg-white/10 text-white hover:bg-white/20"}`)}catch(c){console.error("Failed to get trace status",c)}const o=await $e.getTraceLogs(n,r),a=[{id:"timestamp",header:"Timestamp",headerTitle:"Time of request",width:"1%",accessor:c=>new Date(c.timestamp).toLocaleString(),className:"font-mono text-xs text-dim whitespace-nowrap"},{id:"source",header:"Source",headerTitle:"Request source (IP/User)",width:"1%",accessor:c=>c.source,className:"text-dim text-xs truncate max-w-[150px]",cellTitle:c=>c.source},{id:"method",header:"Method",headerTitle:"HTTP Method",width:"1%",accessor:c=>c.method,className:"font-mono text-xs whitespace-nowrap"},{id:"url",header:"Request URL",headerTitle:"Request URL",accessor:c=>c.url,className:"text-bright font-mono text-xs truncate max-w-[800px]",cellTitle:c=>c.url},{id:"status",header:"Status",headerTitle:"HTTP Status Code",width:"1%",accessor:c=>c.status,className:c=>`font-bold text-xs uppercase whitespace-nowrap ${c.status==="success"?"text-success":"text-danger"}`},{id:"duration",header:"Duration",headerTitle:"Execution time in ms",width:"1%",align:"right",accessor:c=>$s(c.duration),className:"font-mono text-xs text-dim whitespace-nowrap"}];new ep("trace-table",{columns:a,dense:!0,tableLayout:"auto",onRowClick:c=>{const l=document.getElementById("trace-modal"),u=document.getElementById("trace-detail-content");l&&u&&(u.textContent=JSON.stringify(c,null,2),l.style.display="flex")}}).render(o)}function np(e){const t=document.getElementById("pools-container");!t||!e.pools||(t.innerHTML=e.pools.map(i=>wS(i)).join(""))}function ip(e){const t=e.status.config;if(!t)return;const i=document.getElementById("config-view");i&&(i.innerHTML=TS(t))}function Jr(e){if(e===0)return"0 B";const t=1024,i=["B","KB","MB","GB"],r=Math.floor(Math.log(e)/Math.log(t));return`${Number.parseFloat((e/Math.pow(t,r)).toFixed(2))} ${i[r]}`}function sl(e){return $s(e/1e3)}function ks(e,t){const i=document.getElementById("middleware-version");i&&e.version&&(i.textContent=`${e.version} (2/18/2026, 7:14:55 PM)`,i.title="The version of the web_plsql gateway and admin console build time");const r=document.getElementById("node-version");r&&e.system&&(r.textContent=e.system.nodeVersion,r.title="The version of the Node.js runtime");const n=document.getElementById("platform-info");n&&e.system&&(n.textContent=`${e.system.platform} (${e.system.arch})`,n.title="The operating system and architecture");const o=document.getElementById("system-uptime");o&&e.uptime&&(o.textContent=_s(e.uptime));const a=document.getElementById("system-start-time");a&&e.startTime&&(a.textContent=Qg(e.startTime));const s=document.getElementById("system-status-text");s&&(s.textContent=e.status?.toUpperCase()??"-");const c=document.getElementById("total-requests");c&&e.metrics&&(c.textContent=e.metrics.requestCount.toLocaleString(),c.title="Cumulative number of requests handled by the server");const l=document.getElementById("total-errors");l&&e.metrics&&(l.textContent=e.metrics.errorCount.toLocaleString(),l.title="Cumulative number of failed requests");const u=document.getElementById("active-pools");if(u&&e.pools&&(u.textContent=e.pools.length.toString(),u.title="Number of database connection pools currently active"),e.system){const d=e.system,f={heapUsed:d.memory.heapUsed,heapTotal:d.memory.heapTotal,rss:d.memory.rss,external:d.memory.external,cpuUser:d.cpu.user,cpuSystem:d.cpu.system},h=(_,k,x)=>{const y=f[k],z=d.memory[x],T=document.getElementById(_),I=document.getElementById(`${_}-max`);T&&(T.textContent=Jr(y)),I&&typeof z=="number"&&(I.textContent=Jr(z))},g=(_,k,x)=>{const y=f[k],z=document.getElementById(_);if(z&&(z.textContent=sl(y)),x){const T=d.cpu[x],I=document.getElementById(`${_}-max`);I&&typeof T=="number"&&(I.textContent=sl(T))}};h("memory-heap-used","heapUsed","heapUsedMax"),h("memory-heap-total","heapTotal","heapTotalMax"),h("memory-rss","rss","rssMax"),h("memory-external","external","externalMax"),g("cpu-user","cpuUser","userMax"),g("cpu-system","cpuSystem","systemMax");const m=document.getElementById("cpu-percent");if(m&&d.cpu.max!==void 0){const _=d.cpu.max;m.textContent=`${_.toFixed(1)}%`,m.title="Max System CPU usage"}const p=document.getElementById("memory-percent");if(p&&d.memory.totalMemory){const _=d.memory.rss/d.memory.totalMemory*100;p.textContent=`${_.toFixed(1)}%`,p.title=`System Memory Used relative to Total (${Jr(d.memory.totalMemory)})`}const b=document.getElementById("cpu-cores-info");b&&d.cpuCores&&(b.textContent=`${d.cpuCores} Cores`)}}async function Qn(e,t){const i=e.querySelector(".material-symbols-rounded"),r=i?.textContent;e.disabled=!0,e.classList.add("loading"),i&&(i.textContent="sync",i.classList.add("animate-spin"));try{return await t()}finally{e.disabled=!1,e.classList.remove("loading"),i&&(i.textContent=r??"",i.classList.remove("animate-spin"))}}function ot(e,t){const i=document.getElementById(e);return i&&i.addEventListener("click",()=>{Qn(i,t)}),i}const OS={overview:"dashboard",trace:"bolt",errors:"error",access:"list_alt",pools:"database",stats:"query_stats",config:"settings",system:"terminal"},O={currentView:"overview",status:{},maxHistoryPoints:30,lastRequestCount:0,lastErrorCount:0,lastUpdateTime:Date.now(),lastBucketTimestamp:0,nextRefreshTimeout:null,nextRefreshTime:0,countdownInterval:null,history:{labels:[],requests:[],avgResponseTimes:[],p95ResponseTimes:[],p99ResponseTimes:[],cpuUsage:[],memoryUsage:[],poolUsage:{}},charts:{},metricsMin:{},metricsMax:{}},at={REFRESH_INTERVAL:"admin_refresh_interval",HISTORY_DURATION:"admin_history_duration",AUTO_REFRESH:"admin_auto_refresh",THEME:"theme",LAST_VIEW:"admin_last_view"};function rp(e){console.error("Connection lost:",e);const t=document.getElementById("server-status-dot"),i=document.getElementById("server-status-text");t&&(t.className="dot offline"),i&&(i.textContent="OFFLINE");const r=document.getElementById("btn-pause");r&&(r.style.display="none");const n=document.getElementById("btn-resume");n&&(n.style.display="none");const o=document.querySelector("#system-status .dot"),a=document.getElementById("system-status-text");o&&(o.className="dot offline"),a&&(a.textContent="OFFLINE");const s=document.getElementById("offline-modal");s&&s.style.display!=="flex"&&(s.style.display="flex"),Ai(6e4),op(6e4)}function op(e){O.countdownInterval&&clearInterval(O.countdownInterval);const t=Date.now()+e,i=document.getElementById("offline-countdown"),r=document.getElementById("reconnect-text");r&&(r.textContent="Try Reconnect");const n=()=>{if(!i)return;const o=Math.max(0,Math.ceil((t-Date.now())/1e3));i.textContent=`Retrying in ${o}s...`,o<=0&&O.countdownInterval&&(clearInterval(O.countdownInterval),O.countdownInterval=null)};n(),O.countdownInterval=setInterval(n,1e3)}function DS(){O.countdownInterval&&(clearInterval(O.countdownInterval),O.countdownInterval=null);const e=document.getElementById("offline-countdown");e&&(e.textContent="")}async function qe(e=!1,t=!1){const i=document.getElementById("reconnect-spinner"),r=document.getElementById("reconnect-icon"),n=document.getElementById("reconnect-text");i&&(i.style.display="inline-block"),r&&(r.style.display="none"),n&&(n.textContent="Connecting...");try{const o=Date.now(),a=await $e.getStatus(e,t),s=document.getElementById("offline-modal");if(s?.style.display==="flex"&&s&&(s.style.display="none",DS()),i&&(i.style.display="none"),r&&(r.style.display="inline-block"),n&&(n.textContent="Try Reconnect"),a.history&&O.history.labels.length===0&&_S(O,a.history),!a.config&&O.status.config&&(a.config=O.status.config),O.status=a,!a.metrics||!a.pools)return;const l=a.history?.at(-1),u=(a.intervalMs??Yg)/1e3,d=l?l.requests/u:0,f=l?l.durationAvg:a.metrics.avgResponseTime;if(O.lastRequestCount=a.metrics.requestCount,O.lastErrorCount=a.metrics.errorCount,O.lastUpdateTime=o,a.system){const U=a.system,Q={heapUsed:U.memory.heapUsed,heapTotal:U.memory.heapTotal,rss:U.memory.rss,external:U.memory.external,cpuUser:U.cpu.user,cpuSystem:U.cpu.system};$S(Q,O.metricsMin,O.metricsMax)}if(l&&l.timestamp>O.lastBucketTimestamp){O.lastBucketTimestamp=l.timestamp;const U=new Date(l.timestamp).toLocaleTimeString();ol(O,U,d,f,a.pools)}else if(!l&&O.history.labels.length===0){const U=new Date().toLocaleTimeString();ol(O,U,0,0,a.pools)}const h=document.getElementById("sidebar-version");h&&(h.textContent=`v${a.version}`);const g=document.getElementById("uptime-val");g&&(g.textContent=_s(a.uptime));const m=document.getElementById("start-time-val");m&&(m.textContent=`Started: ${Qg(a.startTime)}`);const p=document.getElementById("req-per-sec");p&&(p.textContent=d.toFixed(2));const b=document.getElementById("max-req-per-sec");b&&(b.textContent=a.metrics.maxRequestsPerSecond.toFixed(2));const _=document.getElementById("avg-resp-time");_&&(_.textContent=`${a.metrics.avgResponseTime.toFixed(1)}ms`);const k=document.getElementById("max-resp-time");k&&(k.textContent=`${a.metrics.maxResponseTime.toFixed(1)}ms`);const x=document.getElementById("err-count");x&&(x.textContent=a.metrics.errorCount.toLocaleString());let y=0,z=0;a.pools.forEach(U=>{U.cache&&(y+=U.cache.procedureName.hits+U.cache.argument.hits,z+=U.cache.procedureName.misses+U.cache.argument.misses)});const T=y+z,I=T>0?Math.round(y/T*100):0,E=document.getElementById("cache-hit-rate-val");E&&(E.textContent=`${I}%`,E.style.color=I>80?"var(--success)":I>50?"var(--warning)":"var(--danger)");const R=document.getElementById("cache-hits-val");R&&(R.textContent=y.toLocaleString());const A=document.getElementById("cache-misses-val");A&&(A.textContent=z.toLocaleString());const F=document.getElementById("server-status-dot"),ie=document.getElementById("server-status-text");F&&(F.className="dot "+a.status),ie&&(ie.textContent=a.status.toUpperCase());const se=document.getElementById("btn-pause");se&&(se.style.display=a.status==="running"?"flex":"none");const W=document.getElementById("btn-resume");if(W&&(W.style.display=a.status==="paused"?"flex":"none"),O.currentView==="errors"&&await Cr(),O.currentView==="trace"&&await Xt(),O.currentView==="pools"&&np(a),O.currentView==="config"&&ip(O),O.currentView==="system"&&ks(a,O),Vt?.checked&&Ue){const U=Number.parseInt(Ue.value);Ai(U)}}catch(o){document.getElementById("offline-modal")?.style.display==="flex"?(Ai(6e4),op(6e4),i&&(i.style.display="none"),r&&(r.style.display="inline-block"),n&&(n.textContent="Try Reconnect")):rp(o)}}function Ai(e){O.nextRefreshTimeout&&(clearTimeout(O.nextRefreshTimeout),O.nextRefreshTimeout=null),O.nextRefreshTime=Date.now()+e,O.nextRefreshTimeout=setTimeout(()=>{qe()},e)}function ap(){const e=document.getElementById("refresh-interval");if(e){const t=Number.parseInt(e.value);Ai(t)}}function PS(){O.nextRefreshTimeout&&(clearTimeout(O.nextRefreshTimeout),O.nextRefreshTimeout=null)}function sp(){const e=document.getElementById("chart-history-points");e&&Array.from(e.options).forEach(t=>{const i=Number.parseInt(t.value);let r="";if(i<60)r=`${i}s`;else if(i<3600)r=`${Math.round(i/60)} min`;else{const n=i/3600;r=n===Math.round(n)?`${n} hour${n>1?"s":""}`:`${n.toFixed(1)} hours`}t.textContent=`Last ${r}`})}document.querySelectorAll("nav button").forEach(e=>{const t=e;t.addEventListener("click",async()=>{try{const i=t.dataset.view;if(!i)return;O.currentView=i,localStorage.setItem(at.LAST_VIEW,i),document.querySelectorAll(".view").forEach(o=>{const a=o;a.style.display="none"});const r=document.getElementById("view-"+i);r&&(r.style.display="block"),document.querySelectorAll("nav button").forEach(o=>o.classList.remove("active")),t.classList.add("active");const n=document.getElementById("view-title");if(n){const a=Array.from(t.childNodes).filter(c=>c.nodeType===Node.TEXT_NODE).map(c=>c.textContent?.trim()).join(""),s=OS[i]??"chevron_right";n.innerHTML=`<span class="material-symbols-rounded">${s}</span>${a}`}i==="errors"&&await Cr(),i==="trace"&&await Xt(),i==="pools"&&np(O.status),i==="stats"&&await xs(),i==="config"&&ip(O),i==="system"&&ks(O.status,O)}catch(i){rp(i)}})});const Vt=document.getElementById("auto-refresh-toggle");Vt&&Vt.addEventListener("change",e=>{const t=e.target;localStorage.setItem(at.AUTO_REFRESH,String(t.checked)),t.checked?ap():PS()});const Ue=document.getElementById("refresh-interval");Ue&&Ue.addEventListener("change",()=>{localStorage.setItem(at.REFRESH_INTERVAL,Ue.value);const e=Number.parseInt(Ue.value),t=Number.parseInt(We?.value??"60");O.maxHistoryPoints=Math.max(1,Math.floor(t/(e/1e3))),O.history.labels=[],O.history.requests=[],O.history.avgResponseTimes=[],O.history.cpuUsage=[],O.history.memoryUsage=[],O.history.poolUsage={},sp(),ks(O.status),qe()});const We=document.getElementById("chart-history-points");We&&We.addEventListener("change",()=>{localStorage.setItem(at.HISTORY_DURATION,We.value);const e=document.getElementById("refresh-interval");if(!e)return;const t=Number.parseInt(e.value),i=Number.parseInt(We.value);O.maxHistoryPoints=Math.max(1,Math.floor(i/(t/1e3))),O.history.labels=[],O.history.requests=[],O.history.avgResponseTimes=[],O.history.cpuUsage=[],O.history.memoryUsage=[],O.history.poolUsage={},qe()});const qr=document.getElementById("manual-refresh");qr&&qr.addEventListener("click",()=>{Qn(qr,qe)});const cl=document.getElementById("error-filter");cl&&cl.addEventListener("keydown",e=>{e.key==="Enter"&&Cr()});ot("error-load-btn",Cr);ot("trace-load-btn",Xt);ot("access-load-btn",tp);ot("stats-load-btn",xs);const ll=document.getElementById("trace-filter");ll&&ll.addEventListener("keydown",e=>{e.key==="Enter"&&Xt()});const Kr=document.getElementById("trace-clear-btn");Kr&&Kr.addEventListener("click",async()=>{confirm("Are you sure you want to clear all traces?")&&await Qn(Kr,async()=>{await $e.clearTraces(),await Xt()})});const Xr=document.getElementById("trace-status-toggle");Xr&&Xr.addEventListener("click",async()=>{await Qn(Xr,async()=>{const{enabled:e}=await $e.getTraceStatus();await $e.toggleTrace(!e),await Xt()})});const ul=document.getElementById("access-filter");ul&&ul.addEventListener("keydown",e=>{e.key==="Enter"&&tp()});const dl=document.getElementById("stats-limit");dl&&dl.addEventListener("keydown",e=>{e.key==="Enter"&&xs()});ot("btn-pause",async()=>{confirm("Are you sure you want to pause the server?")&&(await $e.post("api/server/pause"),await qe())});ot("btn-resume",async()=>{await $e.post("api/server/resume"),await qe()});ot("btn-stop",async()=>{confirm("Are you sure you want to STOP the server? This action cannot be undone from the web interface.")&&(await $e.post("api/server/stop"),alert("Server stop requested. The interface will now become unresponsive."))});ot("btn-clear-all-cache",async()=>{confirm("Are you sure you want to clear ALL caches?")&&(await $e.post("api/cache/clear"),await qe())});const Gr=document.getElementById("btn-reconnect");Gr&&Gr.addEventListener("click",()=>{Qn(Gr,qe)});globalThis.addEventListener("keydown",e=>{if(e.key==="Escape"){const t=document.getElementById("error-modal");t&&(t.style.display="none");const i=document.getElementById("trace-modal");i&&(i.style.display="none")}});const fl=localStorage.getItem(at.REFRESH_INTERVAL);fl&&Ue&&(Ue.value=fl);const hl=localStorage.getItem(at.HISTORY_DURATION);hl&&We&&(We.value=hl);const ml=localStorage.getItem(at.AUTO_REFRESH);ml!==null&&Vt&&(Vt.checked=ml==="true");SS(O);yS(O);sp();if(Ue&&We){const e=Number.parseInt(Ue.value),t=Number.parseInt(We.value);O.maxHistoryPoints=Math.max(1,Math.floor(t/(e/1e3)))}const gl=localStorage.getItem(at.LAST_VIEW);if(gl){const e=document.querySelector(`nav button[data-view="${gl}"]`);e&&e.click()}qe(!0,!0).then(()=>{Vt?.checked&&ap()}).catch(console.error);
147
+ `),s=n.length):n&&typeof n=="object"&&"message"in n?(o.textContent=n.message,s=0):(o.textContent="No logs available",s=0),a&&(a.textContent=s>0?`Showing last ${s} log entries`:"No logs available"),o.parentElement&&(o.parentElement.scrollTop=o.parentElement.scrollHeight)}async function Xt(){const e=document.getElementById("trace-filter"),t=document.getElementById("trace-limit"),i=document.getElementById("trace-status-toggle"),r=e?.value??"",n=t?Number.parseInt(t.value):100;try{const{enabled:c}=await $e.getTraceStatus();i&&(i.textContent=c?"Tracing: ON":"Tracing: OFF",i.dataset.enabled=String(c),i.className=`px-4 py-2 rounded-lg text-sm font-bold transition-colors ${c?"bg-success text-black hover:bg-success/80":"bg-white/10 text-white hover:bg-white/20"}`)}catch(c){console.error("Failed to get trace status",c)}const o=await $e.getTraceLogs(n,r),a=[{id:"timestamp",header:"Timestamp",headerTitle:"Time of request",width:"1%",accessor:c=>new Date(c.timestamp).toLocaleString(),className:"font-mono text-xs text-dim whitespace-nowrap"},{id:"source",header:"Source",headerTitle:"Request source (IP/User)",width:"1%",accessor:c=>c.source,className:"text-dim text-xs truncate max-w-[150px]",cellTitle:c=>c.source},{id:"method",header:"Method",headerTitle:"HTTP Method",width:"1%",accessor:c=>c.method,className:"font-mono text-xs whitespace-nowrap"},{id:"url",header:"Request URL",headerTitle:"Request URL",accessor:c=>c.url,className:"text-bright font-mono text-xs truncate max-w-[800px]",cellTitle:c=>c.url},{id:"status",header:"Status",headerTitle:"HTTP Status Code",width:"1%",accessor:c=>c.status,className:c=>`font-bold text-xs uppercase whitespace-nowrap ${c.status==="success"?"text-success":"text-danger"}`},{id:"duration",header:"Duration",headerTitle:"Execution time in ms",width:"1%",align:"right",accessor:c=>$s(c.duration),className:"font-mono text-xs text-dim whitespace-nowrap"}];new ep("trace-table",{columns:a,dense:!0,tableLayout:"auto",onRowClick:c=>{const l=document.getElementById("trace-modal"),u=document.getElementById("trace-detail-content");l&&u&&(u.textContent=JSON.stringify(c,null,2),l.style.display="flex")}}).render(o)}function np(e){const t=document.getElementById("pools-container");!t||!e.pools||(t.innerHTML=e.pools.map(i=>wS(i)).join(""))}function ip(e){const t=e.status.config;if(!t)return;const i=document.getElementById("config-view");i&&(i.innerHTML=TS(t))}function Jr(e){if(e===0)return"0 B";const t=1024,i=["B","KB","MB","GB"],r=Math.floor(Math.log(e)/Math.log(t));return`${Number.parseFloat((e/Math.pow(t,r)).toFixed(2))} ${i[r]}`}function sl(e){return $s(e/1e3)}function ks(e,t){const i=document.getElementById("middleware-version");i&&e.version&&(i.textContent=`${e.version} (2/22/2026, 11:54:57 PM)`,i.title="The version of the web_plsql gateway and admin console build time");const r=document.getElementById("node-version");r&&e.system&&(r.textContent=e.system.nodeVersion,r.title="The version of the Node.js runtime");const n=document.getElementById("platform-info");n&&e.system&&(n.textContent=`${e.system.platform} (${e.system.arch})`,n.title="The operating system and architecture");const o=document.getElementById("system-uptime");o&&e.uptime&&(o.textContent=_s(e.uptime));const a=document.getElementById("system-start-time");a&&e.startTime&&(a.textContent=Qg(e.startTime));const s=document.getElementById("system-status-text");s&&(s.textContent=e.status?.toUpperCase()??"-");const c=document.getElementById("total-requests");c&&e.metrics&&(c.textContent=e.metrics.requestCount.toLocaleString(),c.title="Cumulative number of requests handled by the server");const l=document.getElementById("total-errors");l&&e.metrics&&(l.textContent=e.metrics.errorCount.toLocaleString(),l.title="Cumulative number of failed requests");const u=document.getElementById("active-pools");if(u&&e.pools&&(u.textContent=e.pools.length.toString(),u.title="Number of database connection pools currently active"),e.system){const d=e.system,f={heapUsed:d.memory.heapUsed,heapTotal:d.memory.heapTotal,rss:d.memory.rss,external:d.memory.external,cpuUser:d.cpu.user,cpuSystem:d.cpu.system},h=(_,k,x)=>{const y=f[k],z=d.memory[x],T=document.getElementById(_),I=document.getElementById(`${_}-max`);T&&(T.textContent=Jr(y)),I&&typeof z=="number"&&(I.textContent=Jr(z))},g=(_,k,x)=>{const y=f[k],z=document.getElementById(_);if(z&&(z.textContent=sl(y)),x){const T=d.cpu[x],I=document.getElementById(`${_}-max`);I&&typeof T=="number"&&(I.textContent=sl(T))}};h("memory-heap-used","heapUsed","heapUsedMax"),h("memory-heap-total","heapTotal","heapTotalMax"),h("memory-rss","rss","rssMax"),h("memory-external","external","externalMax"),g("cpu-user","cpuUser","userMax"),g("cpu-system","cpuSystem","systemMax");const m=document.getElementById("cpu-percent");if(m&&d.cpu.max!==void 0){const _=d.cpu.max;m.textContent=`${_.toFixed(1)}%`,m.title="Max System CPU usage"}const p=document.getElementById("memory-percent");if(p&&d.memory.totalMemory){const _=d.memory.rss/d.memory.totalMemory*100;p.textContent=`${_.toFixed(1)}%`,p.title=`System Memory Used relative to Total (${Jr(d.memory.totalMemory)})`}const b=document.getElementById("cpu-cores-info");b&&d.cpuCores&&(b.textContent=`${d.cpuCores} Cores`)}}async function Qn(e,t){const i=e.querySelector(".material-symbols-rounded"),r=i?.textContent;e.disabled=!0,e.classList.add("loading"),i&&(i.textContent="sync",i.classList.add("animate-spin"));try{return await t()}finally{e.disabled=!1,e.classList.remove("loading"),i&&(i.textContent=r??"",i.classList.remove("animate-spin"))}}function ot(e,t){const i=document.getElementById(e);return i&&i.addEventListener("click",()=>{Qn(i,t)}),i}const OS={overview:"dashboard",trace:"bolt",errors:"error",access:"list_alt",pools:"database",stats:"query_stats",config:"settings",system:"terminal"},O={currentView:"overview",status:{},maxHistoryPoints:30,lastRequestCount:0,lastErrorCount:0,lastUpdateTime:Date.now(),lastBucketTimestamp:0,nextRefreshTimeout:null,nextRefreshTime:0,countdownInterval:null,history:{labels:[],requests:[],avgResponseTimes:[],p95ResponseTimes:[],p99ResponseTimes:[],cpuUsage:[],memoryUsage:[],poolUsage:{}},charts:{},metricsMin:{},metricsMax:{}},at={REFRESH_INTERVAL:"admin_refresh_interval",HISTORY_DURATION:"admin_history_duration",AUTO_REFRESH:"admin_auto_refresh",THEME:"theme",LAST_VIEW:"admin_last_view"};function rp(e){console.error("Connection lost:",e);const t=document.getElementById("server-status-dot"),i=document.getElementById("server-status-text");t&&(t.className="dot offline"),i&&(i.textContent="OFFLINE");const r=document.getElementById("btn-pause");r&&(r.style.display="none");const n=document.getElementById("btn-resume");n&&(n.style.display="none");const o=document.querySelector("#system-status .dot"),a=document.getElementById("system-status-text");o&&(o.className="dot offline"),a&&(a.textContent="OFFLINE");const s=document.getElementById("offline-modal");s&&s.style.display!=="flex"&&(s.style.display="flex"),Ai(6e4),op(6e4)}function op(e){O.countdownInterval&&clearInterval(O.countdownInterval);const t=Date.now()+e,i=document.getElementById("offline-countdown"),r=document.getElementById("reconnect-text");r&&(r.textContent="Try Reconnect");const n=()=>{if(!i)return;const o=Math.max(0,Math.ceil((t-Date.now())/1e3));i.textContent=`Retrying in ${o}s...`,o<=0&&O.countdownInterval&&(clearInterval(O.countdownInterval),O.countdownInterval=null)};n(),O.countdownInterval=setInterval(n,1e3)}function DS(){O.countdownInterval&&(clearInterval(O.countdownInterval),O.countdownInterval=null);const e=document.getElementById("offline-countdown");e&&(e.textContent="")}async function qe(e=!1,t=!1){const i=document.getElementById("reconnect-spinner"),r=document.getElementById("reconnect-icon"),n=document.getElementById("reconnect-text");i&&(i.style.display="inline-block"),r&&(r.style.display="none"),n&&(n.textContent="Connecting...");try{const o=Date.now(),a=await $e.getStatus(e,t),s=document.getElementById("offline-modal");if(s?.style.display==="flex"&&s&&(s.style.display="none",DS()),i&&(i.style.display="none"),r&&(r.style.display="inline-block"),n&&(n.textContent="Try Reconnect"),a.history&&O.history.labels.length===0&&_S(O,a.history),!a.config&&O.status.config&&(a.config=O.status.config),O.status=a,!a.metrics||!a.pools)return;const l=a.history?.at(-1),u=(a.intervalMs??Yg)/1e3,d=l?l.requests/u:0,f=l?l.durationAvg:a.metrics.avgResponseTime;if(O.lastRequestCount=a.metrics.requestCount,O.lastErrorCount=a.metrics.errorCount,O.lastUpdateTime=o,a.system){const U=a.system,Q={heapUsed:U.memory.heapUsed,heapTotal:U.memory.heapTotal,rss:U.memory.rss,external:U.memory.external,cpuUser:U.cpu.user,cpuSystem:U.cpu.system};$S(Q,O.metricsMin,O.metricsMax)}if(l&&l.timestamp>O.lastBucketTimestamp){O.lastBucketTimestamp=l.timestamp;const U=new Date(l.timestamp).toLocaleTimeString();ol(O,U,d,f,a.pools)}else if(!l&&O.history.labels.length===0){const U=new Date().toLocaleTimeString();ol(O,U,0,0,a.pools)}const h=document.getElementById("sidebar-version");h&&(h.textContent=`v${a.version}`);const g=document.getElementById("uptime-val");g&&(g.textContent=_s(a.uptime));const m=document.getElementById("start-time-val");m&&(m.textContent=`Started: ${Qg(a.startTime)}`);const p=document.getElementById("req-per-sec");p&&(p.textContent=d.toFixed(2));const b=document.getElementById("max-req-per-sec");b&&(b.textContent=a.metrics.maxRequestsPerSecond.toFixed(2));const _=document.getElementById("avg-resp-time");_&&(_.textContent=`${a.metrics.avgResponseTime.toFixed(1)}ms`);const k=document.getElementById("max-resp-time");k&&(k.textContent=`${a.metrics.maxResponseTime.toFixed(1)}ms`);const x=document.getElementById("err-count");x&&(x.textContent=a.metrics.errorCount.toLocaleString());let y=0,z=0;a.pools.forEach(U=>{U.cache&&(y+=U.cache.procedureName.hits+U.cache.argument.hits,z+=U.cache.procedureName.misses+U.cache.argument.misses)});const T=y+z,I=T>0?Math.round(y/T*100):0,E=document.getElementById("cache-hit-rate-val");E&&(E.textContent=`${I}%`,E.style.color=I>80?"var(--success)":I>50?"var(--warning)":"var(--danger)");const R=document.getElementById("cache-hits-val");R&&(R.textContent=y.toLocaleString());const A=document.getElementById("cache-misses-val");A&&(A.textContent=z.toLocaleString());const F=document.getElementById("server-status-dot"),ie=document.getElementById("server-status-text");F&&(F.className="dot "+a.status),ie&&(ie.textContent=a.status.toUpperCase());const se=document.getElementById("btn-pause");se&&(se.style.display=a.status==="running"?"flex":"none");const W=document.getElementById("btn-resume");if(W&&(W.style.display=a.status==="paused"?"flex":"none"),O.currentView==="errors"&&await Cr(),O.currentView==="trace"&&await Xt(),O.currentView==="pools"&&np(a),O.currentView==="config"&&ip(O),O.currentView==="system"&&ks(a,O),Vt?.checked&&Ue){const U=Number.parseInt(Ue.value);Ai(U)}}catch(o){document.getElementById("offline-modal")?.style.display==="flex"?(Ai(6e4),op(6e4),i&&(i.style.display="none"),r&&(r.style.display="inline-block"),n&&(n.textContent="Try Reconnect")):rp(o)}}function Ai(e){O.nextRefreshTimeout&&(clearTimeout(O.nextRefreshTimeout),O.nextRefreshTimeout=null),O.nextRefreshTime=Date.now()+e,O.nextRefreshTimeout=setTimeout(()=>{qe()},e)}function ap(){const e=document.getElementById("refresh-interval");if(e){const t=Number.parseInt(e.value);Ai(t)}}function PS(){O.nextRefreshTimeout&&(clearTimeout(O.nextRefreshTimeout),O.nextRefreshTimeout=null)}function sp(){const e=document.getElementById("chart-history-points");e&&Array.from(e.options).forEach(t=>{const i=Number.parseInt(t.value);let r="";if(i<60)r=`${i}s`;else if(i<3600)r=`${Math.round(i/60)} min`;else{const n=i/3600;r=n===Math.round(n)?`${n} hour${n>1?"s":""}`:`${n.toFixed(1)} hours`}t.textContent=`Last ${r}`})}document.querySelectorAll("nav button").forEach(e=>{const t=e;t.addEventListener("click",async()=>{try{const i=t.dataset.view;if(!i)return;O.currentView=i,localStorage.setItem(at.LAST_VIEW,i),document.querySelectorAll(".view").forEach(o=>{const a=o;a.style.display="none"});const r=document.getElementById("view-"+i);r&&(r.style.display="block"),document.querySelectorAll("nav button").forEach(o=>o.classList.remove("active")),t.classList.add("active");const n=document.getElementById("view-title");if(n){const a=Array.from(t.childNodes).filter(c=>c.nodeType===Node.TEXT_NODE).map(c=>c.textContent?.trim()).join(""),s=OS[i]??"chevron_right";n.innerHTML=`<span class="material-symbols-rounded">${s}</span>${a}`}i==="errors"&&await Cr(),i==="trace"&&await Xt(),i==="pools"&&np(O.status),i==="stats"&&await xs(),i==="config"&&ip(O),i==="system"&&ks(O.status,O)}catch(i){rp(i)}})});const Vt=document.getElementById("auto-refresh-toggle");Vt&&Vt.addEventListener("change",e=>{const t=e.target;localStorage.setItem(at.AUTO_REFRESH,String(t.checked)),t.checked?ap():PS()});const Ue=document.getElementById("refresh-interval");Ue&&Ue.addEventListener("change",()=>{localStorage.setItem(at.REFRESH_INTERVAL,Ue.value);const e=Number.parseInt(Ue.value),t=Number.parseInt(We?.value??"60");O.maxHistoryPoints=Math.max(1,Math.floor(t/(e/1e3))),O.history.labels=[],O.history.requests=[],O.history.avgResponseTimes=[],O.history.cpuUsage=[],O.history.memoryUsage=[],O.history.poolUsage={},sp(),ks(O.status),qe()});const We=document.getElementById("chart-history-points");We&&We.addEventListener("change",()=>{localStorage.setItem(at.HISTORY_DURATION,We.value);const e=document.getElementById("refresh-interval");if(!e)return;const t=Number.parseInt(e.value),i=Number.parseInt(We.value);O.maxHistoryPoints=Math.max(1,Math.floor(i/(t/1e3))),O.history.labels=[],O.history.requests=[],O.history.avgResponseTimes=[],O.history.cpuUsage=[],O.history.memoryUsage=[],O.history.poolUsage={},qe()});const qr=document.getElementById("manual-refresh");qr&&qr.addEventListener("click",()=>{Qn(qr,qe)});const cl=document.getElementById("error-filter");cl&&cl.addEventListener("keydown",e=>{e.key==="Enter"&&Cr()});ot("error-load-btn",Cr);ot("trace-load-btn",Xt);ot("access-load-btn",tp);ot("stats-load-btn",xs);const ll=document.getElementById("trace-filter");ll&&ll.addEventListener("keydown",e=>{e.key==="Enter"&&Xt()});const Kr=document.getElementById("trace-clear-btn");Kr&&Kr.addEventListener("click",async()=>{confirm("Are you sure you want to clear all traces?")&&await Qn(Kr,async()=>{await $e.clearTraces(),await Xt()})});const Xr=document.getElementById("trace-status-toggle");Xr&&Xr.addEventListener("click",async()=>{await Qn(Xr,async()=>{const{enabled:e}=await $e.getTraceStatus();await $e.toggleTrace(!e),await Xt()})});const ul=document.getElementById("access-filter");ul&&ul.addEventListener("keydown",e=>{e.key==="Enter"&&tp()});const dl=document.getElementById("stats-limit");dl&&dl.addEventListener("keydown",e=>{e.key==="Enter"&&xs()});ot("btn-pause",async()=>{confirm("Are you sure you want to pause the server?")&&(await $e.post("api/server/pause"),await qe())});ot("btn-resume",async()=>{await $e.post("api/server/resume"),await qe()});ot("btn-stop",async()=>{confirm("Are you sure you want to STOP the server? This action cannot be undone from the web interface.")&&(await $e.post("api/server/stop"),alert("Server stop requested. The interface will now become unresponsive."))});ot("btn-clear-all-cache",async()=>{confirm("Are you sure you want to clear ALL caches?")&&(await $e.post("api/cache/clear"),await qe())});const Gr=document.getElementById("btn-reconnect");Gr&&Gr.addEventListener("click",()=>{Qn(Gr,qe)});globalThis.addEventListener("keydown",e=>{if(e.key==="Escape"){const t=document.getElementById("error-modal");t&&(t.style.display="none");const i=document.getElementById("trace-modal");i&&(i.style.display="none")}});const fl=localStorage.getItem(at.REFRESH_INTERVAL);fl&&Ue&&(Ue.value=fl);const hl=localStorage.getItem(at.HISTORY_DURATION);hl&&We&&(We.value=hl);const ml=localStorage.getItem(at.AUTO_REFRESH);ml!==null&&Vt&&(Vt.checked=ml==="true");SS(O);yS(O);sp();if(Ue&&We){const e=Number.parseInt(Ue.value),t=Number.parseInt(We.value);O.maxHistoryPoints=Math.max(1,Math.floor(t/(e/1e3)))}const gl=localStorage.getItem(at.LAST_VIEW);if(gl){const e=document.querySelector(`nav button[data-view="${gl}"]`);e&&e.click()}qe(!0,!0).then(()=>{Vt?.checked&&ap()}).catch(console.error);
@@ -6,7 +6,7 @@
6
6
  <title>web_plsql Admin Console</title>
7
7
  <link rel="icon" type="image/svg+xml" href="/admin/assets/favicon-mQAM4tVu.svg" />
8
8
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
9
- <script type="module" crossorigin src="/admin/assets/main-C8GKnEhz.js"></script>
9
+ <script type="module" crossorigin src="/admin/assets/main-D2PlS0b8.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/admin/assets/main-bU4x_yEW.css">
11
11
  </head>
12
12
  <body class="dark">
Binary file
Binary file
package/dist/index.mjs CHANGED
@@ -211,7 +211,7 @@ globalThis.__VERSION__ ??= "**development**";
211
211
  * Returns the current library version
212
212
  * @returns {string} - Version.
213
213
  */
214
- const getVersion = () => "1.3.1";
214
+ const getVersion = () => "1.3.2";
215
215
 
216
216
  //#endregion
217
217
  //#region src/backend/server/config.ts
@@ -2969,33 +2969,36 @@ const processRequest = async (req, res, options, connectionPool, procedureNameCa
2969
2969
  debug$2("processRequest: ENTER");
2970
2970
  if (typeof req.params.name !== "string") console.warn(`processRequest: WARNING: the req.params.name is not a string but an array of string: ${req.params.name}`);
2971
2971
  const connection = await connectionPool.getConnection();
2972
- const cgiObj = getCGI(req, options.documentTable, options.cgi ?? {}, authenticatedUser);
2973
- debug$2("processRequest: cgiObj=", cgiObj);
2974
- const filesToUpload = getFiles(req);
2975
- debug$2("processRequest: filesToUpload=", filesToUpload);
2976
- const argObj = {};
2977
- Object.assign(argObj, req.query);
2978
- filesToUpload.reduce((aggregator, file) => {
2979
- aggregator[file.fieldname] = file.filename;
2980
- return aggregator;
2981
- }, argObj);
2982
- Object.assign(argObj, normalizeBody(req));
2983
- debug$2("processRequest: argObj=", argObj);
2984
- await invokeProcedure(req, res, argObj, cgiObj, filesToUpload, options, connection, procedureNameCache, argumentCache);
2985
- if (options.transactionMode === "rollback") {
2986
- debug$2("transactionMode: rollback");
2987
- await connection.rollback();
2988
- } else if (typeof options.transactionMode === "function") {
2989
- debug$2("transactionMode: callback");
2990
- const procName = Array.isArray(req.params.name) ? req.params.name[0] : req.params.name;
2991
- const result = options.transactionMode(connection, procName ?? "");
2992
- debug$2("transactionMode: callback restult", result);
2993
- if (result && typeof result.then === "function") await result;
2994
- } else {
2995
- debug$2("transactionMode: commit");
2996
- await connection.commit();
2997
- }
2998
- await connection.release();
2972
+ try {
2973
+ const cgiObj = getCGI(req, options.documentTable, options.cgi ?? {}, authenticatedUser);
2974
+ debug$2("processRequest: cgiObj=", cgiObj);
2975
+ const filesToUpload = getFiles(req);
2976
+ debug$2("processRequest: filesToUpload=", filesToUpload);
2977
+ const argObj = {};
2978
+ Object.assign(argObj, req.query);
2979
+ filesToUpload.reduce((aggregator, file) => {
2980
+ aggregator[file.fieldname] = file.filename;
2981
+ return aggregator;
2982
+ }, argObj);
2983
+ Object.assign(argObj, normalizeBody(req));
2984
+ debug$2("processRequest: argObj=", argObj);
2985
+ await invokeProcedure(req, res, argObj, cgiObj, filesToUpload, options, connection, procedureNameCache, argumentCache);
2986
+ if (options.transactionMode === "rollback") {
2987
+ debug$2("transactionMode: rollback");
2988
+ await connection.rollback();
2989
+ } else if (typeof options.transactionMode === "function") {
2990
+ debug$2("transactionMode: callback");
2991
+ const procName = Array.isArray(req.params.name) ? req.params.name[0] : req.params.name;
2992
+ const result = options.transactionMode(connection, procName ?? "");
2993
+ debug$2("transactionMode: callback restult", result);
2994
+ if (result && typeof result.then === "function") await result;
2995
+ } else {
2996
+ debug$2("transactionMode: commit");
2997
+ await connection.commit();
2998
+ }
2999
+ } finally {
3000
+ await connection.release();
3001
+ }
2999
3002
  debug$2("processRequest: EXIT");
3000
3003
  };
3001
3004
 
@@ -3237,8 +3240,8 @@ const createAdminRouter = (adminContext) => {
3237
3240
  const name = cache?.poolName ?? `pool-${index}`;
3238
3241
  const p = pool;
3239
3242
  const stats = typeof p.getStatistics === "function" ? p.getStatistics() : null;
3240
- const procStats = cache?.procedureNameCache.getStats();
3241
- const argStats = cache?.argumentCache.getStats();
3243
+ const procStats = cache?.procedureNameCache?.getStats();
3244
+ const argStats = cache?.argumentCache?.getStats();
3242
3245
  return {
3243
3246
  name,
3244
3247
  stats,
@@ -3246,12 +3249,12 @@ const createAdminRouter = (adminContext) => {
3246
3249
  connectionsInUse: pool.connectionsInUse,
3247
3250
  cache: {
3248
3251
  procedureName: {
3249
- size: cache?.procedureNameCache.keys().length ?? 0,
3252
+ size: cache?.procedureNameCache?.keys().length ?? 0,
3250
3253
  hits: procStats?.hits ?? 0,
3251
3254
  misses: procStats?.misses ?? 0
3252
3255
  },
3253
3256
  argument: {
3254
- size: cache?.argumentCache.keys().length ?? 0,
3257
+ size: cache?.argumentCache?.keys().length ?? 0,
3255
3258
  hits: argStats?.hits ?? 0,
3256
3259
  misses: argStats?.misses ?? 0
3257
3260
  }
@@ -3458,32 +3461,45 @@ const handlerAdminConsole = (config, adminContext) => {
3458
3461
  const resolvedStaticDir = config.staticDir ?? resolveAdminStaticDir();
3459
3462
  if (adminRoute && !adminRoute.startsWith("/")) throw new Error("adminRoute must start with /");
3460
3463
  if (!config.devMode && !existsSync(resolvedStaticDir)) throw new Error(`Admin console not built. Run 'npm run build:frontend' first.\nExpected: ${resolvedStaticDir}`);
3461
- const originalRotate = adminContext.statsManager.rotateBucket.bind(adminContext.statsManager);
3462
- adminContext.statsManager.rotateBucket = () => {
3463
- originalRotate(adminContext.pools.map((pool, index) => {
3464
- const cache = adminContext.caches[index];
3465
- const name = cache?.poolName ?? `pool-${index}`;
3466
- const procStats = cache?.procedureNameCache.getStats();
3467
- const argStats = cache?.argumentCache.getStats();
3468
- return {
3469
- name,
3470
- connectionsOpen: pool.connectionsOpen,
3471
- connectionsInUse: pool.connectionsInUse,
3472
- cache: {
3473
- procedureName: {
3474
- size: cache?.procedureNameCache.keys().length ?? 0,
3475
- hits: procStats?.hits ?? 0,
3476
- misses: procStats?.misses ?? 0
3477
- },
3478
- argument: {
3479
- size: cache?.argumentCache.keys().length ?? 0,
3480
- hits: argStats?.hits ?? 0,
3481
- misses: argStats?.misses ?? 0
3464
+ const statsManager = adminContext.statsManager;
3465
+ const currentRotate = statsManager.rotateBucket;
3466
+ if (!Object.prototype.hasOwnProperty.call(currentRotate, "_isWrapped")) {
3467
+ const originalRotate = statsManager.rotateBucket;
3468
+ const wrappedRotate = (poolSnapshots = []) => {
3469
+ const mergedSnapshots = [...adminContext.pools.map((pool, index) => {
3470
+ const cache = adminContext.caches[index];
3471
+ const name = cache?.poolName ?? `pool-${index}`;
3472
+ const procStats = cache?.procedureNameCache?.getStats();
3473
+ const argStats = cache?.argumentCache?.getStats();
3474
+ return {
3475
+ name,
3476
+ connectionsOpen: pool.connectionsOpen,
3477
+ connectionsInUse: pool.connectionsInUse,
3478
+ cache: {
3479
+ procedureName: {
3480
+ size: cache?.procedureNameCache?.keys().length ?? 0,
3481
+ hits: procStats?.hits ?? 0,
3482
+ misses: procStats?.misses ?? 0
3483
+ },
3484
+ argument: {
3485
+ size: cache?.argumentCache?.keys().length ?? 0,
3486
+ hits: argStats?.hits ?? 0,
3487
+ misses: argStats?.misses ?? 0
3488
+ }
3482
3489
  }
3483
- }
3484
- };
3485
- }));
3486
- };
3490
+ };
3491
+ })];
3492
+ if (Array.isArray(poolSnapshots)) {
3493
+ for (const ps of poolSnapshots) if (!mergedSnapshots.some((s) => s.name === ps.name)) mergedSnapshots.push(ps);
3494
+ }
3495
+ originalRotate.call(statsManager, mergedSnapshots);
3496
+ };
3497
+ Object.defineProperty(wrappedRotate, "_isWrapped", {
3498
+ value: true,
3499
+ writable: false
3500
+ });
3501
+ statsManager.rotateBucket = wrappedRotate;
3502
+ }
3487
3503
  const router = Router();
3488
3504
  router.use((req, res, next) => {
3489
3505
  if (adminContext.paused && !req.path.startsWith(adminRoute)) {