uraniyum 1.0.9 → 1.0.910

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.min.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).MiniUI={})}(this,function(e){"use strict";let t=null;function n(e){const n=()=>{!function(e){for(const t of e.deps)t.subs.delete(e);e.deps.clear()}(n),t=n;try{e()}finally{t=null}};return n.deps=new Set,n(),n}function o(e){return e&&"object"==typeof e&&"subs"in e&&Object.prototype.hasOwnProperty.call(e,"val")}const r=new Proxy({},{get:(e,t)=>(e,...r)=>{const s=document.createElement(t);if(!e||"object"!=typeof e||e instanceof Node||o(e)||"function"==typeof e)null!=e&&r.unshift(e);else for(const[t,n]of Object.entries(e))s.setAttribute(t,String(n));for(let e of r.flat()){if("function"==typeof e){let t=document.createTextNode("");s.appendChild(t),n(()=>{const n=e(),o=n instanceof Node?n:document.createTextNode(String(n??""));t.parentNode?.replaceChild(o,t),t=o});continue}if(o(e)){const t=document.createTextNode(String(e.val));s.appendChild(t),e.subs.add(()=>{t.textContent=String(e.val)});continue}e instanceof Node?s.appendChild(e):s.appendChild(document.createTextNode(String(e)))}return s}});const s=new Map,c=new Map;let i=null;const f=["root","button","icon","text","container","wrapper","card","header","section"];function d(){return i||(i=document.createElement("style"),i.id="styles",document.head.appendChild(i)),i}function a(e){let t=5381;for(let n=0;n<e.length;n++)t=33*t^e.charCodeAt(n);return(t>>>0).toString(36)}const u=new Map;function l(e){return u.has(e)||u.set(e,e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()),u.get(e)}function p(e,t){const n=[];for(const[o,r]of Object.entries(e)){if(null==r||"object"==typeof r)continue;let e="function"==typeof r?r():r;if("string"==typeof e)for(const[n,o]of t)e=e.replaceAll(`$${n}`,o);n.push(`${l(o)}:${e}`)}return n.join(";")}const h=/^@keyframes /;function m(e,t){const n=`${e}-${a(e+JSON.stringify(t))}`;if(c.has(n))return n;const o=Object.entries(t).map(([e,t])=>`${e}{${p(t,new Map)}}`).join("");return d().appendChild(document.createTextNode(`@keyframes ${n}{${o}}\n`)),c.set(n,n),n}function y(e,t,n){const o=[],r={};for(const[s,c]of Object.entries(t))if(!h.test(s))if(s.startsWith(":")&&"object"==typeof c)o.push(`.${e}${s}{${p(c,n)}}`);else if(s.startsWith("@media")&&"object"==typeof c)o.push(`${s}{.${e}{${p(c,n)}}}`);else if(s.includes("&")&&"object"==typeof c){const t=s.replaceAll("&",`.${e}`);o.push(`${t}{${p(c,n)}}`)}else s.startsWith("@")||(r[s]=c);return Object.keys(r).length>0&&o.push(`.${e}{${p(r,n)}}`),o}function b(e){return()=>{const t={},n=[],o=new Map;for(const[t,n]of Object.entries(e))if(h.test(t)){const e=t.replace("@keyframes ",""),r=m(e,n);o.set(e,r)}for(const[r,c]of Object.entries(e)){if(h.test(r))continue;const e=a(JSON.stringify(c)),i=f.includes(r)?`${r}-${e}`:`css-${e}`;if(!s.has(i)){const e=y(i,c,o);n.push(...e),s.set(i,i)}t[r]=i}if(n.length){const e=d();for(const t of n)e.appendChild(document.createTextNode(t+"\n"))}return t}}e.add=function(e){if("function"==typeof e){const t=e();if(!(t instanceof HTMLElement))throw new Error("Component function must return an HTMLElement");return void document.body.appendChild(t)}if("render"in e&&"function"==typeof e.render){if(!(e.element instanceof HTMLElement))throw new Error("Component object must have an HTMLElement in .element");return document.body.appendChild(e.element),void e.render()}throw new Error("Invalid component passed to add()")},e.derive=n,e.makeStyles=b,e.mergeClasses=(...e)=>e.filter(Boolean).join(" "),e.mergeStyleSets=function(...e){const t={};for(const n of e)if(n)for(const e in n)t[e]={...t[e]||{},...n[e]};return b(t)()},e.router=function(e){const t=[];for(const n in e){const o=[],r=n.replace(/\/:[^\/]+/g,e=>(o.push(e.slice(2)),"/([^/]+)")).replace(/\*/g,".*"),s=new RegExp(`^${r}$`);t.push({regex:s,keys:o,handler:e[n]})}const n=document.createElement("div"),o=()=>{const n=window.location.pathname;for(const{regex:e,keys:o,handler:r}of t){const t=n.match(e);if(t){const e={};return o.forEach((n,o)=>{e[n]=t[o+1]}),r(e)}}if(e["*"])return e["*"]();throw new Error("No matching route and no wildcard route provided")},r=()=>{const e=o();n.innerHTML="",n.appendChild(e)};return n.appendChild(o()),window.addEventListener("popstate",r),{element:n,render:r}},e.state=function(e){const n={subs:new Set,get val(){return function(e){t&&(e.subs.add(t),t.deps.add(e))}(n),e},set val(t){if(t!==e){e=t;for(const e of Array.from(n.subs))e()}}};return n},e.tags=r,Object.defineProperty(e,"__esModule",{value:!0})});
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).MiniUI={})}(this,function(t){"use strict";const e=Symbol("STATE");let n=null;function o(t){const e=()=>{!function(t){for(const e of t.deps)e.subs.delete(t);t.deps.clear()}(e),n=e;try{t()}finally{n=null}};return e.deps=new Set,e(),e}function c(t){return t&&"object"==typeof t&&"subs"in t&&"val"in t}const r=new Proxy({},{get:(t,e)=>(...t)=>{const n=document.createElement(e);return function(t,e){for(const n of e.flat())null!=n&&!1!==n&&("function"!=typeof n?c(n)?i(t,n):n instanceof Node?t.appendChild(n):t.appendChild(document.createTextNode(String(n))):s(t,n))}(n,t),n}});function s(t,e){let n=document.createTextNode("");t.appendChild(n),o(()=>{const t=e();if(t instanceof Node)return void(n!==t&&(n.parentNode?.replaceChild(t,n),n=t));const o=String(t??"");if(n.nodeType===Node.TEXT_NODE)n.data!==o&&(n.data=o);else{const t=document.createTextNode(o);n.parentNode?.replaceChild(t,n),n=t}})}function i(t,e){const n=document.createTextNode(String(e.val));t.appendChild(n),e.subs.add(()=>{const t=String(e.val);n.data!==t&&(n.data=t)})}const u=new Map,f=new Map;let a=null,l=null;const p=["root","button","icon","text","container","wrapper","card","header","section"],d="@keyframes ";function h(){return l||(a||(a=document.createElement("style"),a.id="styles",document.head.appendChild(a)),l=a.sheet),l}function y(t){let e=5381;for(let n=0;n<t.length;n++)e=33*e^t.charCodeAt(n);return(e>>>0).toString(36)}const m=new Map;function b(t){const e=m.get(t);if(e)return e;const n=t.replace(/[A-Z]/g,t=>"-"+t.toLowerCase());return m.set(t,n),n}function $(t){const e=[];for(const n in t){if(!Object.prototype.hasOwnProperty.call(t,n))continue;const o=t[n];if(n.startsWith("@")||"object"==typeof o||null==o)continue;let c;c="function"==typeof o?o.toString():String(o),e.push(`${n}:${c}`)}return e.sort(),y(e.join("|"))}function g(t,e){const n=[];for(const o in t){if(!Object.prototype.hasOwnProperty.call(t,o))continue;const c=t[o];if(null==c||"object"==typeof c)continue;let r="function"==typeof c?c():c;if("string"==typeof r&&e.size>0)for(const[t,n]of e)r.includes(`$${t}`)&&(r=r.replaceAll(`$${t}`,n));n.push(`${b(o)}:${r}`)}return n.join(";")}function w(t,e){const n=`${t}-${y(t+JSON.stringify(e))}`,o=f.get(n);if(o)return o;const c=h(),r=[];for(const t in e){if(!Object.prototype.hasOwnProperty.call(e,t))continue;const n=g(e[t]??{},new Map);r.push(`${t}{${n}}`)}const s=`@keyframes ${n}{${r.join("")}}`;return c.insertRule(s,c.cssRules.length),f.set(n,n),n}function j(t,e,n){const o=[],c=[];for(const r in e){if(!Object.prototype.hasOwnProperty.call(e,r))continue;const s=e[r];if(!r.startsWith("@keyframes")&&s){if(":"===r[0]&&"object"==typeof s){const e=g(s??{},n);e&&o.push(`.${t}${r}{${e}}`);continue}if(r.startsWith("@media")&&"object"==typeof s){const e=g(s??{},n);e&&o.push(`${r}{.${t}{${e}}}`);continue}if(r.includes("&")&&"object"==typeof s){const e=r.replace(/&/g,`.${t}`),c=g(s??{},n);c&&o.push(`${e}{${c}}`);continue}if(!r.startsWith("@")&&"object"!=typeof s){const t="function"==typeof s?s():s;c.push(`${b(r)}:${t}`)}}}return c.length>0&&o.push(`.${t}{${c.join(";")}}`),o}function S(t){return()=>{const e={},n=new Map,o=h();for(const e in t){if(!Object.prototype.hasOwnProperty.call(t,e))continue;if(!e.startsWith(d))continue;const o=e.slice(11),c=w(o,t[e]);n.set(o,c)}for(const c in t){if(!Object.prototype.hasOwnProperty.call(t,c))continue;if(c.startsWith(d))continue;const r=t[c],s=$(r),i=p.includes(c)?`${c}-${s}`:`css-${s}`;if(!u.has(i)){const t=j(i,r,n);for(const e of t)o.insertRule(e,o.cssRules.length);u.set(i,!0)}e[c]=i}return e}}t.STATE=e,t.add=function(t){if("function"==typeof t){const e=t();if(!(e instanceof HTMLElement))throw new Error("Component function must return an HTMLElement");return void document.body.appendChild(e)}if("render"in t&&"function"==typeof t.render){if(!(t.element instanceof HTMLElement))throw new Error("Component object must have an HTMLElement in .element");return document.body.appendChild(t.element),void t.render()}throw new Error("Invalid component passed to add()")},t.derive=o,t.isState=function(t){return"object"==typeof t&&null!==t&&!0===t[e]},t.makeStyles=S,t.mergeClasses=(...t)=>t.filter(Boolean).join(" "),t.mergeStyleSets=function(...t){const e={};for(const n of t)if(n)for(const t in n){if(!Object.prototype.hasOwnProperty.call(n,t))continue;const o=e[t]??{},c=n[t]??{};e[t]={...o,...c}}return S(e)()},t.router=function(t){const e=[];let n=null;for(const o of Object.keys(t)){const c=t[o];if(!c)continue;if("*"===o){n=c;continue}const r=[],s=o.replace(/:[^/]+/g,t=>(r.push(t.slice(1)),"([^/]+)"));e.push({regex:new RegExp(`^${s}$`),keys:r,handler:c})}const o=document.createElement("div");let c=null,r="";function s(t){const e={};if(!t)return e;for(const[n,o]of new URLSearchParams(t))e[n]=o;return e}function i(){const t=location.pathname+location.search;if(t===r)return;r=t;const i=function(t){for(const{regex:n,keys:o,handler:c}of e){const e=t.match(n);if(!e)continue;const r={};return o.forEach((t,n)=>{r[t]=decodeURIComponent(e[n+1])}),Object.assign(r,s(location.search)),c(r)}if(n)return n({});throw new Error(`No route matches "${t}"`)}(location.pathname);c?o.replaceChild(i,c):o.appendChild(i),c=i}return window.addEventListener("popstate",i),i(),{element:o,render:i,push:function(t){history.pushState(null,"",t),i()},replace:function(t){history.replaceState(null,"",t),i()},back:()=>history.back()}},t.state=function(t){const o={[e]:!0,subs:new Set,get val(){return function(t){n&&(t.subs.add(n),n.deps.add(t))}(o),t},set val(e){if(e!==t){t=e;for(const t of Array.from(o.subs))t()}}};return o},t.tags=r,Object.defineProperty(t,"__esModule",{value:!0})});
2
2
  //# sourceMappingURL=index.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.min.js","sources":["../src/state.ts","../src/tags.ts","../src/styles.ts","../src/helpers.ts","../src/router.ts"],"sourcesContent":["export type Effect = (() => void) & { deps: Set<State<any>> };\r\n\r\nexport interface State<T> {\r\n subs: Set<Effect | (() => void)>;\r\n val: T;\r\n}\r\n\r\nfunction isState<T = any>(x: unknown): x is State<T> {\r\n return typeof x === \"object\" && x !== null && \"subs\" in x && \"val\" in x;\r\n}\r\n\r\nlet currentEffect: Effect | null = null;\r\n\r\nfunction track<T>(st: State<T>): void {\r\n if (currentEffect) {\r\n st.subs.add(currentEffect);\r\n currentEffect.deps.add(st);\r\n }\r\n}\r\n\r\nfunction cleanup(effect: Effect): void {\r\n for (const d of effect.deps) d.subs.delete(effect);\r\n effect.deps.clear();\r\n}\r\n\r\nexport function state<T>(v: T): State<T> {\r\n const st = {\r\n subs: new Set<Effect | (() => void)>(),\r\n\r\n get val() {\r\n track(st);\r\n return v;\r\n },\r\n\r\n set val(nv: T) {\r\n if (nv === v) return;\r\n v = nv;\r\n for (const fn of Array.from(st.subs)) {\r\n (fn as (() => void))();\r\n }\r\n }\r\n } as State<T>;\r\n\r\n return st;\r\n}\r\n\r\nexport function derive(fn: () => void): Effect {\r\n const effect = (() => {\r\n cleanup(effect);\r\n currentEffect = effect;\r\n\r\n try {\r\n fn();\r\n } finally {\r\n currentEffect = null;\r\n }\r\n }) as Effect;\r\n\r\n effect.deps = new Set();\r\n effect();\r\n return effect;\r\n}\r\n","import { derive, state, State } from \"./state\";\r\n\r\nfunction isState(x: any): x is State<any> {\r\n return (\r\n x &&\r\n typeof x === \"object\" &&\r\n \"subs\" in x &&\r\n Object.prototype.hasOwnProperty.call(x, \"val\")\r\n );\r\n}\r\n\r\nexport const tags: Record<string, (attrs?: any, ...children: any[]) => HTMLElement> =\r\n new Proxy({}, {\r\n get(_, tag: string) {\r\n return (attrsOrChild?: any, ...children: any[]) => {\r\n const el = document.createElement(tag);\r\n\r\n if (\r\n attrsOrChild &&\r\n typeof attrsOrChild === \"object\" &&\r\n !(attrsOrChild instanceof Node) &&\r\n !isState(attrsOrChild) &&\r\n typeof attrsOrChild !== \"function\"\r\n ) {\r\n for (const [k, v] of Object.entries(attrsOrChild)) {\r\n el.setAttribute(k, String(v));\r\n }\r\n } else if (attrsOrChild != null) {\r\n children.unshift(attrsOrChild);\r\n }\r\n\r\n for (let child of children.flat()) {\r\n\r\n if (typeof child === \"function\") {\r\n let node: Node = document.createTextNode(\"\");\r\n el.appendChild(node);\r\n\r\n derive(() => {\r\n const value = child();\r\n\r\n const newNode: Node =\r\n value instanceof Node\r\n ? value\r\n : document.createTextNode(String(value ?? \"\"));\r\n\r\n node.parentNode?.replaceChild(newNode, node);\r\n node = newNode;\r\n });\r\n\r\n continue;\r\n }\r\n\r\n if (isState(child)) {\r\n const node = document.createTextNode(String(child.val));\r\n el.appendChild(node);\r\n\r\n child.subs.add(() => {\r\n node.textContent = String(child.val);\r\n });\r\n\r\n continue;\r\n }\r\n\r\n if (child instanceof Node) {\r\n el.appendChild(child);\r\n continue;\r\n }\r\n\r\n el.appendChild(\r\n document.createTextNode(String(child))\r\n );\r\n }\r\n\r\n return el;\r\n };\r\n }\r\n });\r\n","export type CSSValue = string | number | (() => string | number);\r\nexport interface CSSProperties {\r\n [key: string]: CSSValue | CSSProperties;\r\n}\r\nexport type StyleRule = CSSProperties & {\r\n ':hover'?: CSSProperties;\r\n ':focus'?: CSSProperties;\r\n ':active'?: CSSProperties;\r\n ':disabled'?: CSSProperties;\r\n [key: string]: any;\r\n};\r\nexport type StylesMap<T extends Record<string, any> = {}> = {\r\n [K in keyof T]: StyleRule;\r\n};\r\nexport type ClassNames<T extends Record<string, any>> = { [K in keyof T]: string };\r\n\r\nconst styleCache = new Map<string, string>();\r\nconst keyframeCache = new Map<string, string>();\r\nlet styleElement: HTMLStyleElement | null = null;\r\n\r\nconst ALLOWED_PREFIXES = [\r\n 'root', 'button', 'icon', 'text', 'container', 'wrapper',\r\n 'card', 'header', 'section'\r\n];\r\n\r\nfunction ensureStyleElement() {\r\n if (!styleElement) {\r\n styleElement = document.createElement('style');\r\n styleElement.id = 'styles';\r\n document.head.appendChild(styleElement);\r\n }\r\n return styleElement;\r\n}\r\n\r\nfunction hashString(str: string): string {\r\n let hash = 5381;\r\n for (let i = 0; i < str.length; i++) hash = (hash * 33) ^ str.charCodeAt(i);\r\n return (hash >>> 0).toString(36);\r\n}\r\n\r\nconst kebabCache = new Map<string, string>();\r\nfunction camelToKebab(str: string): string {\r\n if (!kebabCache.has(str)) {\r\n kebabCache.set(str, str.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase());\r\n }\r\n return kebabCache.get(str)!;\r\n}\r\n\r\nfunction stringifyDecls(props: CSSProperties, keyframes: Map<string, string>): string {\r\n const out: string[] = [];\r\n for (const [key, raw] of Object.entries(props)) {\r\n if (raw == null || typeof raw === \"object\") continue;\r\n let val = typeof raw === \"function\" ? raw() : raw;\r\n\r\n if (typeof val === \"string\") {\r\n for (const [orig, uniq] of keyframes) {\r\n val = val.replaceAll(`$${orig}`, uniq);\r\n }\r\n }\r\n out.push(`${camelToKebab(key)}:${val}`);\r\n }\r\n return out.join(\";\");\r\n}\r\n\r\nconst KEYFRAMES_REGEX = /^@keyframes /;\r\n\r\nfunction registerKeyframes(name: string, frames: Record<string, CSSProperties>): string {\r\n const hash = hashString(name + JSON.stringify(frames));\r\n const unique = `${name}-${hash}`;\r\n\r\n if (keyframeCache.has(unique)) return unique;\r\n\r\n const rules = Object.entries(frames)\r\n .map(([step, styles]) => `${step}{${stringifyDecls(styles, new Map())}}`)\r\n .join(\"\");\r\n\r\n ensureStyleElement().appendChild(\r\n document.createTextNode(`@keyframes ${unique}{${rules}}\\n`)\r\n );\r\n keyframeCache.set(unique, unique);\r\n return unique;\r\n}\r\n\r\nfunction buildRules(className: string, rule: StyleRule, keyframes: Map<string, string>): string[] {\r\n const rules: string[] = [];\r\n const base: CSSProperties = {};\r\n\r\n for (const [key, val] of Object.entries(rule)) {\r\n if (KEYFRAMES_REGEX.test(key)) continue;\r\n\r\n if (key.startsWith(':') && typeof val === \"object\") {\r\n rules.push(`.${className}${key}{${stringifyDecls(val, keyframes)}}`);\r\n }\r\n else if (key.startsWith('@media') && typeof val === \"object\") {\r\n rules.push(`${key}{.${className}{${stringifyDecls(val, keyframes)}}}`);\r\n }\r\n else if (key.includes('&') && typeof val === \"object\") {\r\n const selector = key.replaceAll(\"&\", `.${className}`);\r\n rules.push(`${selector}{${stringifyDecls(val, keyframes)}}`);\r\n }\r\n else if (!key.startsWith('@')) {\r\n base[key] = val;\r\n }\r\n }\r\n\r\n if (Object.keys(base).length > 0) {\r\n rules.push(`.${className}{${stringifyDecls(base, keyframes)}}`);\r\n }\r\n\r\n return rules;\r\n}\r\n\r\nexport function makeStyles<T extends StylesMap>(styles: T) {\r\n return () => {\r\n const classNames = {} as ClassNames<T>;\r\n const collectedRules: string[] = [];\r\n const keyframesMap = new Map<string, string>();\r\n\r\n for (const [key, val] of Object.entries(styles)) {\r\n if (KEYFRAMES_REGEX.test(key)) {\r\n const name = key.replace('@keyframes ', '');\r\n const unique = registerKeyframes(name, val as any);\r\n keyframesMap.set(name, unique);\r\n }\r\n }\r\n\r\n for (const [slot, rule] of Object.entries(styles)) {\r\n if (KEYFRAMES_REGEX.test(slot)) continue;\r\n\r\n const hash = hashString(JSON.stringify(rule));\r\n const className =\r\n ALLOWED_PREFIXES.includes(slot) ? `${slot}-${hash}` : `css-${hash}`;\r\n\r\n if (!styleCache.has(className)) {\r\n const rules = buildRules(className, rule as StyleRule, keyframesMap);\r\n collectedRules.push(...rules);\r\n styleCache.set(className, className);\r\n }\r\n\r\n classNames[slot as keyof T] = className;\r\n }\r\n\r\n if (collectedRules.length) {\r\n const el = ensureStyleElement();\r\n for (const r of collectedRules) el.appendChild(document.createTextNode(r + \"\\n\"));\r\n }\r\n\r\n return classNames;\r\n };\r\n}\r\n\r\nexport const mergeClasses = (...classes: (string | undefined | null | false)[]) =>\r\n classes.filter(Boolean).join(\" \");\r\n\r\nexport function mergeStyleSets<T extends StylesMap>(...sets: (T | undefined)[]) {\r\n const merged: any = {};\r\n for (const set of sets) {\r\n if (!set) continue;\r\n for (const k in set) {\r\n merged[k] = { ...(merged[k] || {}), ...set[k] };\r\n }\r\n }\r\n return makeStyles(merged)();\r\n}","type Component =\r\n | (() => HTMLElement)\r\n | { render: () => void; element: HTMLElement };\r\n\r\nexport function add(component: Component) {\r\n if (typeof component === \"function\") {\r\n const el = component();\r\n if (!(el instanceof HTMLElement)) {\r\n throw new Error(\"Component function must return an HTMLElement\");\r\n }\r\n document.body.appendChild(el);\r\n return;\r\n }\r\n\r\n if (\"render\" in component && typeof component.render === \"function\") {\r\n if (!(component.element instanceof HTMLElement)) {\r\n throw new Error(\"Component object must have an HTMLElement in .element\");\r\n }\r\n document.body.appendChild(component.element);\r\n component.render();\r\n return;\r\n }\r\n\r\n throw new Error(\"Invalid component passed to add()\");\r\n}\r\n","export type RouteHandler = (params?: Record<string, string>) => HTMLElement;\r\n\r\nexport function router(routes: Record<string, RouteHandler>) {\r\n type RoutePattern = {\r\n regex: RegExp;\r\n keys: string[];\r\n handler: RouteHandler;\r\n };\r\n\r\n const routePatterns: RoutePattern[] = [];\r\n\r\n for (const path in routes) {\r\n const keys: string[] = [];\r\n\r\n const pattern = path\r\n .replace(/\\/:[^\\/]+/g, match => {\r\n keys.push(match.slice(2));\r\n return \"/([^/]+)\";\r\n })\r\n .replace(/\\*/g, \".*\");\r\n\r\n const regex = new RegExp(`^${pattern}$`);\r\n routePatterns.push({ regex, keys, handler: routes[path] });\r\n }\r\n\r\n const root = document.createElement(\"div\");\r\n\r\n const renderRoute = (): HTMLElement => {\r\n const path = window.location.pathname;\r\n\r\n for (const { regex, keys, handler } of routePatterns) {\r\n const match = path.match(regex);\r\n\r\n if (match) {\r\n const params: Record<string, string> = {};\r\n\r\n keys.forEach((key, i) => {\r\n params[key] = match[i + 1];\r\n });\r\n\r\n return handler(params);\r\n }\r\n }\r\n\r\n if (routes[\"*\"]) return routes[\"*\"]();\r\n\r\n throw new Error(\"No matching route and no wildcard route provided\");\r\n };\r\n\r\n const updateRoot = () => {\r\n const el = renderRoute();\r\n root.innerHTML = \"\";\r\n root.appendChild(el);\r\n };\r\n\r\n root.appendChild(renderRoute());\r\n\r\n window.addEventListener(\"popstate\", updateRoot);\r\n\r\n return {\r\n element: root,\r\n render: updateRoot\r\n };\r\n}"],"names":["currentEffect","derive","fn","effect","d","deps","subs","delete","clear","cleanup","Set","isState","x","Object","prototype","hasOwnProperty","call","tags","Proxy","get","_","tag","attrsOrChild","children","el","document","createElement","Node","unshift","k","v","entries","setAttribute","String","child","flat","node","createTextNode","appendChild","value","newNode","parentNode","replaceChild","val","add","textContent","styleCache","Map","keyframeCache","styleElement","ALLOWED_PREFIXES","ensureStyleElement","id","head","hashString","str","hash","i","length","charCodeAt","toString","kebabCache","camelToKebab","has","set","replace","toLowerCase","stringifyDecls","props","keyframes","out","key","raw","orig","uniq","replaceAll","push","join","KEYFRAMES_REGEX","registerKeyframes","name","frames","unique","JSON","stringify","rules","map","step","styles","buildRules","className","rule","base","test","startsWith","includes","selector","keys","makeStyles","classNames","collectedRules","keyframesMap","slot","r","component","HTMLElement","Error","body","render","element","classes","filter","Boolean","sets","merged","routes","routePatterns","path","pattern","match","slice","regex","RegExp","handler","root","renderRoute","window","location","pathname","params","forEach","updateRoot","innerHTML","addEventListener","st","track","nv","Array","from"],"mappings":"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"}
