UNPKG

hooktml

Version:

A reactive HTML component library with hooks-based lifecycle management

4 lines (3 loc) 25.1 kB
(()=>{var E=e=>e===null||typeof e>"u",V=e=>typeof e>"u",k=e=>!E(e),Me=e=>typeof e=="string"&&e.trim()!==""&&!isNaN(Number(e)),L=e=>typeof e=="string",B=e=>L(e)&&e.length>0,M=e=>L(e)&&e.length===0,A=e=>typeof e=="object"&&e!==null&&!Array.isArray(e)&&Object.keys(e).length>0,g=e=>typeof e=="function",R=e=>Array.isArray(e),H=e=>R(e)&&e.length===0,b=e=>R(e)&&e.length>0,d=e=>e instanceof HTMLElement,le=e=>!e||typeof e!="object"?!1:!!(e instanceof HTMLElement||e===document||"nodeType"in e&&e.nodeType===9&&"addEventListener"in e&&g(e.addEventListener)||e===window||"window"in e&&e.window===e&&"addEventListener"in e&&g(e.addEventListener)),Se=e=>b(e)&&e.every(le),I=e=>b(e)&&e.every(d),U=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),T=e=>A(e)&&"value"in e&&"subscribe"in e&&g(e.subscribe);var Le={componentPath:void 0,debug:!1,attributePrefix:"",formattedPrefix:""},ve={...Le},Je=e=>E(e)||!L(e)||M(e)?"":e.endsWith("-")?e:`${e}-`,Ae=(e={})=>{let t={...e};"attributePrefix"in t&&(t.formattedPrefix=Je(t.attributePrefix)),ve={...Le,...t}},y=()=>({...ve});var G=e=>`[HookTML] ${e}`,a={log:(e,...t)=>{let{debug:r}=y();r&&g(console.log)&&console.log(G(e),...t)},info:(e,...t)=>{let{debug:r}=y();r&&g(console.info)&&console.info(G(e),...t)},warn:(e,...t)=>{g(console.warn)&&console.warn(G(e),...t)},error:(e,...t)=>{g(console.error)&&console.error(G(e),...t)}};var $e=e=>M(e)?!1:e.startsWith("use")&&e.length>3,Q=new Map,pe=new Map,me=e=>{if(!g(e))return a.warn("Invalid hook: must be a function"),!1;let t=e.name;if(M(t))return a.warn("Invalid hook: must be a named function"),!1;if(!$e(t))return a.warn(`Invalid hook name: "${t}". Hook names must start with "use"`),!1;let r=!Q.has(t);return r&&(Q.set(t,e),a.log(`Registered hook: ${t}`)),r},ge=e=>{if(!g(e))return a.warn("Invalid chainable hook: must be a function"),!1;let t=e.name;if(M(t))return a.warn("Invalid chainable hook: must be a named function"),!1;if(!$e(t))return a.warn(`Invalid chainable hook name: "${t}". Hook names must start with "use"`),!1;let r=!pe.has(t);return r&&(pe.set(t,e),a.log(`Registered chainable hook: ${t}`)),r};var Re=e=>{if(L(e))return Q.get(e)};var N=()=>new Map(Q),Z=()=>new Map(pe);var w=({fn:e,onError:t,onFinally:r})=>{try{return e()}catch(o){return t(o)}finally{k(r)&&r()}};var j=[],J=new WeakMap,Y=new WeakMap,_=new WeakMap,D=new WeakMap,q=new WeakMap,Ye=e=>{let t={element:e,effectQueue:[],cleanups:[]},r=J.get(e)||[];return J.set(e,r),_.set(e,0),t},Xe=()=>j.length>0?j[j.length-1]:null,et=(e,t,r)=>{let o;return w({fn:()=>{let n=D.get(t);n||(n=new Map,D.set(t,n));let c=n.get(r);g(c)&&X(c),o=e(),g(o)&&n.set(r,o);let i=q.get(t);i||(i=new Set,q.set(t,i)),i.add(r)},onError:n=>{a.error("Error in effect execution:",n)}}),o},tt=e=>{let{element:t,effectQueue:r}=e;if(H(r))return;a.log(`Executing ${r.length} effect(s) for element:`,t);let o=q.get(t);o||(o=new Set,q.set(t,o)),r.forEach((n,c)=>{o.has(c)||et(n,t,c)}),r.length=0,_.set(t,0)},ee=(e,t)=>{let r=Ye(e);return j.push(r),w({fn:()=>{let o=t();return tt(r),o},onError:o=>(a.error("Error in withHookContext:",o),null),onFinally:()=>{j.pop()}})},X=e=>{g(e)&&w({fn:e,onError:t=>{a.error("Error in effect cleanup:",t)}})},x=(e,t)=>{let r=Xe();if(!r){a.warn("useEffect called outside component/directive context");return}if(E(t))throw new Error("[HookTML] useEffect requires a dependencies array. For one-time effects, use an empty array [].");if(!R(t))throw new Error("[HookTML] useEffect dependencies must be an array.");let{element:o}=r,n=_.get(o)||0;if(_.set(o,n+1),b(t)){let i=t.filter(u=>!T(u)&&!E(u));if(!H(i)){let{debug:u}=y(),l=p=>U(p)?JSON.stringify(p).slice(0,50):String(p),s=u?` Non-reactive values: ${i.map(l).join(", ")}`:"";a.warn(`useEffect dependency array contains ${i.length} non-signal value(s) that won't trigger re-runs. To make values reactive, convert them to signals with signal().${s}`)}}let c=()=>{let i=Y.get(o);i||(i=new Map,Y.set(o,i));let u=i.get(n);u||(u=new Set,i.set(n,u)),u.forEach(s=>s()),u.clear(),t.forEach(s=>{if(T(s)){let p=s.subscribe(()=>{l()});u.add(p)}});let l=()=>{let s=D.get(o);s||(s=new Map,D.set(o,s));let p=s.get(n);g(p)&&X(p);let f=e();return g(f)&&s.set(n,f),f};return l()};r.effectQueue.push(c)},Ne=e=>{let t=J.get(e),r=!1;k(t)&&b(t)&&(t.forEach(c=>{X(c)}),J.delete(e),r=!0);let o=Y.get(e);o&&(o.forEach(c=>{c.forEach(i=>i()),c.clear()}),Y.delete(e),r=!0);let n=D.get(e);return n&&(n.forEach(c=>{X(c)}),D.delete(e),r=!0),_.delete(e),q.delete(e),r};var v=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),te=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),re=e=>/[^aeiou]y$/.test(e)?e.slice(0,-1)+"ies":/(s|x|z|ch|sh)$/.test(e)?e+"es":e+"s";var de=(e,t)=>{if(!d(e))throw new Error("[HookTML] useChildren requires an HTMLElement as first argument");if(E(t)||!B(t))throw new Error("[HookTML] useChildren requires a non-empty string prefix as second argument");let r=`[use-${t}]`,o=`${t}-`,n={},c={},i=e.getElementsByTagName("*");for(let u=0;u<i.length;u++){let l=i[u];if(!d(l))continue;let s=!1,p=[];for(let m=0;m<l.attributes.length;m++){let h=l.attributes[m];h.name.startsWith(o)&&(s=!0,p.push(h))}if(!s)continue;let f=l.closest(r);if(!(f&&f!==e))for(let m=0;m<p.length;m++){let C=p[m].name.slice(o.length),$=v(C);R(c[$])||(c[$]=[]),c[$].push(l)}}return Object.keys(c).forEach(u=>{let l=c[u],s=re(u);n[u]=l[0],n[s]=l}),n};var O=(e,t,r=[])=>{if(E(e))return a.info("[HookTML] useEvents called with null/undefined element, skipping event registration"),()=>{};if(H(e))return a.info("[HookTML] useEvents called with empty array, skipping event registration"),()=>{};let o=le(e),n=Se(e);if(!o&&!n)throw new Error("[HookTML] useEvents requires an EventTarget or array of EventTargets as first argument");let c=n?e:[e];if(!A(t))throw new Error("[HookTML] useEvents requires a non-empty object mapping event names to listeners");let u=Object.values(t).filter(T).concat(r),l=new Map,s=()=>{l.forEach((f,m)=>{c.forEach(h=>{h.removeEventListener(m,f)})}),l.clear();let p=Object.entries(t).filter(([f,m])=>{let h=T(m)?m.value:m;if(!g(h)){a.warn(`Event handler for '${f}' is not a function, skipping`);return}return[f,h]});c.forEach((f,m)=>{p.forEach(([h,C])=>{let $=Ze=>{g(C)&&C(Ze,m)};f.addEventListener(h,$),l.set(h,$)})})};return s(),b(u)&&x(()=>{s()},u),()=>{l.forEach((p,f)=>{c.forEach(m=>{m.removeEventListener(f,p)})}),l.clear()}};var P=(e,t,r=[])=>{if(E(e))return a.info("[HookTML] useClasses called with null/undefined element, skipping class application"),()=>{};if(H(e))return a.info("[HookTML] useClasses called with empty array, skipping class application"),()=>{};let o=I(e)?e:[e];if(o.some(s=>!d(s)))throw new Error("[HookTML] useClasses requires HTMLElement(s) as first argument");if(!A(t))throw new Error("[HookTML] useClasses requires a non-empty object mapping class names to boolean conditions");let c=Object.values(t).filter(T).concat(r),i=new WeakMap,u=(s,p,f)=>g(s)?!!s(p,f):T(s)?!!s.value:!!s,l=()=>{o.forEach((s,p)=>{let f=i.get(s);f||(f=new Set,i.set(s,f)),f.forEach(m=>{s.classList.remove(m)}),f.clear(),Object.entries(t).forEach(([m,h])=>{u(h,s,p)&&(s.classList.add(m),f.add(m))})})};return l(),b(c)&&x(()=>{l()},c),()=>{o.forEach(s=>{let p=i.get(s);p&&(p.forEach(f=>{s.classList.remove(f)}),p.clear())})}};var F=(e,t,r=[])=>{if(E(e))return a.info("[HookTML] useAttributes called with null/undefined element, skipping attribute setting"),()=>{};if(H(e))return a.info("[HookTML] useAttributes called with empty array, skipping attribute setting"),()=>{};let o=I(e)?e:[e];if(o.some(s=>!d(s)))throw new Error("[HookTML] useAttributes requires HTMLElement(s) as first argument");if(!A(t))throw new Error("[HookTML] useAttributes requires a non-empty object mapping attribute names to values");let n=Object.values(t).filter(T),c=n.concat(r),i=new WeakMap,u=(s,p,f)=>g(s)?s(p,f):T(s)?s.value:s,l=()=>{o.forEach((s,p)=>{let f=i.get(s);f||(f=new Map,i.set(s,f)),Object.entries(t).forEach(([m,h])=>{f.has(m)||f.set(m,s.hasAttribute(m)?s.getAttribute(m):null);let C=u(h,s,p);E(C)?s.removeAttribute(m):s.setAttribute(m,C)})})};return l(),b(c)&&w({fn:()=>{x(()=>{l()},c)},onError:s=>{a.error("Error in useAttributes:",s);let p=n.map(f=>T(f)?f.subscribe(()=>l()):null).filter(k);o.forEach(f=>{let m=i.get(f);if(m){let h=m.get("__cleanup");m.set("__cleanup",()=>{p.forEach(C=>C()),g(h)&&h()})}})}}),()=>{o.forEach(s=>{let p=i.get(s);if(p){let f=p.get("__cleanup");g(f)&&f(),p.forEach((m,h)=>{h!=="__cleanup"&&(E(m)?s.removeAttribute(h):s.setAttribute(h,m))}),p.clear()}})}};var W=(e,t,r=[])=>{if(E(e))return a.info("[HookTML] useStyles called with null/undefined element, skipping style application"),()=>{};if(H(e))return a.info("[HookTML] useStyles called with empty array, skipping style application"),()=>{};let o=I(e)?e:[e];if(o.some(s=>!d(s)))throw new Error("[HookTML] useStyles requires HTMLElement(s) as first argument");if(!A(t))throw new Error("[HookTML] useStyles requires a non-empty object mapping style properties to values");let n=Object.values(t).filter(T),c=n.concat(r),i=new WeakMap,u=(s,p,f)=>g(s)?s(p,f):T(s)?s.value:s,l=()=>{o.forEach((s,p)=>{let f=i.get(s);f||(f=new Map,i.set(s,f)),Object.entries(t).forEach(([m,h])=>{let C=m.includes("-")?v(m):m;f.has(C)||f.set(C,s.style[C]);let $=u(h,s,p);s.style[C]=$})})};return l(),b(c)&&w({fn:()=>{x(()=>{l()},c)},onError:s=>{a.error("Error in useStyles:",s);let p=n.map(f=>f.subscribe(()=>l()));o.forEach(f=>{let m=i.get(f);if(m){let h=m.get("__cleanup");m.set("__cleanup",()=>{p.forEach(C=>C()),g(h)&&h()})}})}}),()=>{o.forEach(s=>{let p=i.get(s);if(p){let f=p.get("__cleanup");g(f)&&f(),p.forEach((m,h)=>{h!=="__cleanup"&&(s.style[h]=m)}),p.clear()}})}};var K=(e,t,r=[])=>{if(E(e)){a.info("[HookTML] useText called with null/undefined element, skipping text updates");return}if(!d(e)){a.info("[HookTML] useText requires HTMLElement as first argument");return}if(!g(t)){a.info("[HookTML] useText requires a function as the second argument");return}x(()=>{e.textContent=t()},r)};var he=e=>{if(!d(e))throw new Error("[HookTML] with(el) requires an HTMLElement as argument");let t={useEvents:o=>(O(e,o),t),useClasses:o=>(P(e,o),t),useAttributes:o=>(F(e,o),t),useStyles:o=>(W(e,o),t),useText:o=>(K(e,o),t)};return Z().forEach((o,n)=>{g(o)&&!t[n]&&(t[n]=(...c)=>(o(e,...c),t))}),t};var oe=new Map,rt=e=>M(e)?!1:/^[A-Z][A-Za-z0-9]*$/.test(e),ye=e=>{if(!g(e))return a.warn("Invalid component: must be a function"),!1;let t=e.name;if(!rt(t))return a.warn(`Invalid component name: "${t}". Must be a non-empty PascalCase name`),!1;let r=!oe.has(t);return r&&(oe.set(t,e),a.log(`Registered component: ${t}`)),r},z=()=>Array.from(oe.keys()),ze=e=>{if(L(e))return oe.get(e)};var ne=class{constructor(){this.stateRegistry=new WeakMap}markInitialized(t){if(!d(t))throw new Error("[HookTML] markInitialized requires an HTMLElement");let r=this.getOrCreateState(t);r.initialized=!0}markDirectiveInitialized(t,r){if(!d(t))throw new Error("[HookTML] markDirectiveInitialized requires an HTMLElement");if(!r)throw new Error("[HookTML] directiveName is required");let o=this.getOrCreateState(t);o.initializedDirectives.includes(r)||o.initializedDirectives.push(r)}isInitialized(t){return d(t)?this.stateRegistry.get(t)?.initialized??!1:!1}isDirectiveInitialized(t,r){return!d(t)||!r?!1:this.stateRegistry.get(t)?.initializedDirectives.includes(r)??!1}getInitializedDirectives(t){return d(t)?this.stateRegistry.get(t)?.initializedDirectives??[]:[]}clearState(t){d(t)&&this.stateRegistry.delete(t)}getOrCreateState(t){let r=this.stateRegistry.get(t);return r||(r={initialized:!1,initializedDirectives:[]},this.stateRegistry.set(t,r)),r}};var se=class{constructor(){this.teardownRegistry=new WeakMap,this.stateManager=new ne}registerComponent(t,r){if(!d(t))throw new Error("[HookTML] registerComponent requires an HTMLElement");if(!g(r))return!1;let o=this.teardownRegistry.get(t);return o||(o={component:void 0,directives:[]},this.teardownRegistry.set(t,o)),o.component=r,this.stateManager.markInitialized(t),!0}registerDirective(t,r,o){if(!d(t))throw new Error("[HookTML] registerDirective requires an HTMLElement");if(!g(r))return!1;if(!o)throw new Error("[HookTML] directiveName is required");let n=this.teardownRegistry.get(t);return n||(n={component:void 0,directives:[]},this.teardownRegistry.set(t,n)),n.directives.push(r),this.stateManager.markDirectiveInitialized(t,o),!0}getComponentTeardown(t){if(!d(t))throw new Error("[HookTML] getComponentTeardown requires an HTMLElement");return this.teardownRegistry.get(t)?.component}getDirectiveTeardowns(t){if(!d(t))throw new Error("[HookTML] getDirectiveTeardowns requires an HTMLElement");return this.teardownRegistry.get(t)?.directives??[]}getTeardowns(t){if(!d(t))throw new Error("[HookTML] getTeardowns requires an HTMLElement");return this.teardownRegistry.get(t)??{component:void 0,directives:[]}}hasRegistration(t){if(!d(t))return!1;let r=this.teardownRegistry.get(t);return k(r)&&(g(r.component)||b(r.directives))}executeComponentTeardown(t){if(!d(t))throw new Error("[HookTML] executeComponentTeardown requires an HTMLElement");let r=this.teardownRegistry.get(t);if(!r?.component)return{success:!0,error:void 0};let o=r.component;return w({fn:()=>(o(),r.component=void 0,{success:!0,error:void 0}),onError:n=>(a.error("Error in component teardown:",n),{success:!1,error:n})})}executeDirectiveTeardowns(t){if(!d(t))throw new Error("[HookTML] executeDirectiveTeardowns requires an HTMLElement");let r=this.teardownRegistry.get(t);if(!r?.directives.length)return[];let o=r.directives.map(n=>w({fn:()=>(n(),{success:!0,error:void 0}),onError:c=>(a.error("Error in directive teardown:",c),{success:!1,error:c})}));return r.directives=[],o}executeTeardowns(t){if(!d(t))throw new Error("[HookTML] executeTeardowns requires an HTMLElement");let r=this.executeComponentTeardown(t),o=this.executeDirectiveTeardowns(t);return this.teardownRegistry.delete(t),this.stateManager.clearState(t),{component:r,directives:o}}isInitialized(t){return this.stateManager.isInitialized(t)}isDirectiveInitialized(t,r){return this.stateManager.isDirectiveInitialized(t,r)}getInitializedDirectives(t){return this.stateManager.getInitializedDirectives(t)}markInitialized(t){this.stateManager.markInitialized(t)}clearState(t){this.stateManager.clearState(t)}};var S=new se;var Ie=e=>{d(e)&&S.markInitialized(e)};var ot=(e,t)=>{let{formattedPrefix:r}=y();return e.classList.contains(t)||d(e)&&e.getAttribute(`${r}use-component`)===t},nt=(e,t,r)=>{let o=re(t);o in e?R(e[o])&&e[o].push(r):e[o]=[e[t],r]},De=(e,t)=>{let{formattedPrefix:r}=y(),o=`${r}${t.toLowerCase()}-`,n={};return Array.from(e.getElementsByTagName("*")).some(i=>ot(i,t)?!0:(Array.from(i.attributes).forEach(({name:u})=>{if(u.startsWith(o)){let l=v(u.slice(o.length));n[l]?nt(n,l,i):n[l]=i}}),!1)),n};var Ee=e=>e==="true"?!0:e==="false"?!1:e==="null"?null:Me(e)?Number(e):e,je=(e,t)=>{let{formattedPrefix:r}=y(),o=`${r}${t.toLowerCase()}-`,n={};Array.from(e.attributes).forEach(({name:i,value:u})=>{if(i.startsWith(o)){let l=v(i.slice(o.length));n[l]=Ee(u)}});let c=De(e,t);return Object.keys(c).length>0&&(n.children=c),n};var ie=e=>{if(!d(e))throw new Error("[HookTML] removeCloak requires an HTMLElement");e.removeAttribute("data-hooktml-cloak")};var _e="__hooktml",st="[data-hooktml-cloak] { visibility: hidden; }",it=()=>{let e=document.getElementById(_e);if(e instanceof HTMLStyleElement)return e;let t=document.createElement("style");return t.id=_e,t.textContent=st,document.head.appendChild(t),t},at=e=>y().componentSelectorMode==="class"?`.${e.name}`:`[data-component="${e.name}"]`,ct=e=>e?e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\s*([{};:,])\s*/g,"$1").replace(/\s+/g," ").trim():"",qe=e=>ct(e),ut=e=>{let t=e.styles?.trim()??"";return`${at(e)} { ${t} }`},ft=(e,t)=>{let r=qe(t);return Array.from(e.cssRules).some(o=>qe(o.cssText)===r)},Oe=(e,t)=>{let{debug:r}=y();if(!d(t))throw new Error("[HookTML] injectComponentStyles requires an HTMLElement as second argument");if(k(e.styles)&&!L(e.styles)){r&&a.warn(`Component "${e.name}" has non-string styles property (type: ${typeof e.styles}). Styles must be a string.`,e),ie(t);return}if(E(e.styles)||M(e.styles)){ie(t);return}let n=it().sheet,c=ut(e);k(n)&&(ft(n,c)?a.warn(`Duplicate style injection skipped for component "${e.name}". Styles already present in stylesheet.`,t):(n.insertRule(c,n.cssRules.length),a.log(`Injected styles for component "${e.name}"`))),ie(t)};var lt=e=>e.map(t=>`.${t}`).join(", "),pt=(e,t="")=>e.map(r=>`[${t}use-component="${r}"]`).join(", "),mt=(e,t,r="")=>{let n=Array.from(e.classList).find(i=>t.includes(i));if(k(n))return n;let c=e.getAttribute(`${r}use-component`);return k(c)&&t.includes(c)?c:null},ae=()=>{a.log("Scanning for components...");let e=z(),{formattedPrefix:t}=y();if(!e.length)return a.log("No components registered yet"),[];let r=lt(e),o=pt(e,t),n=`${r}, ${o}`;return a.log(`Scanning DOM with selector: "${n}"`),Array.from(document.querySelectorAll(n)).map(i=>{let u=mt(i,e,t);return k(u)?{element:i,componentName:u}:null}).filter(k)},ce=e=>{if(E(e)||H(e))return a.log("No components to initialize"),[];a.log(`Initializing ${e.length} component(s)...`);let t=e.map(({element:r,componentName:o})=>{if(S.isInitialized(r))return a.log(`Skipping already initialized component: ${o}`),null;let n=ze(o);return E(n)?(a.warn(`No registered function found for component: ${o}`),null):w({fn:()=>{a.log(`Initializing component: ${o}`);let c=je(r,o),i=ee(r,()=>n(r,c));return i===null?null:(g(i)?S.registerComponent(r,i):U(i)&&(k(i.context)&&Object.defineProperty(r,"component",{value:i.context,writable:!0,configurable:!0}),g(i.cleanup)&&S.registerComponent(r,i.cleanup)),Oe(n,r),Ie(r),{element:r,componentName:o,instance:i})},onError:c=>(a.error(`Error initializing component ${o}:`,c),null)})}).filter(k);return a.log(`Successfully initialized ${t.length} component(s)`),t};var ue=new WeakMap,Pe=(e,t)=>{if(!d(e))return;let r=ue.get(e);if(r)return r.get(t)},Fe=(e,t,r)=>{if(!d(e))return;let o=ue.get(e);o||(o=new Map,ue.set(e,o)),o.set(t,r),a.log(`Stored hook instance for "${t}" on element:`,e)},We=e=>{d(e)&&ue.delete(e)};var gt=(e,t="")=>e.length?e.map(o=>`[${t}${te(o)}]`).join(", "):"",dt=(e,t="")=>{let r=Array.from(e.attributes),o=`${t}use-`;return r.filter(n=>n.name.startsWith(o)).map(n=>({name:n.name.substring(t.length),originalName:n.name,value:n.value}))},we=e=>{if(S.hasRegistration(e)){a.log("\u23ED\uFE0F Skipping already processed element",e);return}let{formattedPrefix:r}=y(),o=dt(e,r);a.log(`Processing element with ${o.length} hook(s):`,e),o.forEach(({name:n,originalName:c,value:i})=>{let u=v(n);a.log(`Looking for hook "${u}" from attribute "${c}"`);let l=Re(u);if(k(l)&&g(l)){if(a.log(`Found hook "${u}" for element:`,e),Pe(e,u)){a.log(`Using existing instance for hook "${u}"`);return}let p=M(i)?!0:Ee(i);k(i)&&a.log(`Passing value to hook "${u}":`,p);let f={};B(i)&&(f.value=p);let m={current:void 0};if(w({fn:()=>{a.log(`Calling hook function for "${u}" with hook context`),m.current=ee(e,()=>{let h=l(e,f);return Fe(e,u,h),h}),a.log(`Hook "${u}" returned:`,m.current,typeof m.current)},onError:h=>{a.error(`Error applying hook "${u}":`,h)}}),g(m.current)){a.log(`Storing teardown function for hook "${u}" on element:`,e),S.registerDirective(e,m.current,u);let h=S.hasRegistration(e);a.log(`\u2705 Verified teardown is registered: ${h}`)}else a.log(`Hook "${u}" did not return a teardown function`)}else a.warn(`Unknown hook "${u}" requested on element:`,e)})},Ke=()=>{let e=N(),t=Array.from(e.keys()),{formattedPrefix:r}=y();if(H(t))return a.log("No hooks registered yet"),0;let o=gt(t,r);a.log(`Scanning DOM for hook directives with selector: "${o}"`);let n=Array.from(document.querySelectorAll(o)).filter(d);return a.log(`Found ${n.length} element(s) with hook directives`),n.forEach(c=>we(c)),n.length};var ht=e=>e.nodeType===Node.ELEMENT_NODE,yt=(e,t)=>{let r=t.removedNodes||[];Array.from(r).filter(n=>d(n)&&ht(n)).flatMap(n=>{let c=n,i=Array.from(c.getElementsByTagName("*")).filter(d);return[c,...i]}).forEach(n=>{e.elements.has(n)&&(e.delegate.removeElement(n),e.elements.delete(n))}),ke(e)},ke=e=>{if(!e.started)return;let t=new Set(e.delegate.matchElements(e.root));Array.from(e.elements).filter(n=>!t.has(n)).forEach(n=>{e.delegate.removeElement(n),e.elements.delete(n)}),Array.from(t).filter(n=>!e.elements.has(n)).forEach(n=>{e.delegate.addElement(n),e.elements.add(n)})},Et=(e,t)=>{let r={root:e,delegate:t,elements:new Set,started:!1},o=new MutationObserver(s=>{r.started&&s.forEach(p=>yt(r,p))}),n=()=>o.observe(e,{attributes:!0,childList:!0,subtree:!0}),c=()=>o.disconnect();return{start:()=>{r.started||(r.started=!0,n(),ke(r))},stop:()=>{r.started&&(o.takeRecords&&o.takeRecords(),c(),r.started=!1)},pause:s=>{r.started&&(c(),r.started=!1),s(),r.started||(n(),r.started=!0)},refresh:()=>ke(r)}},Ve=(e,t="")=>e.length?e.map(o=>`[${t}${te(o)}]`).join(", "):"",Be=(e,t="")=>{if(!e.length)return"";let r=e.map(n=>`.${n}`).join(", "),o=e.map(n=>`[${t}use-component="${n}"]`).join(", ");return`${r}, ${o}`},wt=()=>({matchElements:o=>{let{formattedPrefix:n}=y(),c=N(),i=Array.from(c.keys()),u=z(),l=[];return b(i)&&l.push(Ve(i,n)),b(u)&&l.push(Be(u,n)),H(l)?[]:Array.from(o.querySelectorAll(l.join(", "))).filter(d)},addElement:o=>{w({fn:()=>{let{formattedPrefix:n}=y(),c=N(),i=Array.from(c.keys());if(b(i)){let l=Ve(i,n);o.matches(l)&&we(o)}let u=z();if(b(u)){let l=Be(u,n);if(o.matches(l)){let s=ae().filter(p=>p.element===o);b(s)&&ce(s)}}},onError:n=>{y().debug&&a.error("Error processing element:",n)}})},removeElement:o=>{w({fn:()=>{S.executeTeardowns(o),Ne(o),We(o)},onError:n=>{y().debug&&a.error("Error removing element:",n)}})}}),Ue=()=>{let e=wt(),t=Et(document.body,e);return{start:()=>{t.start(),a.log("DOM observation started")},stop:()=>{t.stop(),a.log("DOM Observer stopped")}}};var be=e=>{let t={current:e},r=new Set,o={get value(){return!V(globalThis)&&globalThis.__HOOKTML_TRACK_SIGNAL__&&globalThis.__HOOKTML_TRACK_SIGNAL__(o),t.current},set value(n){t.current!==n&&(t.current=n,r.size>0&&r.forEach(c=>{w({fn:()=>c(n),onError:i=>{a.error("Error in signal subscriber:",i)}})}))},subscribe(n){if(!g(n))throw new Error("[HookTML] Signal subscribers must be functions");return r.add(n),()=>{r.delete(n)}},destroy(){r.clear()},toString(){return`Signal(${t.current})`}};return o};var Te=class e{static instance=null;constructor(){this.currentTracker=null,this.computingSignals=new Set}static getInstance(){return this.instance||(this.instance=new e),this.instance}trackDependency(t){this.currentTracker&&g(this.currentTracker)&&this.currentTracker(t)}startTracking(t){let r=this.currentTracker;return this.currentTracker=t,()=>{this.currentTracker=r}}isComputing(t){return this.computingSignals.has(t)}markAsComputing(t){this.computingSignals.add(t)}markAsComplete(t){this.computingSignals.delete(t)}},Ge=Te.getInstance(),Ce=e=>{Ge.trackDependency(e)};V(globalThis)||(globalThis.__HOOKTML_TRACK_SIGNAL__=Ce);var He=e=>{if(!g(e))throw new Error("[HookTML] computed() requires a function");let t={value:void 0,hasValue:!1,isStale:!0,isComputing:!1,dependencies:new Set,subscribers:new Set,notificationScheduled:!1},r=new Set,o=()=>{r.forEach(i=>i()),r.clear(),t.dependencies.clear()},n=()=>{t.notificationScheduled||t.subscribers.size===0||(t.notificationScheduled=!0,queueMicrotask(()=>{if(t.notificationScheduled=!1,t.subscribers.size>0){let i=c.value;t.subscribers.forEach(u=>{w({fn:()=>u(i),onError:l=>{console.error("[HookTML] Error in computed subscriber:",l)}})})}}))},c={get value(){if(!t.isStale&&t.hasValue)return Ce(c),t.value;if(t.isComputing)throw new Error("[HookTML] Circular dependency detected in computed signal");o(),t.isComputing=!0;let i=new Set,u=Ge.startTracking(s=>{if(T(s)&&!i.has(s)){i.add(s);let p=s.subscribe(()=>{let f=t.isStale;t.isStale=!0,!f&&t.hasValue&&n()});r.add(p)}}),l=e();return u(),t.isComputing=!1,t.dependencies=i,t.value=l,t.hasValue=!0,t.isStale=!1,Ce(c),l},set value(i){throw new Error("[HookTML] Cannot assign to computed signal. Computed signals are read-only.")},subscribe(i){if(!g(i))throw new Error("[HookTML] Computed subscribers must be functions");return t.subscribers.add(i),()=>{t.subscribers.delete(i)}},destroy(){o(),t.subscribers.clear(),t.hasValue=!1,t.isStale=!0,t.notificationScheduled=!1},toString(){let i=t.dependencies.size,u=t.isStale?" (stale)":"";return`Computed(${t.hasValue?t.value:"uncomputed"}, ${i} deps${u})`}};return c};var xe={current:null},Qe=e=>{Ae(e),a.log("Initializing...");let{debug:t,attributePrefix:r}=y();return r&&a.log(`Using attribute prefix: "${r}"`),xe.current=Ue(),xe.current.start(),fe(),a.log("Initialization complete"),{config:y(),scan:fe,stop:()=>xe.current?.stop(),components:z,hooks:N,chainableHooks:Z}},fe=()=>{a.log("Manual scan triggered");let e=ae(),t=ce(e);return Ke(),a.log(`Manual scan complete, initialized ${t.length} new component(s)`),t};Object.assign(window,{HookTML:{start:Qe,scan:fe,registerComponent:ye,registerHook:me,registerChainableHook:ge,useEffect:x,useChildren:de,useEvents:O,useClasses:P,useAttributes:F,useStyles:W,useText:K,with:he,signal:be,computed:He,getConfig:y}});})();