release: WRNexusJS 0.7.0

This commit is contained in:
2026-08-01 10:04:42 +05:30
parent c54144f2e4
commit 87507edf59
207 changed files with 12607 additions and 679 deletions
+10 -3
View File
@@ -1,7 +1,7 @@
export const DEV_TOOLBAR_RUNTIME = String.raw`(() => {
if (window.__wrnexusDevToolbar) return;
const KEY = "__wrnexus_dev_toolbar_settings__";
const state = { open:false, issues:[], report:null, search:"", severity:"all", category:"all", platform:null, panels:[], config:{ position:"bottom-center", scanOnNavigation:true, scanOnHmr:true } };
const state = { open:false, issues:[], report:null, search:"", severity:"all", category:"all", platform:null, panels:[], runtimeMetrics:{ hydrationCount:0, hydrationMs:0, longTasks:[] }, config:{ position:"bottom-center", scanOnNavigation:true, scanOnHmr:true } };
try { state.config = Object.assign(state.config, JSON.parse(localStorage.getItem(KEY) || "{}")); } catch {}
const host = document.createElement("wrnexus-dev-toolbar");
host.setAttribute("data-wrnexus-dev-toolbar", "");
@@ -36,16 +36,21 @@ export const DEV_TOOLBAR_RUNTIME = String.raw`(() => {
const count=document.querySelectorAll("*").length;if(count>1500)found.push(issue("html/dom-size","html",count>3000?"error":"warning","DOM is large","The page contains "+count+" elements.",null,"Reduce wrappers and render hidden content on demand."));
q("button button,button a,a a,a button").forEach(el=>found.push(issue("html/nested-interactive","html","error","Interactive elements are nested","Nested links or buttons create invalid interaction behavior.",el,"Use one interactive element.")));
q("a").forEach(el=>{const href=el.getAttribute("href");if(!href||href==="#")found.push(issue("links/empty","links","warning","Link has no useful destination","The href is missing or only #.",el,"Use a real URL or a button."));if(el.target==="_blank"&&!/\b(noopener|noreferrer)\b/.test(el.rel))found.push(issue("links/blank-rel","security","warning","New-tab link lacks rel protection","The opened page may access window.opener.",el,"Add rel=\"noopener noreferrer\"."));});
q("form").forEach(el=>{const method=(el.getAttribute("method")||"get").toLowerCase();const action=el.getAttribute("action")||"";if(location.protocol==="https:"&&action.startsWith("http://"))found.push(issue("security/insecure-form-action","security","error","Form submits over HTTP",action,el,"Use an HTTPS or same-origin form action."));if(method!=="get"&&!el.hasAttribute("data-csrf")&&!el.querySelector('input[name="wire-csrf"],input[name="_csrf"]'))found.push(issue("security/missing-csrf","security","error","State-changing form has no CSRF marker","Cookie-authenticated form submissions require CSRF protection.",el,"Add data-csrf or a framework-generated CSRF field."));});
q("script:not([src]):not([type='application/json']):not([nonce])").forEach(el=>found.push(issue("security/inline-script","security","warning","Inline script has no CSP nonce","A strict Content Security Policy will block this script.",el,"Move code to a client module or attach the request nonce.")));
q("iframe:not([sandbox])").forEach(el=>found.push(issue("security/iframe-sandbox","security","warning","Iframe is not sandboxed","Untrusted iframe content has broad browser capabilities.",el,"Add the narrowest sandbox and permissions policy.")));
q("[src],[href],[action]").forEach(el=>{const raw=el.getAttribute("src")||el.getAttribute("href")||el.getAttribute("action")||"";if(location.protocol==="https:"&&raw.startsWith("http://"))found.push(issue("security/mixed-content","security","error","Mixed-content resource",raw,el,"Use HTTPS or a same-origin resource."));});
try{for(let i=0;i<localStorage.length;i++){const key=localStorage.key(i)||"";if(/(?:token|secret|password|session|credential|authorization)/i.test(key))found.push(issue("security/sensitive-local-storage","security","error","Sensitive value may be stored in localStorage","Storage key “"+key+"” looks authentication- or secret-related.",null,"Keep sessions and credentials in Secure, HttpOnly cookies."));}}catch{}
if(document.documentElement.scrollWidth>innerWidth+2)found.push(issue("responsive/document-overflow","responsive","error","Page has horizontal overflow","Document width exceeds the viewport.",null,"Inspect fixed widths, long text and overflowing media."));
q("body *").filter(visible).slice(0,2500).forEach(el=>{const r=el.getBoundingClientRect();if((r.right>innerWidth+8||r.left<-8)&&found.filter(x=>x.ruleId==="responsive/element-overflow").length<20)found.push(issue("responsive/element-overflow","responsive","warning","Element extends outside the viewport","Element bounds exceed the current viewport.",el,"Use fluid sizing, wrapping, max-width or an intentional scroll container."));});
const resources=performance.getEntriesByType("resource");const total=resources.reduce((s,e)=>s+(e.transferSize||0),0);if(resources.length>150)found.push(issue("performance/resource-count","performance","warning","Page loads many resources","Found "+resources.length+" resource requests.",null,"Remove duplicates and defer non-critical resources."));if(total>5000000)found.push(issue("performance/transfer-size","performance",total>10000000?"error":"warning","Page transfer size is large","Observed transfer size is about "+(total/1000000).toFixed(2)+" MB.",null,"Compress and optimize page resources."));
const resources=performance.getEntriesByType("resource");const total=resources.reduce((s,e)=>s+(e.transferSize||0),0);const jsBytes=resources.filter(e=>/(?:\.m?js)(?:\?|$)/i.test(e.name)).reduce((sum,e)=>sum+(e.transferSize||0),0);if(resources.length>150)found.push(issue("performance/resource-count","performance","warning","Page loads many resources","Found "+resources.length+" resource requests.",null,"Remove duplicates and defer non-critical resources."));if(total>5000000)found.push(issue("performance/transfer-size","performance",total>10000000?"error":"warning","Page transfer size is large","Observed transfer size is about "+(total/1000000).toFixed(2)+" MB.",null,"Compress and optimize page resources."));if(jsBytes>150000)found.push(issue("performance/javascript-budget","javascript",jsBytes>300000?"error":"warning","JavaScript budget exceeded","JavaScript transfer is about "+(jsBytes/1000).toFixed(1)+" KB.",null,"Split routes and defer optional hydration."));const hydrationRoots=q("[data-wrn-client-module],[data-wrn-hydrate]");if(hydrationRoots.length>50)found.push(issue("performance/hydration-count","runtime","warning","Many components hydrate",hydrationRoots.length+" hydration boundaries were found.",null,"Use visible, idle or interaction hydration."));if(state.runtimeMetrics.longTasks.length)found.push(issue("performance/long-tasks","javascript","warning","Long main-thread tasks detected",state.runtimeMetrics.longTasks.length+" task(s) exceeded 50 ms.",null,"Split expensive work and reduce hydration.","high",{longestMs:Math.max(...state.runtimeMetrics.longTasks)}));
q("[data-wrn-client-module]").forEach(el=>found.push(issue("runtime/client-module","runtime","info","Client function module",el.getAttribute("data-wrn-client-module")||"Unknown module",el,"Loaded according to the component hydration strategy.","high",{hydration:el.getAttribute("data-wrn-hydrate"),runtime:el.getAttribute("data-wrn-runtime")})));
const storeContainer=window.__wrnexusStoreContainer;
if(storeContainer&&typeof storeContainer.inspect==="function"){
for(const store of storeContainer.inspect())found.push(issue("stores/instance","stores","info",store.kind+" store: "+store.name,JSON.stringify({state:store.state,computed:store.computed}),null,"Use store actions for mutations. Sensitive server state is never hydrated.","high",store));
}
const unique=new Map();[...state.issues.filter(x=>["runtime","stores","network","server","compiler","routing"].includes(x.category)),...found].forEach(x=>unique.set(x.fingerprint,x));state.issues=[...unique.values()];
state.report={url:location.href,pathname:location.pathname,title:document.title,status:200,generatedAt:Date.now(),issues:state.issues,metrics:{domNodes:count,cssResources:q('link[rel="stylesheet"]').length,scriptResources:q("script[src]").length,imageResources:q("img").length,totalTransferBytes:total,pageLoadMs:performance.getEntriesByType("navigation")[0]?.duration}}; render(); return state.report;
state.report={url:location.href,pathname:location.pathname,title:document.title,status:200,generatedAt:Date.now(),issues:state.issues,metrics:{domNodes:count,cssResources:q('link[rel="stylesheet"]').length,scriptResources:q("script[src]").length,imageResources:q("img").length,totalTransferBytes:total,javascriptBytes:jsBytes,hydratedComponents:state.runtimeMetrics.hydrationCount,hydrationMs:state.runtimeMetrics.hydrationMs,longTasks:state.runtimeMetrics.longTasks.length,pageLoadMs:performance.getEntriesByType("navigation")[0]?.duration}}; render(); return state.report;
};
let highlightEl=null; const clearHighlight=()=>{highlightEl?.remove();highlightEl=null}; const highlight=sel=>{clearHighlight();let target;try{target=document.querySelector(sel)}catch{}if(!target)return;const r=target.getBoundingClientRect();highlightEl=document.createElement("div");highlightEl.className="wrn-highlight";Object.assign(highlightEl.style,{left:r.left+"px",top:r.top+"px",width:r.width+"px",height:r.height+"px"});root.appendChild(highlightEl);target.scrollIntoView({block:"center",behavior:"smooth"});setTimeout(clearHighlight,3000)};
const render=()=>{const filtered=state.issues.filter(x=>{const severityMatches=state.severity==="all"||x.severity===state.severity;const categoryMatches=state.category==="all"||x.category===state.category;const textMatches=!state.search||[x.title,x.message,x.ruleId,x.category,x.severity,x.source?.file].join(" ").toLowerCase().includes(state.search);return severityMatches&&categoryMatches&&textMatches;});root.querySelector("[data-errors]").textContent=state.issues.filter(x=>x.severity==="error").length;root.querySelector("[data-warnings]").textContent=state.issues.filter(x=>x.severity==="warning").length;root.querySelector("[data-suggestions]").textContent=state.issues.filter(x=>x.severity==="suggestion").length;root.querySelectorAll("[data-filter]").forEach(button=>{const active=button.dataset.filter===state.severity;button.classList.toggle("active",active);button.setAttribute("aria-pressed",String(active));});root.querySelectorAll("[data-category]").forEach(button=>{const active=button.dataset.category===state.category;button.classList.toggle("active",active);button.setAttribute("aria-pressed",String(active));});meta.textContent=location.pathname+" · "+state.issues.length+" findings"+(state.category!=="all"?" · "+state.category:"")+(state.severity!=="all"?" · "+state.severity:"");if(!filtered.length){list.innerHTML='<div class="wrn-empty">No issues match the current filters.</div>';return;}list.innerHTML=filtered.map(x=>'<article class="wrn-issue"><span class="wrn-severity '+esc(x.severity)+'"></span><div><div class="wrn-issue-title">'+esc(x.title)+'</div><div class="wrn-issue-message">'+esc(x.message)+'</div><div class="wrn-issue-meta">'+esc(x.severity)+' · '+esc(x.category)+' · '+esc(x.ruleId)+(x.source?.file?' · '+esc(x.source.file):'')+'</div></div><div class="wrn-actions">'+(x.target?.selector?'<button class="wrn-small" data-highlight="'+esc(x.target.selector)+'">Show</button>':'')+'</div></article>').join("");};
@@ -57,6 +62,8 @@ export const DEV_TOOLBAR_RUNTIME = String.raw`(() => {
addEventListener("wrnexus:navigated",()=>{if(state.config.scanOnNavigation)setTimeout(scan,50)});addEventListener("wrnexus:hmr",()=>{if(state.config.scanOnHmr)setTimeout(scan,100)});addEventListener("wrnexus:runtime-error",event=>addRuntime("error","WRNexus runtime error",event.detail?.message||"Runtime failure",event.detail||{}));
addEventListener("wrnexus:diagnostic",event=>{const detail=event.detail||{};const code=detail.code||"WRN-RUNTIME";const category=String(code).includes("HYDRATE")?"runtime":String(code).includes("ROUTE")?"routing":"compiler";const title=String(code).includes("HYDRATE")?"Hydration diagnostic":"WRNexus diagnostic";const x=issue(String(code),category,"error",title,detail.message||"Framework diagnostic",null,"Open the source location and resolve the reported framework contract.","high",detail);state.issues=[x,...state.issues.filter(i=>i.fingerprint!==x.fingerprint)];render();});
addEventListener("wrnexus:store-mutation",event=>{const mutation=event.detail||{};const x=issue("stores/mutation","stores","info","Store action: "+(mutation.store||"unknown")+"."+(mutation.action||"direct"),"Changed fields: "+((mutation.changed||[]).join(", ")||"none"),null,"Use the Stores panel to inspect safe client state.","high",mutation);state.issues=[x,...state.issues.filter(i=>i.fingerprint!==x.fingerprint)];render();});
addEventListener("wrnexus:hydrated",event=>{state.runtimeMetrics.hydrationCount+=1;state.runtimeMetrics.hydrationMs+=Number(event.detail?.durationMs||0);});
try{new PerformanceObserver(list=>{for(const entry of list.getEntries()){if(entry.duration>50)state.runtimeMetrics.longTasks.push(entry.duration);}state.runtimeMetrics.longTasks=state.runtimeMetrics.longTasks.slice(-100);}).observe({type:"longtask",buffered:true});}catch{}
fetch("/__wrnexus/dev-toolbar/platform").then(r=>r.ok?r.json():null).then(data=>{if(!data)return;state.platform=data.platform;state.panels=data.panels||[];render();}).catch(()=>{});
window.__wrnexusDevToolbar={open(){state.open=true;panel.classList.add("open")},close(){state.open=false;panel.classList.remove("open")},toggle(){state.open=!state.open;panel.classList.toggle("open",state.open)},scan,clear(){state.issues=[];render()},report(){return state.report},highlight,configure(config){state.config=Object.assign(state.config,config||{});shell.dataset.position=state.config.position;try{localStorage.setItem(KEY,JSON.stringify(state.config))}catch{}}};
const observer=new MutationObserver(()=>{clearTimeout(observer.timer);observer.timer=setTimeout(()=>{if(!state.open)return;scan()},400)});observer.observe(document.documentElement,{childList:true,subtree:true,attributes:true,attributeFilter:["class","style","src","href","alt","aria-label"]});
+109 -5
View File
@@ -6,13 +6,13 @@ export const performanceRules: DevToolbarRule[] = [
id: "performance/resources",
category: "performance",
defaultSeverity: "warning",
description: "Checks resource count and transfer sizes.",
run: ({ performanceEntries }) => {
description: "Checks resource, DOM, hydration, and main-thread budgets.",
run: ({ document, root, performanceEntries }) => {
const resources = performanceEntries.filter(
(entry): entry is PerformanceResourceTiming => entry.entryType === "resource",
);
const issues = [];
if (resources.length > 150)
if (resources.length > 150) {
issues.push(
createIssue({
ruleId: "performance/resource-count",
@@ -24,8 +24,9 @@ export const performanceRules: DevToolbarRule[] = [
"Remove duplicates, combine tiny assets where useful, and load non-critical resources later.",
}),
);
}
const total = resources.reduce((sum, entry) => sum + (entry.transferSize || 0), 0);
if (total > 5_000_000)
if (total > 5_000_000) {
issues.push(
createIssue({
ruleId: "performance/transfer-size",
@@ -37,7 +38,26 @@ export const performanceRules: DevToolbarRule[] = [
"Compress images, scripts, styles and fonts; review third-party resources.",
}),
);
for (const entry of resources.filter((item) => item.duration > 2000).slice(0, 20))
}
const javascriptBytes = resources
.filter((entry) => /(?:\.m?js)(?:\?|$)/i.test(entry.name))
.reduce((sum, entry) => sum + (entry.transferSize || 0), 0);
if (javascriptBytes > 150_000) {
issues.push(
createIssue({
ruleId: "performance/javascript-budget",
category: "javascript",
severity: javascriptBytes > 300_000 ? "error" : "warning",
title: "JavaScript budget exceeded",
message: `JavaScript transfer is approximately ${(javascriptBytes / 1_000).toFixed(1)} KB.`,
recommendation:
"Split routes, remove unused client code, and defer optional hydration.",
}),
);
}
for (const entry of resources.filter((item) => item.duration > 2_000).slice(0, 20)) {
issues.push(
createIssue({
ruleId: "performance/slow-resource",
@@ -49,6 +69,90 @@ export const performanceRules: DevToolbarRule[] = [
metadata: { url: entry.name, duration: entry.duration },
}),
);
}
const domNodes = root.querySelectorAll("*").length;
if (domNodes > 1_500) {
issues.push(
createIssue({
ruleId: "performance/dom-size",
category: "performance",
severity: domNodes > 3_000 ? "error" : "warning",
title: "DOM is large",
message: `The page contains ${domNodes} elements.`,
recommendation:
"Virtualize long lists and avoid rendering hidden or duplicate structures.",
}),
);
}
const hydrationRoots = root.querySelectorAll("[data-wrn-client-module], [data-wrn-hydrate]");
if (hydrationRoots.length > 50) {
issues.push(
createIssue({
ruleId: "performance/hydration-count",
category: "runtime",
severity: "warning",
title: "Many components hydrate",
message: `${hydrationRoots.length} hydration boundaries were found.`,
recommendation:
"Use visible, idle, or interaction hydration and keep static components server-only.",
}),
);
}
for (const image of root.querySelectorAll<HTMLImageElement>("img")) {
if (!image.complete || !image.naturalWidth) continue;
const renderedWidth = Math.max(1, image.getBoundingClientRect().width);
if (image.naturalWidth > renderedWidth * 2.5) {
issues.push(
createIssue({
ruleId: "performance/oversized-image",
category: "images",
severity: "warning",
title: "Image is larger than rendered size",
message: `${image.naturalWidth}px image is rendered at about ${Math.round(renderedWidth)}px.`,
element: image,
recommendation: "Generate responsive srcset candidates and accurate sizes.",
}),
);
}
}
const blocking = document.querySelectorAll(
'head script:not([async]):not([defer]):not([type="module"]), head link[rel="stylesheet"]:not([media])',
);
if (blocking.length > 4) {
issues.push(
createIssue({
ruleId: "performance/render-blocking",
category: "performance",
severity: "warning",
title: "Multiple render-blocking resources",
message: `${blocking.length} potentially render-blocking resources were found.`,
recommendation: "Inline only critical CSS and defer non-critical scripts and styles.",
}),
);
}
const longTasks = performanceEntries.filter(
(entry) => entry.entryType === "longtask" && entry.duration > 50,
);
if (longTasks.length) {
issues.push(
createIssue({
ruleId: "performance/long-tasks",
category: "javascript",
severity: "warning",
title: "Long main-thread tasks detected",
message: `${longTasks.length} task(s) exceeded 50 ms.`,
recommendation:
"Split expensive work, reduce hydration, and move non-UI work off the main thread.",
metadata: { longestMs: Math.max(...longTasks.map((entry) => entry.duration)) },
}),
);
}
return issues;
},
},
+136 -4
View File
@@ -1,16 +1,18 @@
import type { DevToolbarRule } from "./types.ts";
import { createIssue } from "./helpers.ts";
const SECRET_KEY = /pass(word)?|token|secret|api[-_]?key|authorization|session/i;
export const securityRules: DevToolbarRule[] = [
{
id: "security/page",
category: "security",
defaultSeverity: "warning",
description: "Checks development-visible security mistakes.",
run: ({ url, root }) => {
run: ({ document, window, url, root }) => {
const issues = [];
for (const [key] of url.searchParams)
if (/pass(word)?|token|secret|api[-_]?key/i.test(key))
for (const [key] of url.searchParams) {
if (SECRET_KEY.test(key)) {
issues.push(
createIssue({
ruleId: "security/secret-query",
@@ -22,7 +24,10 @@ export const securityRules: DevToolbarRule[] = [
"Send secrets in a secure request body or authorization header, not a URL.",
}),
);
for (const form of root.querySelectorAll<HTMLFormElement>('form[action^="http://"]'))
}
}
for (const form of root.querySelectorAll<HTMLFormElement>('form[action^="http://"]')) {
issues.push(
createIssue({
ruleId: "security/insecure-form",
@@ -34,6 +39,133 @@ export const securityRules: DevToolbarRule[] = [
recommendation: "Submit to an HTTPS endpoint.",
}),
);
}
for (const form of root.querySelectorAll<HTMLFormElement>("form")) {
const method = (form.method || "get").toUpperCase();
if (
!["GET", "HEAD"].includes(method) &&
!form.querySelector('input[name="wire-csrf"], input[name="_csrf"]')
) {
issues.push(
createIssue({
ruleId: "security/missing-csrf",
category: "security",
severity: "error",
title: "State-changing form has no CSRF token",
message: `${method} form does not contain a recognized CSRF field.`,
element: form,
recommendation: "Enable WRNexus CSRF middleware and use the generated form token.",
}),
);
}
}
for (const script of root.querySelectorAll<HTMLScriptElement>("script:not([src])")) {
if (script.type === "application/json" || script.hasAttribute("nonce")) continue;
issues.push(
createIssue({
ruleId: "security/inline-script",
category: "security",
severity: "warning",
title: "Inline script has no CSP nonce",
message: "A strict Content Security Policy will block this inline script.",
element: script,
recommendation: "Move code into a client module or attach the request CSP nonce.",
}),
);
}
for (const element of root.querySelectorAll<HTMLElement>("[style]")) {
issues.push(
createIssue({
ruleId: "security/inline-style",
category: "security",
severity: "suggestion",
title: "Inline style weakens strict CSP",
message: "The element uses a style attribute.",
element,
recommendation:
"Prefer extracted CSS classes or a nonce/hash-compatible style strategy.",
confidence: "medium",
}),
);
}
for (const element of root.querySelectorAll<HTMLElement>("[src], [href], [action]")) {
const raw =
element.getAttribute("src") ??
element.getAttribute("href") ??
element.getAttribute("action");
if (window.location.protocol === "https:" && raw?.startsWith("http://")) {
issues.push(
createIssue({
ruleId: "security/mixed-content",
category: "security",
severity: "error",
title: "Mixed-content resource",
message: `${raw} is loaded over insecure HTTP.`,
element,
recommendation: "Use HTTPS or serve the resource from the same secure origin.",
}),
);
}
}
for (const frame of root.querySelectorAll<HTMLIFrameElement>("iframe:not([sandbox])")) {
issues.push(
createIssue({
ruleId: "security/iframe-sandbox",
category: "security",
severity: "warning",
title: "Iframe is not sandboxed",
message: "Third-party or untrusted iframe content has broad browser capabilities.",
element: frame,
recommendation: "Add the narrowest possible sandbox and permissions policy.",
}),
);
}
try {
for (let index = 0; index < window.localStorage.length; index += 1) {
const key = window.localStorage.key(index) ?? "";
if (SECRET_KEY.test(key)) {
issues.push(
createIssue({
ruleId: "security/sensitive-local-storage",
category: "security",
severity: "error",
title: "Sensitive value may be stored in localStorage",
message: `Storage key “${key}” looks authentication- or secret-related.`,
recommendation: "Keep sessions and credentials in Secure, HttpOnly cookies.",
}),
);
}
}
} catch {
// Storage may be unavailable in restricted browser contexts.
}
const hydration = document.querySelectorAll<HTMLScriptElement>(
'script[type="application/json"][data-wrnexus-state], script[data-wrnexus-store-state]',
);
for (const script of hydration) {
if (SECRET_KEY.test(script.textContent ?? "")) {
issues.push(
createIssue({
ruleId: "security/hydration-secret",
category: "security",
severity: "error",
title: "Hydration payload may contain a secret",
message: "Sensitive-looking field names were found in serialized client state.",
element: script,
recommendation: "Move sensitive state to server-only state and regenerate the page.",
confidence: "medium",
}),
);
}
}
return issues;
},
},
+7
View File
@@ -79,6 +79,13 @@ export interface DevToolbarMetrics {
longTasks?: number;
layoutShifts?: number;
pageLoadMs?: number;
hydrationMs?: number;
hydratedComponents?: number;
javascriptBytes?: number;
cssBytes?: number;
lcpMs?: number;
cls?: number;
inpMs?: number;
}
export interface DevToolbarPageReport {