1
+ {"version":3,"file":"index.min.js","sources":["../src/state.ts","../src/tags.ts","../src/styles.ts","../src/helpers.ts","../src/router.ts"],"sourcesContent":["export const STATE = Symbol(\"STATE\");\nexport function isState(x) {\n return typeof x === \"object\" && x !== null && x[STATE] === true;\n}\nlet currentEffect = null;\nfunction track(st) {\n if (currentEffect) {\n st.subs.add(currentEffect);\n currentEffect.deps.add(st);\n }\n}\nfunction cleanup(effect) {\n for (const st of effect.deps)\n st.subs.delete(effect);\n effect.deps.clear();\n}\nexport function state(v) {\n const st = {\n [STATE]: true,\n subs: new Set(),\n get val() {\n track(st);\n return v;\n },\n set val(nv) {\n if (nv === v)\n return;\n v = nv;\n for (const fn of Array.from(st.subs)) {\n fn();\n }\n }\n };\n return st;\n}\nexport function derive(fn) {\n const effect = (() => {\n cleanup(effect);\n currentEffect = effect;\n try {\n fn();\n }\n finally {\n currentEffect = null;\n }\n });\n effect.deps = new Set();\n effect();\n return effect;\n}\n","import { derive, state } from \"./state\";\nfunction isState(x) {\n return x && typeof x === \"object\" && \"subs\" in x && \"val\" in x;\n}\nexport const tags = new Proxy({}, {\n get(_, tag) {\n return (...children) => {\n const el = document.createElement(tag);\n processChildren(el, children);\n return el;\n };\n }\n});\nfunction processChildren(el, children) {\n for (const child of children.flat()) {\n if (child == null || child === false)\n continue;\n if (typeof child === \"function\") {\n mountReactiveFunction(el, child);\n continue;\n }\n if (isState(child)) {\n mountReactiveState(el, child);\n continue;\n }\n if (child instanceof Node) {\n el.appendChild(child);\n continue;\n }\n el.appendChild(document.createTextNode(String(child)));\n }\n}\nfunction mountReactiveFunction(el, fn) {\n let node = document.createTextNode(\"\");\n el.appendChild(node);\n derive(() => {\n const value = fn();\n if (value instanceof Node) {\n if (node !== value) {\n node.parentNode?.replaceChild(value, node);\n node = value;\n }\n return;\n }\n const text = String(value ?? \"\");\n if (node.nodeType === Node.TEXT_NODE) {\n if (node.data !== text) {\n node.data = text;\n }\n }\n else {\n const newNode = document.createTextNode(text);\n node.parentNode?.replaceChild(newNode, node);\n node = newNode;\n }\n });\n}\nfunction mountReactiveState(el, st) {\n const node = document.createTextNode(String(st.val));\n el.appendChild(node);\n st.subs.add(() => {\n const newVal = String(st.val);\n if (node.data !== newVal) {\n node.data = newVal;\n }\n });\n}\n","const styleCache = new Map();\nconst keyframeCache = new Map();\nlet styleElement = null;\nlet styleSheet = null;\nconst ALLOWED_PREFIXES = [\n \"root\",\n \"button\",\n \"icon\",\n \"text\",\n \"container\",\n \"wrapper\",\n \"card\",\n \"header\",\n \"section\",\n];\nconst KEYFRAMES_PREFIX = \"@keyframes \";\nfunction ensureStyleSheet() {\n if (!styleSheet) {\n if (!styleElement) {\n styleElement = document.createElement(\"style\");\n styleElement.id = \"styles\";\n document.head.appendChild(styleElement);\n }\n styleSheet = styleElement.sheet;\n }\n return styleSheet;\n}\nfunction hashString(str) {\n let hash = 5381;\n for (let i = 0; i < str.length; i++) {\n hash = (hash * 33) ^ str.charCodeAt(i);\n }\n return (hash >>> 0).toString(36);\n}\nconst camelToKebabCache = new Map();\nfunction camelToKebab(str) {\n const cached = camelToKebabCache.get(str);\n if (cached)\n return cached;\n const result = str.replace(/[A-Z]/g, (m) => \"-\" + m.toLowerCase());\n camelToKebabCache.set(str, result);\n return result;\n}\nfunction stableHashRule(rule) {\n const parts = [];\n for (const key in rule) {\n if (!Object.prototype.hasOwnProperty.call(rule, key))\n continue;\n const val = rule[key];\n if (key.startsWith(\"@\") || typeof val === \"object\" || val == null)\n continue;\n let v;\n if (typeof val === \"function\")\n v = val.toString();\n else\n v = String(val);\n parts.push(`${key}:${v}`);\n }\n parts.sort();\n return hashString(parts.join(\"|\"));\n}\nfunction stringifyDecls(props, keyframes) {\n const out = [];\n for (const key in props) {\n if (!Object.prototype.hasOwnProperty.call(props, key))\n continue;\n const raw = props[key];\n if (raw == null || typeof raw === \"object\")\n continue;\n let val = typeof raw === \"function\" ? raw() : raw;\n if (typeof val === \"string\" && keyframes.size > 0) {\n for (const [orig, uniq] of keyframes) {\n if (val.includes(`$${orig}`)) {\n val = val.replaceAll(`$${orig}`, uniq);\n }\n }\n }\n out.push(`${camelToKebab(key)}:${val}`);\n }\n return out.join(\";\");\n}\nfunction registerKeyframes(name, frames) {\n const hash = hashString(name + JSON.stringify(frames));\n const unique = `${name}-${hash}`;\n const cached = keyframeCache.get(unique);\n if (cached)\n return cached;\n const sheet = ensureStyleSheet();\n const steps = [];\n for (const step in frames) {\n if (!Object.prototype.hasOwnProperty.call(frames, step))\n continue;\n const decls = stringifyDecls(frames[step] ?? {}, new Map());\n steps.push(`${step}{${decls}}`);\n }\n const ruleText = `@keyframes ${unique}{${steps.join(\"\")}}`;\n sheet.insertRule(ruleText, sheet.cssRules.length);\n keyframeCache.set(unique, unique);\n return unique;\n}\nfunction buildRules(className, rule, keyframes) {\n const rules = [];\n const baseDecls = [];\n for (const key in rule) {\n if (!Object.prototype.hasOwnProperty.call(rule, key))\n continue;\n const val = rule[key];\n if (key.startsWith(\"@keyframes\")) {\n continue;\n }\n if (!val)\n continue;\n if (key[0] === \":\" && typeof val === \"object\") {\n const decls = stringifyDecls(val ?? {}, keyframes);\n if (decls) {\n rules.push(`.${className}${key}{${decls}}`);\n }\n continue;\n }\n if (key.startsWith(\"@media\") && typeof val === \"object\") {\n const decls = stringifyDecls(val ?? {}, keyframes);\n if (decls) {\n rules.push(`${key}{.${className}{${decls}}}`);\n }\n continue;\n }\n if (key.includes(\"&\") && typeof val === \"object\") {\n const selector = key.replace(/&/g, `.${className}`);\n const decls = stringifyDecls(val ?? {}, keyframes);\n if (decls) {\n rules.push(`${selector}{${decls}}`);\n }\n continue;\n }\n if (!key.startsWith(\"@\") && typeof val !== \"object\") {\n const raw = typeof val === \"function\" ? val() : val;\n baseDecls.push(`${camelToKebab(key)}:${raw}`);\n }\n }\n if (baseDecls.length > 0) {\n rules.push(`.${className}{${baseDecls.join(\";\")}}`);\n }\n return rules;\n}\nexport function makeStyles(styles) {\n return () => {\n const classNames = {};\n const keyframesMap = new Map();\n const sheet = ensureStyleSheet();\n for (const key in styles) {\n if (!Object.prototype.hasOwnProperty.call(styles, key))\n continue;\n if (!key.startsWith(KEYFRAMES_PREFIX))\n continue;\n const name = key.slice(KEYFRAMES_PREFIX.length);\n const frames = styles[key];\n const unique = registerKeyframes(name, frames);\n keyframesMap.set(name, unique);\n }\n for (const slot in styles) {\n if (!Object.prototype.hasOwnProperty.call(styles, slot))\n continue;\n if (slot.startsWith(KEYFRAMES_PREFIX))\n continue;\n const rule = styles[slot];\n const hash = stableHashRule(rule);\n const className = ALLOWED_PREFIXES.includes(slot)\n ? `${slot}-${hash}`\n : `css-${hash}`;\n if (!styleCache.has(className)) {\n const rules = buildRules(className, rule, keyframesMap);\n for (const r of rules) {\n sheet.insertRule(r, sheet.cssRules.length);\n }\n styleCache.set(className, true);\n }\n classNames[slot] = className;\n }\n return classNames;\n };\n}\nexport const mergeClasses = (...classes) => classes.filter(Boolean).join(\" \");\nexport function mergeStyleSets(...sets) {\n const merged = {};\n for (const set of sets) {\n if (!set)\n continue;\n for (const key in set) {\n if (!Object.prototype.hasOwnProperty.call(set, key))\n continue;\n const prev = merged[key] ?? {};\n const cur = set[key] ?? {};\n merged[key] = { ...prev, ...cur };\n }\n }\n return makeStyles(merged)();\n}\n","export function add(component) {\n if (typeof component === \"function\") {\n const el = component();\n if (!(el instanceof HTMLElement)) {\n throw new Error(\"Component function must return an HTMLElement\");\n }\n document.body.appendChild(el);\n return;\n }\n if (\"render\" in component && typeof component.render === \"function\") {\n if (!(component.element instanceof HTMLElement)) {\n throw new Error(\"Component object must have an HTMLElement in .element\");\n }\n document.body.appendChild(component.element);\n component.render();\n return;\n }\n throw new Error(\"Invalid component passed to add()\");\n}\n","export function router(routes) {\n const patterns = [];\n let wildcard = null;\n for (const path of Object.keys(routes)) {\n const handler = routes[path];\n if (!handler)\n continue;\n if (path === \"*\") {\n wildcard = handler;\n continue;\n }\n const keys = [];\n const pattern = path.replace(/:[^/]+/g, (m) => {\n keys.push(m.slice(1));\n return \"([^/]+)\";\n });\n patterns.push({\n regex: new RegExp(`^${pattern}$`),\n keys,\n handler\n });\n }\n const root = document.createElement(\"div\");\n let currentEl = null;\n let currentPath = \"\";\n function parseQuery(search) {\n const params = {};\n if (!search)\n return params;\n for (const [k, v] of new URLSearchParams(search))\n params[k] = v;\n return params;\n }\n function matchRoute(pathname) {\n for (const { regex, keys, handler } of patterns) {\n const match = pathname.match(regex);\n if (!match)\n continue;\n const params = {};\n keys.forEach((key, i) => {\n params[key] = decodeURIComponent(match[i + 1]);\n });\n Object.assign(params, parseQuery(location.search));\n return handler(params);\n }\n if (wildcard)\n return wildcard({});\n throw new Error(`No route matches \"${pathname}\"`);\n }\n function render() {\n const path = location.pathname + location.search;\n if (path === currentPath)\n return;\n currentPath = path;\n const next = matchRoute(location.pathname);\n if (currentEl) {\n root.replaceChild(next, currentEl);\n }\n else {\n root.appendChild(next);\n }\n currentEl = next;\n }\n window.addEventListener(\"popstate\", render);\n render();\n function push(path) {\n history.pushState(null, \"\", path);\n render();\n }\n function replace(path) {\n history.replaceState(null, \"\", path);\n render();\n }\n return {\n element: root,\n render,\n push,\n replace,\n back: () => history.back()\n };\n}\n"],"names":["STATE","Symbol","currentEffect","derive","fn","effect","st","deps","subs","delete","clear","cleanup","Set","isState","x","tags","Proxy","get","_","tag","children","el","document","createElement","child","flat","mountReactiveState","Node","appendChild","createTextNode","String","mountReactiveFunction","processChildren","node","value","parentNode","replaceChild","text","nodeType","TEXT_NODE","data","newNode","val","add","newVal","styleCache","Map","keyframeCache","styleElement","styleSheet","ALLOWED_PREFIXES","KEYFRAMES_PREFIX","ensureStyleSheet","id","head","sheet","hashString","str","hash","i","length","charCodeAt","toString","camelToKebabCache","camelToKebab","cached","result","replace","m","toLowerCase","set","stableHashRule","rule","parts","key","Object","prototype","hasOwnProperty","call","startsWith","v","push","sort","join","stringifyDecls","props","keyframes","out","raw","size","orig","uniq","includes","replaceAll","registerKeyframes","name","frames","unique","JSON","stringify","steps","step","decls","ruleText","insertRule","cssRules","buildRules","className","rules","baseDecls","selector","makeStyles","styles","classNames","keyframesMap","slice","slot","has","r","component","HTMLElement","Error","body","render","element","classes","filter","Boolean","sets","merged","prev","cur","routes","patterns","wildcard","path","keys","handler","pattern","regex","RegExp","root","currentEl","currentPath","parseQuery","search","params","k","URLSearchParams","location","pathname","next","match","forEach","decodeURIComponent","assign","matchRoute","window","addEventListener","history","pushState","replaceState","back","track","nv","Array","from"],"mappings":"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"}
package/dist/router.d.ts CHANGED
@@ -1,5 +1,8 @@
1
- export type RouteHandler = (params?: Record<string, string>) => HTMLElement;
1
+ export type RouteHandler = (params: Record<string, string>) => HTMLElement;
2
2
  export declare function router(routes: Record<string, RouteHandler>): {
3
3
  element: HTMLDivElement;
4
4
  render: () => void;
5
+ push: (path: string) => void;
6
+ replace: (path: string) => void;
7
+ back: () => void;
5
8
  };
