Files
sigpro/sigpro.js
2026-03-16 18:32:03 +01:00

2 lines
8.0 KiB
JavaScript

let activeEffect=null;const effectQueue=new Set;let isFlushScheduled=!1,flushCount=0;const flushEffectQueue=()=>{if(isFlushScheduled=!1,flushCount++,flushCount>100)throw effectQueue.clear(),flushCount=0,new Error("SigPro: Infinite reactive loop detected.");try{const e=Array.from(effectQueue);effectQueue.clear();for(const t of e)t.run()}catch(e){console.error("SigPro Flush Error:",e)}finally{setTimeout((()=>{flushCount=0}),0)}},$=e=>{const t=new Set;let n;if("function"==typeof e){let s,c=!0;const o={dependencies:new Set,markDirty:()=>{c||(c=!0,t.forEach((e=>{e.markDirty&&e.markDirty(),effectQueue.add(e)})),!isFlushScheduled&&effectQueue.size&&(isFlushScheduled=!0,queueMicrotask(flushEffectQueue)))},run:()=>{o.dependencies.forEach((e=>e.delete(o))),o.dependencies.clear();const t=activeEffect;activeEffect=o;try{s=e()}finally{activeEffect=t,c=!1}}};n=()=>(activeEffect&&(t.add(activeEffect),activeEffect.dependencies.add(t)),c&&o.run(),s)}else n=(...n)=>{if(n.length){const s="function"==typeof n[0]?n[0](e):n[0];Object.is(e,s)||(e=s,t.forEach((e=>{e.markDirty&&e.markDirty(),effectQueue.add(e)})),!isFlushScheduled&&effectQueue.size&&(isFlushScheduled=!0,queueMicrotask(flushEffectQueue)))}return activeEffect&&(t.add(activeEffect),activeEffect.dependencies.add(t)),e};return n};let currentPageCleanups=null;const $e=e=>{const t={dependencies:new Set,cleanupHandlers:new Set,run(){this.cleanupHandlers.forEach((e=>e())),this.cleanupHandlers.clear(),this.dependencies.forEach((e=>e.delete(this))),this.dependencies.clear();const t=activeEffect;activeEffect=this;try{const t=e();"function"==typeof t&&this.cleanupHandlers.add(t)}finally{activeEffect=t}},stop(){this.cleanupHandlers.forEach((e=>e())),this.dependencies.forEach((e=>e.delete(this)))}};return currentPageCleanups&&currentPageCleanups.push((()=>t.stop())),activeEffect&&activeEffect.cleanupHandlers.add((()=>t.stop())),t.run(),()=>t.stop()},$s=(e,t,n=localStorage)=>{let s;try{const c=n.getItem(e);s=null!==c?JSON.parse(c):t}catch(c){console.warn(`Error reading ${e} from storage:`,c),s=t,n.removeItem(e)}const c=$(s);return $e((()=>{try{const t=c();null==t?n.removeItem(e):n.setItem(e,JSON.stringify(t))}catch(t){console.warn(`Error saving ${e} to storage:`,t)}})),c},html=(e,...t)=>{const n=html._templateCache??(html._templateCache=new WeakMap);let s=n.get(e);if(!s){const t=document.createElement("template");t.innerHTML=e.join("{{part}}");const c=[],o=document.createTreeWalker(t.content,133),r=e=>{const n=[];for(;e&&e!==t.content;){let t=0;for(let n=e.previousSibling;n;n=n.previousSibling)t++;n.push(t),e=e.parentNode}return n.reverse()};let i;for(;i=o.nextNode();){let e=!1;const t={type:i.nodeType,path:r(i),parts:[]};if(1===i.nodeType)for(let n=0;n<i.attributes.length;n++){const s=i.attributes[n];s.value.includes("{{part}}")&&(t.parts.push({name:s.name}),e=!0)}else 3===i.nodeType&&i.textContent.includes("{{part}}")&&(e=!0);e&&c.push(t)}n.set(e,s={template:t,dynamicNodes:c})}const c=s.template.content.cloneNode(!0);let o=0;return s.dynamicNodes.map((e=>{return{node:(t=c,n=e.path,n.reduce(((e,t)=>e?.childNodes?.[t]),t)),info:e};var t,n})).forEach((({node:e,info:n})=>{if(e)if(1===n.type)n.parts.forEach((n=>{const s=t[o++],c=n.name,r=c[0];if("@"===r){const[t,...n]=c.slice(1).split("."),o=c=>{if(n.includes("prevent")&&c.preventDefault(),n.includes("stop")&&c.stopPropagation(),!n.includes("self")||c.target===e){if(n.some((e=>e.startsWith("debounce")))){const t=n.find((e=>e.startsWith("debounce")))?.split(":")[1]||300;return clearTimeout(e._debounceTimer),void(e._debounceTimer=setTimeout((()=>s(c)),t))}n.includes("once")&&e.removeEventListener(t,o),s(c)}};e.addEventListener(t,o,{passive:n.includes("passive"),capture:n.includes("capture")})}else if(":"===r){const t=c.slice(1),n="checkbox"===e.type||"radio"===e.type?"change":"input";"function"==typeof s?$e((()=>{const n=s();e[t]!==n&&(e[t]=n)})):e[t]=s,e.addEventListener(n,(()=>{const t="change"===n?e.checked:e.value;"function"==typeof s&&s(t)}))}else if("?"===r){const t=c.slice(1);"function"==typeof s?$e((()=>{const n=s();e.toggleAttribute(t,!!n)})):e.toggleAttribute(t,!!s)}else if("."===r){const t=c.slice(1);"function"==typeof s?$e((()=>{const n=s();e[t]=n,null!=n&&"object"!=typeof n&&"boolean"!=typeof n&&e.setAttribute(t,n)})):(e[t]=s,null!=s&&"object"!=typeof s&&"boolean"!=typeof s&&e.setAttribute(t,s))}else"function"==typeof s?$e((()=>e.setAttribute(c,s()))):e.setAttribute(c,s)}));else if(3===n.type){const n=e.textContent.split("{{part}}").length-1;((e,t)=>{const n=e.textContent.split("{{part}}"),s=e.parentNode;let c=0;n.forEach(((o,r)=>{if(o&&s.insertBefore(document.createTextNode(o),e),r<n.length-1){const a=t[c++],l=document.createComment("s"),u=document.createComment("e");if(s.insertBefore(l,e),s.insertBefore(u,e),"function"==typeof a){let f;$e((()=>{const e=a();e!==f&&(f=e,i(e))}))}else i(a);function i(e){if("object"==typeof e||Array.isArray(e)){for(;l.nextSibling!==u;)s.removeChild(l.nextSibling);const t=Array.isArray(e)?e:[e],n=document.createDocumentFragment();t.forEach((e=>{if(null==e||!1===e)return;const t=e instanceof Node?e:document.createTextNode(e);n.appendChild(t)})),s.insertBefore(n,u)}else{const t=l.nextSibling,n=String(e??"");if(t!==u&&3===t?.nodeType)t.textContent=n;else{for(;l.nextSibling!==u;)s.removeChild(l.nextSibling);s.insertBefore(document.createTextNode(n),u)}}}}})),e.remove()})(e,t.slice(o,o+n)),o+=n}})),c},$p=e=>{const t="page-"+Math.random().toString(36).substring(2,9);return customElements.define(t,class extends HTMLElement{connectedCallback(){this.style.display="contents",this._cleanups=[],currentPageCleanups=this._cleanups;try{const t=e({params:JSON.parse(this.getAttribute("params")||"{}"),onUnmount:e=>this._cleanups.push(e)});this.appendChild(t instanceof Node?t:document.createTextNode(String(t)))}finally{currentPageCleanups=null}}disconnectedCallback(){this._cleanups.forEach((e=>e())),this._cleanups=[],this.innerHTML=""}}),(e={})=>{const n=document.createElement(t);return n.setAttribute("params",JSON.stringify(e)),n}},$c=(e,t,n=[])=>{customElements.get(e)||customElements.define(e,class extends HTMLElement{static get observedAttributes(){return n}constructor(){super(),this._propertySignals={},this.cleanupFunctions=[],n.forEach((e=>this._propertySignals[e]=$(void 0)))}connectedCallback(){const e=[...this.childNodes];this.innerHTML="",n.forEach((e=>{const t=this.hasOwnProperty(e)?this[e]:this.getAttribute(e);Object.defineProperty(this,e,{get:()=>this._propertySignals[e](),set:t=>{const n="false"!==t&&(""===t&&"value"!==e||t);this._propertySignals[e](n)},configurable:!0}),null!=t&&(this[e]=t)}));const s={select:e=>this.querySelector(e),slot:t=>e.filter((e=>{const n=1===e.nodeType?e.getAttribute("slot"):null;return t?n===t:!n})),emit:(e,t)=>this.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0})),host:this,onUnmount:e=>this.cleanupFunctions.push(e)},c=t(this._propertySignals,s);c instanceof Node&&this.appendChild(c)}attributeChangedCallback(e,t,n){this[e]!==n&&(this[e]=n)}disconnectedCallback(){this.cleanupFunctions.forEach((e=>e())),this.cleanupFunctions=[]}})},$f=async(e,t,n)=>{n&&n(!0);try{const n=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),s=await n.text();try{return JSON.parse(s)}catch(e){return console.warn("Invalid JSON response"),null}}catch(e){return null}finally{n&&n(!1)}},$r=e=>{const t=document.createElement("div");t.style.display="contents";const n=()=>{const n=window.location.hash.replace(/^#/,"")||"/";let s=e.find((e=>e.path instanceof RegExp?n.match(e.path):e.path===n)),c={};if(s?.path instanceof RegExp){const e=n.match(s.path);c=e.groups||{id:e[1]}}const o=s?s.component(c):Object.assign(document.createElement("h1"),{textContent:"404"});t.replaceChildren(o instanceof Node?o:document.createTextNode(String(o??"")))};return window.addEventListener("hashchange",n),n(),t};$r.go=e=>{const t=e.startsWith("/")?e:`/${e}`;window.location.hash!==`#${t}`&&(window.location.hash=t)},$.effect=$e,$.page=$p,$.component=$c,$.fetch=$f,$.router=$r,$.storage=$s,"undefined"!=typeof window&&(window.$=$);export{$,html};