package/dist/state.d.ts CHANGED
@@ -1,9 +1,12 @@
1
+ export declare const STATE: unique symbol;
1
2
  export type Effect = (() => void) & {
2
3
  deps: Set<State<any>>;
3
4
  };
4
5
  export interface State<T> {
6
+ [STATE]: true;
5
7
  subs: Set<Effect | (() => void)>;
6
8
  val: T;
7
9
  }
10
+ export declare function isState(x: unknown): x is State<any>;
8
11
  export declare function state<T>(v: T): State<T>;
9
12
  export declare function derive(fn: () => void): Effect;
package/dist/styles.d.ts CHANGED
@@ -1,20 +1,14 @@
1
1
  export type CSSValue = string | number | (() => string | number);
2
2
  export interface CSSProperties {
3
- [key: string]: CSSValue | CSSProperties;
3
+ [key: string]: CSSValue | CSSProperties | undefined;
4
4
  }
5
- export type StyleRule = CSSProperties & {
6
- ':hover'?: CSSProperties;
7
- ':focus'?: CSSProperties;
8
- ':active'?: CSSProperties;
9
- ':disabled'?: CSSProperties;
10
- [key: string]: any;
11
- };
12
- export type StylesMap<T extends Record<string, any> = {}> = {
13
- [K in keyof T]: StyleRule;
14
- };
15
- export type ClassNames<T extends Record<string, any>> = {
5
+ export interface StyleRule {
6
+ [key: string]: CSSValue | CSSProperties | undefined;
7
+ }
8
+ export type StylesMap = Record<string, StyleRule>;
9
+ export type ClassNames<T extends StylesMap> = {
16
10
  [K in keyof T]: string;
17
11
  };
18
12
  export declare function makeStyles<T extends StylesMap>(styles: T): () => ClassNames<T>;
19
13
  export declare const mergeClasses: (...classes: (string | undefined | null | false)[]) => string;
20
- export declare function mergeStyleSets<T extends StylesMap>(...sets: (T | undefined)[]): ClassNames<any>;
14
+ export declare function mergeStyleSets<T extends StylesMap>(...sets: (Partial<T> | undefined)[]): ClassNames<T>;
package/dist/tags.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const tags: Record<string, (attrs?: any, ...children: any[]) => HTMLElement>;
1
+ export declare const tags: Record<string, (...children: any[]) => HTMLElement>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uraniyum",
3
- "version": "1.0.9",
3
+ "version": "1.0.910",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.esm.js",
6
6
  "browser": "dist/index.min.js",
package/src/router.ts CHANGED
@@ -1,64 +1,105 @@
1
- export type RouteHandler = (params?: Record<string, string>) => HTMLElement;
1
+ export type RouteHandler = (params: Record<string, string>) => HTMLElement;
2
+
3
+ interface RoutePattern {
4
+ regex: RegExp;
5
+ keys: string[];
6
+ handler: RouteHandler;
7
+ }
2
8
 
3
9
  export function router(routes: Record<string, RouteHandler>) {
4
- type RoutePattern = {
5
- regex: RegExp;
6
- keys: string[];
7
- handler: RouteHandler;
8
- };
10
+ const patterns: RoutePattern[] = [];
11
+ let wildcard: RouteHandler | null = null;
9
12
 
10
- const routePatterns: RoutePattern[] = [];
13
+ for (const path of Object.keys(routes)) {
14
+ const handler = routes[path];
11
15
 
12
- for (const path in routes) {
13
- const keys: string[] = [];
16
+ if (!handler) continue;
14
17
 
15
- const pattern = path
16
- .replace(/\/:[^\/]+/g, match => {
17
- keys.push(match.slice(2));
18
- return "/([^/]+)";
19
- })
20
- .replace(/\*/g, ".*");
18
+ if (path === "*") {
19
+ wildcard = handler;
20
+ continue;
21
+ }
21
22
 
22
- const regex = new RegExp(`^${pattern}$`);
23
- routePatterns.push({ regex, keys, handler: routes[path] });
23
+ const keys: string[] = [];
24
+ const pattern = path.replace(/:[^/]+/g, (m) => {
25
+ keys.push(m.slice(1));
26
+ return "([^/]+)";
27
+ });
28
+
29
+ patterns.push({
30
+ regex: new RegExp(`^${pattern}$`),
31
+ keys,
32
+ handler
33
+ });
24
34
  }
25
35
 
26
36
  const root = document.createElement("div");
37
+ let currentEl: HTMLElement | null = null;
38
+ let currentPath = "";
39
+
40
+ function parseQuery(search: string): Record<string, string> {
41
+ const params: Record<string, string> = {};
42
+ if (!search) return params;
43
+ for (const [k, v] of new URLSearchParams(search)) params[k] = v;
44
+ return params;
45
+ }
27
46
 
28
- const renderRoute = (): HTMLElement => {
29
- const path = window.location.pathname;
47
+ function matchRoute(pathname: string): HTMLElement {
48
+ for (const { regex, keys, handler } of patterns) {
49
+ const match = pathname.match(regex);
50
+ if (!match) continue;
30
51
 
31
- for (const { regex, keys, handler } of routePatterns) {
32
- const match = path.match(regex);
52
+ const params: Record<string, string> = {};
33
53
 
34
- if (match) {
35
- const params: Record<string, string> = {};
54
+ keys.forEach((key, i) => {
55
+ params[key] = decodeURIComponent(match[i + 1]!);
56
+ });
36
57
 
37
- keys.forEach((key, i) => {
38
- params[key] = match[i + 1];
39
- });
58
+ Object.assign(params, parseQuery(location.search));
40
59
 
41
- return handler(params);
42
- }
60
+ return handler(params);
43
61
  }
44
62
 
45
- if (routes["*"]) return routes["*"]();
63
+ if (wildcard) return wildcard({});
46
64
 
47
- throw new Error("No matching route and no wildcard route provided");
48
- };
65
+ throw new Error(`No route matches "${pathname}"`);
66
+ }
49
67
 
50
- const updateRoot = () => {
51
- const el = renderRoute();
52
- root.innerHTML = "";
53
- root.appendChild(el);
54
- };
68
+ function render() {
69
+ const path = location.pathname + location.search;
55
70
 
56
- root.appendChild(renderRoute());
71
+ if (path === currentPath) return;
72
+ currentPath = path;
57
73
 
58
- window.addEventListener("popstate", updateRoot);
74
+ const next = matchRoute(location.pathname);
75
+
76
+ if (currentEl) {
77
+ root.replaceChild(next, currentEl);
78
+ } else {
79
+ root.appendChild(next);
80
+ }
81
+
82
+ currentEl = next;
83
+ }
84
+
85
+ window.addEventListener("popstate", render);
86
+ render();
87
+
88
+ function push(path: string) {
89
+ history.pushState(null, "", path);
90
+ render();
91
+ }
92
+
93
+ function replace(path: string) {
94
+ history.replaceState(null, "", path);
95
+ render();
96
+ }
59
97
 
60
98
  return {
61
99
  element: root,
62
- render: updateRoot
100
+ render,
101
+ push,
102
+ replace,
103
+ back: () => history.back()
63
104
  };
64
105
  }
package/src/state.ts CHANGED
@@ -1,30 +1,34 @@
1
+ export const STATE = Symbol("STATE");
2
+
1
3
  export type Effect = (() => void) & { deps: Set<State<any>> };
2
4
 
3
5
  export interface State<T> {
6
+ [STATE]: true;
4
7
  subs: Set<Effect | (() => void)>;
5
8
  val: T;
6
9
  }
7
10
 
8
- function isState<T = any>(x: unknown): x is State<T> {
9
- return typeof x === "object" && x !== null && "subs" in x && "val" in x;
11
+ export function isState(x: unknown): x is State<any> {
12
+ return typeof x === "object" && x !== null && (x as any)[STATE] === true;
10
13
  }
11
14
 
12
15
  let currentEffect: Effect | null = null;
13
16
 
14
- function track<T>(st: State<T>): void {
17
+ function track(st: State<any>) {
15
18
  if (currentEffect) {
16
19
  st.subs.add(currentEffect);
17
20
  currentEffect.deps.add(st);
18
21
  }
19
22
  }
20
23
 
21
- function cleanup(effect: Effect): void {
22
- for (const d of effect.deps) d.subs.delete(effect);
24
+ function cleanup(effect: Effect) {
25
+ for (const st of effect.deps) st.subs.delete(effect);
23
26
  effect.deps.clear();
24
27
  }
25
28
 
26
29
  export function state<T>(v: T): State<T> {
27
30
  const st = {
31
+ [STATE]: true,
28
32
  subs: new Set<Effect | (() => void)>(),
29
33
 
30
34
  get val() {
@@ -35,6 +39,7 @@ export function state<T>(v: T): State<T> {
35
39
  set val(nv: T) {
36
40
  if (nv === v) return;
37
41
  v = nv;
42
+
38
43
  for (const fn of Array.from(st.subs)) {
39
44
  (fn as (() => void))();
40
45
  }