lego-dom 2.1.1-alpha → 2.1.4
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/README.md +24 -23
- package/dist/lego.d.ts +95 -0
- package/dist/lego.min.js +7 -0
- package/dist/lego.mjs +7 -0
- package/dist/main.js +7 -0
- package/package.json +17 -8
- package/src/core/batcher.js +84 -0
- package/src/core/globals.js +2 -0
- package/src/core/lifecycle.js +192 -0
- package/src/core/parser.js +45 -0
- package/src/core/reactive.js +73 -0
- package/src/core/registry.js +17 -0
- package/src/core/renderer.js +271 -0
- package/src/core/stylesheets.js +88 -0
- package/src/directives/b-enter.js +36 -0
- package/src/directives/b-for.js +112 -0
- package/src/directives/b-html.js +12 -0
- package/src/directives/b-if.js +24 -0
- package/src/directives/b-init.js +21 -0
- package/src/directives/b-leave.js +34 -0
- package/src/directives/b-show.js +17 -0
- package/src/directives/b-sync.js +16 -0
- package/src/directives/b-text.js +15 -0
- package/src/directives/index.js +22 -0
- package/src/features/error.js +92 -0
- package/src/features/persistence.js +207 -0
- package/src/features/router.js +67 -0
- package/src/index.js +378 -0
- package/src/utils/dom.js +48 -0
- package/src/utils/helpers.js +108 -0
- package/src/utils/lru-cache.js +38 -0
- package/{parse-lego.js → src/utils/parser-utils.js} +9 -23
- package/src/utils/safe-eval.js +83 -0
- package/vite-plugin.js +24 -15
- package/.todo +0 -73
- package/CHANGELOG.md +0 -352
- package/index.js +0 -26
- package/lego.js +0 -2
- package/main.js +0 -1572
- package/main.min.js +0 -7
- /package/{monitoring-plugin.js → src/plugins/monitoring.js} +0 -0
package/README.md
CHANGED
|
@@ -8,10 +8,31 @@ Lego embraces the web platform. It turns standard HTML `<template>` tags into re
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
+
## Supports Modern Toolchains
|
|
12
|
+
|
|
13
|
+
Lego includes a **Vite plugin** for developers who prefer **Single File Components (.lego)**:
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<!-- user-card.lego -->
|
|
17
|
+
<template>
|
|
18
|
+
<h1>{{ name }}</h1>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<style>
|
|
22
|
+
self { display: block; padding: 20px; }
|
|
23
|
+
</style>
|
|
24
|
+
|
|
25
|
+
<script>
|
|
26
|
+
export default { name: 'John Doe' }
|
|
27
|
+
</script>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
11
32
|
## Why Lego?
|
|
12
33
|
|
|
13
34
|
- ⚡ **Extremely Fast** – No virtual DOM. No reconciliation. Direct DOM updates.
|
|
14
|
-
- 📦 **Zero Dependencies** – Weighs less than
|
|
35
|
+
- 📦 **Zero Runtime Dependencies** – Weighs less than 10kb gzipped.
|
|
15
36
|
- 🛠️ **No Build Step** – Works directly in the browser with standard `<script>` tags.
|
|
16
37
|
- 🧩 **Native Web Components** – Real Custom Elements, real Shadow DOM.
|
|
17
38
|
- 🌐 **Built-in Routing** – Lego Router included for client-side routing.
|
|
@@ -46,11 +67,11 @@ This scaffolds a complete project with Vite, the LegoDOM plugin, and a sample co
|
|
|
46
67
|
<style>
|
|
47
68
|
h1 { color: #ffca28; font-family: sans-serif; }
|
|
48
69
|
</style>
|
|
49
|
-
<h1>Hello,
|
|
70
|
+
<h1>Hello, [[ name ]]</h1>
|
|
50
71
|
<button @click="toggle()">Toggle Name</button>
|
|
51
72
|
</template>
|
|
52
73
|
|
|
53
|
-
<script src="https://unpkg.com/lego-dom/
|
|
74
|
+
<script src="https://unpkg.com/lego-dom/dist/lego.min.js"></script>
|
|
54
75
|
<script>
|
|
55
76
|
document.querySelector('hello-world').state = {
|
|
56
77
|
name: 'World',
|
|
@@ -65,26 +86,6 @@ This scaffolds a complete project with Vite, the LegoDOM plugin, and a sample co
|
|
|
65
86
|
|
|
66
87
|
---
|
|
67
88
|
|
|
68
|
-
## Also Supports Modern Toolchains
|
|
69
|
-
|
|
70
|
-
Lego includes a **Vite plugin** for developers who prefer **Single File Components (.lego)**:
|
|
71
|
-
|
|
72
|
-
```html
|
|
73
|
-
<!-- user-card.lego -->
|
|
74
|
-
<template>
|
|
75
|
-
<h1>{{ name }}</h1>
|
|
76
|
-
</template>
|
|
77
|
-
|
|
78
|
-
<style>
|
|
79
|
-
self { display: block; padding: 20px; }
|
|
80
|
-
</style>
|
|
81
|
-
|
|
82
|
-
<script>
|
|
83
|
-
export default { name: 'John Doe' }
|
|
84
|
-
</script>
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
---
|
|
88
89
|
|
|
89
90
|
## 🔗 Links
|
|
90
91
|
|
package/dist/lego.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export interface LegoConfig {
|
|
2
|
+
debug: boolean;
|
|
3
|
+
loader?: (tagName: string) => Promise<string> | string | void;
|
|
4
|
+
metrics: Record<string, any>;
|
|
5
|
+
syntax: 'brackets' | 'mustache';
|
|
6
|
+
onError: (err: Error, type: string, el?: HTMLElement) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface LegoGlobals {
|
|
10
|
+
$route: {
|
|
11
|
+
url: string;
|
|
12
|
+
route: string;
|
|
13
|
+
params: Record<string, string>;
|
|
14
|
+
query: Record<string, string>;
|
|
15
|
+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
16
|
+
body: any;
|
|
17
|
+
};
|
|
18
|
+
$go: (path: string, ...targets: string[]) => {
|
|
19
|
+
get: (push?: boolean) => Promise<void>;
|
|
20
|
+
post: (data: any, push?: boolean) => Promise<void>;
|
|
21
|
+
put: (data: any, push?: boolean) => Promise<void>;
|
|
22
|
+
patch: (data: any, push?: boolean) => Promise<void>;
|
|
23
|
+
delete: (push?: boolean) => Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
$db: (key: string) => {
|
|
26
|
+
default: (val: any) => any;
|
|
27
|
+
debounce: (ms: number) => any;
|
|
28
|
+
};
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface LegoInitOptions {
|
|
33
|
+
debug?: boolean;
|
|
34
|
+
styles?: Record<string, string>;
|
|
35
|
+
loader?: (tagName: string) => Promise<string> | string | void;
|
|
36
|
+
studio?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface Lego {
|
|
40
|
+
config: LegoConfig;
|
|
41
|
+
globals: LegoGlobals;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Initialize the LegoDOM engine
|
|
45
|
+
*/
|
|
46
|
+
init(root?: HTMLElement, options?: LegoInitOptions): Promise<void>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Define a block
|
|
50
|
+
*/
|
|
51
|
+
block(
|
|
52
|
+
tagName: string,
|
|
53
|
+
templateHTML: string,
|
|
54
|
+
logic?: object,
|
|
55
|
+
styles?: string,
|
|
56
|
+
cascade?: string,
|
|
57
|
+
error?: string
|
|
58
|
+
): void;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Define a block from a .lego file content string
|
|
62
|
+
*/
|
|
63
|
+
defineLegoFile(content: string, filename: string): void;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Register a route
|
|
67
|
+
*/
|
|
68
|
+
route(path: string, tagName: string, middleware?: (ctx: { path: string, params: any }) => Promise<boolean> | boolean): void;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Manually snap an element (attach logic)
|
|
72
|
+
*/
|
|
73
|
+
snap(el: HTMLElement): void;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Manually unsnap an element (detach logic)
|
|
77
|
+
*/
|
|
78
|
+
unsnap(el: HTMLElement): void;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get list of registered block names
|
|
82
|
+
*/
|
|
83
|
+
getLegos(): string[];
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get number of active blocks
|
|
87
|
+
*/
|
|
88
|
+
getActiveBlocksCount(): number;
|
|
89
|
+
|
|
90
|
+
db(key: string): any;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
declare const Lego: Lego;
|
|
94
|
+
export { Lego };
|
|
95
|
+
export default Lego;
|
package/dist/lego.min.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
(()=>{var y={},I=new WeakMap,D=new WeakMap,C=new WeakMap,X=new WeakMap,_=new Set,q=new Set,R=new Map,Q=new Map;var M=e=>(D.has(e)||D.set(e,{snapped:!1,bindings:null,bound:!1,rendering:!1,anchor:null}),D.get(e));var N={},he=e=>{N=e};var m={onError:(e,r,o)=>{console.error(`[Lego Error] [${r}]`,e,o)},metrics:{},debug:!1,syntax:"brackets"},P=(e,r)=>{!e||!r||Object.getOwnPropertyNames(e).forEach(o=>{let s=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(r,o,s)})},U=()=>m.syntax==="brackets"?["[[","]]"]:["{{","}}"],ne=new Map,j=()=>{let[e,r]=U(),o=e+r;if(ne.has(o))return ne.get(o);let s=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),t=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`${s}(.*?)${t}`,"g");return ne.set(o,a),a},be=e=>{let o=e.split("/").pop().replace(/\.lego$/,"").replace(/_/g,"-").replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();if(!o.includes("-"))throw new Error(`[Lego] Invalid block definition: "${e}". Block names must contain a hyphen (e.g. user-card.lego or UserCard.lego).`);return o},B=(e,r)=>{if(!e)return"";let o=e.trim().split("."),s=r;for(let t of o){if(s==null)return"";s=s[t]}return s??""},ae=e=>typeof Node<"u"&&e instanceof Node,O=(e,r,o)=>{if(r.nodeType!==Node.ELEMENT_NODE)return;let s=[...r.attributes].find(a=>a.name===e||a.name.startsWith(`${e}.`));if(!s)return;let t={};return o&&(t=o(s,r)),{type:e,node:r,expr:s.value,modifiers:s.name.split(".").slice(1),...t}};var Z=class{constructor(r=1e3){this.limit=r,this.cache=new Map}get(r){if(!this.cache.has(r))return;let o=this.cache.get(r);return this.cache.delete(r),this.cache.set(r,o),o}set(r,o){if(this.cache.has(r))this.cache.delete(r);else if(this.cache.size>=this.limit){let s=this.cache.keys().next().value;this.cache.delete(s)}this.cache.set(r,o)}get size(){return this.cache.size}clear(){this.cache.clear()}};var F=(e,r)=>{if(!e)return;let o=e.parentElement||(e.getRootNode?e.getRootNode().host:null);for(;o;){let s=o.tagName?o.tagName.toLowerCase():"";if(s&&(r==="*"&&y[s]||s===r.toLowerCase()))return o;o=o.parentElement||o.getRootNode&&o.getRootNode().host}},ye=(e,r)=>{if(typeof e=="function"){let s=Array.from(document.querySelectorAll("*")).filter(t=>t.tagName.includes("-"));return[].concat(e(s))}if(e.startsWith("#")){let s=document.getElementById(e.slice(1));return s?[s]:[]}let o=r?.querySelectorAll(e)||[];return o.length>0?[...o]:[...document.querySelectorAll(e)]},Y=(e,r)=>{if(e.type==="checkbox")e.checked!==!!r&&(e.checked=!!r);else{let o=r==null?"":String(r);e.value!==o&&(e.value=o)}};var ie=null,Ee=e=>{ie=e},K=[],ce=e=>{let r=K.find(t=>t.regex.test(e));if(!r)return null;let o=e.match(r.regex).slice(1),s=r.paramNames?Object.fromEntries(r.paramNames.map((t,a)=>[t,o[a]])):{};return{match:r,params:s}},ee=async(e=null,r=null)=>{if(typeof window>"u")return;let o=window.location.pathname,s=ce(o);if(m.debug&&console.log("[Lego Trace] Matching route",o,s?.match),!s)return;let{match:t,params:a}=s;if(t.middleware&&!await t.middleware({path:o,params:a}))return;let i=e&&e.length?e:["lego-router"],n=r||document;t.tagName&&i.flatMap(l=>ye(l,n)).forEach(l=>{l&&(l.innerHTML=`<${t.tagName}></${t.tagName}>`,ie&&ie(l.firstElementChild))})},te=(e,...r)=>o=>{let s=async(t,a=null,i=!0,n={})=>{if(i&&typeof history<"u"){let l={legoTargets:r.filter(d=>typeof d=="string"),method:t,body:a};history.pushState(l,"",e)}await ee(r.length?r:null,o)};return{get:(t=!0,a={})=>s("GET",null,t,a),post:(t,a=!0,i={})=>s("POST",t,a,i),put:(t,a=!0,i={})=>s("PUT",t,a,i),patch:(t,a=!0,i={})=>s("PATCH",t,a,i),delete:(t=!0,a={})=>s("DELETE",null,t,a)}};var we=new Z(1e3),x=(e,r,o=!0)=>{if(/\b(function|eval|import|class|module|deploy|constructor|__proto__)\b/.test(e)){console.warn(`[Lego] Security Warning: Blocked dangerous expression "${e}"`);return}try{let s=r.state||{},t=we.get(e);t||(t=new Function("global","self","event","helpers",`
|
|
2
|
+
with(this) {
|
|
3
|
+
with(helpers) {
|
|
4
|
+
return ${e}
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
`),we.set(e,t));let a={$ancestors:n=>F(r.self,n),$registry:n=>Q.get(n.toLowerCase()),$element:r.self,$route:N.$route,$go:(n,...c)=>te(n,...c)(r.self),$db:N.$db,$emit:(n,c)=>{r.self.dispatchEvent(new CustomEvent(n,{detail:c,bubbles:!0,composed:!0}))}},i=t.call(s,r.global,r.self,r.event,a);return typeof i=="function"?i.call(s,r.event):i}catch(s){if(o)throw s;m.onError(s,"render-error",r.self);return}},le=(e,r={})=>{if(!e||e.trim()==="{}")return{};let o=s=>new Function("scope","global",`with(global) { with(scope) { return (${s}); } }`)(r,N);try{return o(e)}catch(s){let t=e.trim();if(!t.startsWith("{")&&t.includes(":"))try{return o(`{${e}}`)}catch{}return console.error(`[Lego] Error parsing b-logic: "${e.length>80?e.slice(0,80)+"...":e}"`,s),{}}};var xe={name:"b-if",scan:(e,{checkGlobal:r,getPrivateData:o})=>O("b-if",e,(s,t)=>{r(s.value);let a=document.createComment(`b-if: ${s.value}`),i=o(t);return i.anchor=a,{anchor:a}}),execute({binding:e,state:r,global:o}){let{node:s,anchor:t,expr:a}=e,i=!!x(a,{state:r,global:o,self:s}),n=!!s.parentNode;i&&!n?t.parentNode&&t.parentNode.replaceChild(s,t):!i&&n&&s.parentNode.replaceChild(t,s)}};var ve={name:"b-show",scan(e,{checkGlobal:r}){if(e.nodeType===Node.ELEMENT_NODE&&e.hasAttribute("b-show")){let o=e.getAttribute("b-show");return r(o),{type:"b-show",node:e,expr:o}}},execute({binding:e,state:r,global:o}){let{node:s,expr:t}=e;s.style.display=x(t,{state:r,global:o,self:s})?"":"none"}};var Ne={name:"b-text",scan(e){if(e.nodeType===Node.ELEMENT_NODE&&e.hasAttribute("b-text"))return{type:"b-text",node:e,path:e.getAttribute("b-text")}},execute({binding:e,state:r}){let{node:o,path:s}=e;o.textContent=B(s,r)}};var Se={name:"b-html",scan:e=>O("b-html",e),execute:({binding:e,state:r,global:o})=>{let{node:s,expr:t}=e;s.innerHTML=x(t,{state:r,global:o,self:s})||""}};var $e={name:"b-sync",scan(e){if(e.nodeType===Node.ELEMENT_NODE&&e.hasAttribute("b-sync"))return{type:"b-sync",node:e}},execute({binding:e,state:r}){let{node:o}=e;Y(o,B(o.getAttribute("b-sync"),r))}};var Le={name:"b-for",scan(e,{checkGlobal:r,pendingOperations:o}){if(e.nodeType!==Node.ELEMENT_NODE)return;let s=e.getAttribute("b-for")?.match(/^\s*(\w+)\s+in\s+([\s\S]+?)\s*$/);if(s){r(s[2]);let t=document.createComment(`b-for: ${s[1]} in ${s[2].trim()}`);return o.push(()=>{e.parentNode&&(e.parentNode.insertBefore(t,e),e.remove())}),{type:"b-for",anchor:t,itemName:s[1],listName:s[2].trim(),keyPath:e.getAttribute("b-key")||null,template:e.cloneNode(!0)}}},execute({binding:e,state:r,global:o,helpers:s}){let{bind:t,updateNodeBindings:a}=s;if(!t||!a){console.error("[Lego] b-for directive missing required helpers: bind, updateNodeBindings");return}let{anchor:i,listName:n,itemName:c,keyPath:l,template:d}=e,{contextEl:u}=s,f=x(n,{state:r,global:o,self:u})||[];C.has(i)||C.set(i,new Map);let g=C.get(i),b=new Set,h=[];f.forEach((p,$)=>{let E;l?(E=B(l,p),E===void 0&&console.warn(`[Lego] b-key="${l}" resolved to undefined for item at index ${$}. Check for typos.`)):p&&typeof p=="object"?(E=p.id||p._id||p.uuid||p.key,E===void 0&&(X.has(p)||X.set(p,Math.random()),E=X.get(p))):E=`${$}-${p}`,b.add(E);let T=g.get(E);T||(T=d.cloneNode(!0),T.removeAttribute("b-for"),T.removeAttribute("b-key"),g.set(E,T),t(T,u,{name:c,listName:n,index:$}));let Ie=Object.assign(Object.create(r),{[c]:p,$index:$});a(T,Ie),T.querySelectorAll("[b-sync]").forEach(me=>{let ge=me.getAttribute("b-sync");if(ge.startsWith(`${c}.`)){let qe=x(n,{state:r,global:o,self:u});Y(me,B(ge.split(".").slice(1).join("."),qe[$]))}}),h.push(T)});let w=i.nextSibling;h.forEach(p=>{p!==w?i.parentNode.insertBefore(p,w):w=w.nextSibling});for(let[p,$]of g.entries())b.has(p)||($.remove(),g.delete(p))}};var _e={scan:e=>{if(e.hasAttribute("b-init"))return{type:"b-init",node:e,expr:e.getAttribute("b-init")}},execute:({binding:e,state:r})=>{e.initialized||(e.initialized=!0,x(e.expr,{state:r}))}};var Te={name:"b-enter",scan:e=>O("b-enter",e,(r,o)=>({modifiers:r.name.split(".").slice(1)})),execute:({binding:e,state:r})=>{if(e.observer||e.done)return;let o=new IntersectionObserver((s,t)=>{s[0].isIntersecting&&(x(e.expr,{state:r}),e.modifiers.includes("once")&&(t.disconnect(),e.observer=null,e.done=!0))});o.observe(e.node),e.observer=o},destroy:({binding:e})=>{e.observer&&(e.observer.disconnect(),e.observer=null)}};var Ae={scan:e=>O("b-leave",e),execute:({binding:e,state:r})=>{if(e.observer||e.done)return;let o=new IntersectionObserver((s,t)=>{let a=s[0];a.isIntersecting&&(e.activated=!0),!a.isIntersecting&&e.activated&&(x(e.expr,{state:r}),e.modifiers.includes("once")&&(t.disconnect(),e.observer=null,e.done=!0))});o.observe(e.node),e.observer=o},destroy:({binding:e})=>{e.observer&&(e.observer.disconnect(),e.observer=null)}};var G={"b-if":xe,"b-show":ve,"b-text":Ne,"b-html":Se,"b-sync":$e,"b-for":Le,"b-init":_e,"b-enter":Te,"b-leave":Ae};var Ue=(e=null)=>{let r=!1,o=new Set,s=!1,t=e,a=new Set,i=null,n=new Set,c=f=>{t=f},l=()=>{i&&clearTimeout(i),i=setTimeout(()=>{n.forEach(f=>{let g=f._studs;if(g&&typeof g.updated=="function")try{g.updated.call(g)}catch(b){console.error("[Lego] Error in updated hook:",b)}}),n.clear(),i=null},50)},d=()=>{a.size>0&&(a.forEach(f=>o.add(f)),a.clear(),!r&&o.size>0&&(r=!0,requestAnimationFrame(u)))},u=()=>{s=!0;let f=Array.from(o);o.clear(),r=!1,f.forEach(g=>{g.isConnected&&t&&t(g)}),f.forEach(g=>n.add(g)),l(),s=!1,d()};return{add:f=>{if(f){if(s){a.add(f);return}o.add(f),!r&&(r=!0,requestAnimationFrame(u))}},setHandler:c}},re=Ue();var L=new Map,oe=new Map,J=new WeakMap,V=new Map,je=e=>{let r=Array.from(V.entries()).filter(([t])=>t.startsWith("lego:")).sort((t,a)=>t[1].timestamp-a[1].timestamp),o=0,s=[];for(let[t,a]of r){if(o>=e)break;try{localStorage.removeItem(t),o+=a.size,s.push(t),V.delete(t)}catch(i){console.error(`[Lego] Failed to evict ${t}:`,i)}}return s},ke=(e,r,o)=>{m.debug&&console.log("[Lego Trace] scheduleSave",e,r,o),oe.has(e)&&clearTimeout(oe.get(e));let s=()=>{try{let t=JSON.stringify(r),a=new Blob([t]).size;localStorage.setItem(e,t);let i=e.startsWith("lego:")?e:`lego:${e}`;V.set(i,{timestamp:Date.now(),size:a}),oe.delete(e)}catch(t){if(t.name==="QuotaExceededError"){console.warn(`[Lego] Storage quota exceeded for key: ${e}`);try{let a=JSON.stringify(r),i=new Blob([a]).size,n=je(i*2);if(n.length>0){m.debug&&console.log(`[Lego] Evicted ${n.length} old keys, retrying save`),localStorage.setItem(e,a);let c=e.startsWith("lego:")?e:`lego:${e}`;V.set(c,{timestamp:Date.now(),size:i})}else m.onError(new Error("Storage quota exceeded and no keys available for eviction"),"quota",e)}catch{m.onError(new Error(`Critical: Could not save ${e} even after eviction`),"quota-critical",e)}}else console.error(`[Lego] Storage Error (${e}):`,t)}};o>0?oe.set(e,setTimeout(s,o)):s()},fe=e=>({__type:"lego-db",key:e,_default:void 0,_debounce:0,default(o){return this._default=o,this},debounce(o){return this._debounce=o,this},set(o,s=0){return ke(e,o,s),L.has(e)&&L.get(e).forEach(({target:t,prop:a,el:i,batcher:n})=>{t[a]=o,i&&n&&n.add(i)}),o},get(){try{let o=localStorage.getItem(e);return o!==null?JSON.parse(o):null}catch(o){return console.warn(`[Lego] Failed to get localStorage value for key "${e}":`,o),null}},delete(){try{return localStorage.removeItem(e),V.delete(e.startsWith("lego:")?e:`lego:${e}`),L.has(e)&&L.get(e).forEach(({target:o,prop:s,el:t,batcher:a})=>{o[s]=null,t&&a&&a.add(t)}),!0}catch(o){return console.error(`[Lego] Failed to delete localStorage key "${e}":`,o),!1}}}),Ce=e=>e&&e.__type==="lego-db",Me=(e,r,o,s,t)=>{m.debug&&console.log("[Lego Trace] Reactive DB Init:",r,o);let a=o._default;try{let i=localStorage.getItem(o.key);i!==null&&(a=JSON.parse(i))}catch(i){console.warn(`[Lego] Failed to parse localStorage value for key "${o.key}":`,i)}e[r]=a,J.has(e)||J.set(e,{}),J.get(e)[r]={key:o.key,debounce:o._debounce},m.debug&&console.log("[Lego Trace] DB Metadata Set:",r,J.get(e)[r]),L.has(o.key)||L.set(o.key,new Set),L.get(o.key).add({target:e,prop:r,el:s,batcher:t})},Oe=(e,r,o)=>{let s=J.get(e);if(s&&s[r]){let t=s[r].key;ke(t,o,s[r].debounce),L.has(t)&&L.get(t).forEach(({target:a,prop:i,el:n,batcher:c})=>{a!==e&&(a[i]=o,n&&c&&c.add(n))})}},Be=()=>{typeof window<"u"&&window.addEventListener("storage",e=>{if(!(!e.key||!L.has(e.key)))try{let r=JSON.parse(e.newValue);L.get(e.key).forEach(({target:o,prop:s,el:t,batcher:a})=>{o[s]=r,t&&a&&a.add(t)})}catch(r){console.warn("[Lego] Cross-tab sync error:",r)}})};var z=(e,r,o=re)=>{if(e===null||typeof e!="object"||ae(e))return e;if(I.has(e))return I.get(e);for(let a in e){let i=Object.getOwnPropertyDescriptor(e,a);if(i&&i.get)continue;let n=e[a];Ce(n)&&Me(e,a,n,r,o)}let s={get:(a,i)=>{let n=Reflect.get(a,i);return n!==null&&typeof n=="object"&&!ae(n)?z(n,r,o):n},set:(a,i,n,c)=>{let l=a[i];m.debug&&l!==n&&console.log("[Lego Trace] Reactive SET:",i,"Old:",l,"New:",n,"Target:",a);let d=Reflect.set(a,i,n,c);return c===I.get(a)&&l!==n&&(o.add(r),Oe(a,i,n)),d},deleteProperty:(a,i)=>{let n=Reflect.deleteProperty(a,i);return o.add(r),n}},t=new Proxy(e,s);return I.set(e,t),t};var Ke=e=>{let r=e;for(;r;){let o=r.getAttribute&&r.getAttribute("b-error");if(o)return{errorTag:o,targetElement:r};let s=r.tagName?r.tagName.toLowerCase():"",t=y[s];if(t){let a=t.getAttribute("b-error");if(a)return{errorTag:a,targetElement:r}}r=r.parentElement||r.getRootNode&&r.getRootNode().host}return null},de=null,ue=null,De=(e,r)=>{de=e,ue=r},Ge=(e,r,o)=>{if(!de||!ue){console.error("[Lego] Error boundary dependencies not loaded",o);return}try{r.shadowRoot?r.shadowRoot.innerHTML="":r.innerHTML="";let s=document.createElement(e);(r.shadowRoot||r).appendChild(s),de(s),s._studs&&(s._studs.$error={message:o.message,stack:o.stack,component:r.tagName.toLowerCase()},ue(s))}catch(s){console.error("[Lego] Error boundary failed to render:",s),console.error("[Lego] Original error:",o)}},W=(e,r,o)=>{let s=Ke(r);s?Ge(s.errorTag,s.targetElement,e):m.onError(e,o,r)};var se=(e,r,o=null)=>{let s=r._studs,t=n=>{let c=M(n);if(!c.bound){if(n.hasAttributes()){let l=n.attributes;for(let d=0;d<l.length;d++){let u=l[d];if(u.name.startsWith("@")){let f=u.name.slice(1).split("."),g=f[0],b=f.slice(1);n.addEventListener(g,h=>{if(b.includes("prevent")&&h.preventDefault(),b.includes("stop")&&h.stopPropagation(),!(b.includes("self")&&h.target!==h.currentTarget)){if(typeof KeyboardEvent<"u"&&h instanceof KeyboardEvent){if(b.includes("ctrl")&&!h.ctrlKey||b.includes("alt")&&!h.altKey||b.includes("shift")&&!h.shiftKey||b.includes("meta")&&!h.metaKey)return;let w=b.filter(p=>!["prevent","stop","self","ctrl","alt","shift","meta","capture","once","passive"].includes(p));if(w.length>0){let p=h.key.toLowerCase();if(!w.some(E=>E==="enter"?p==="enter":E==="esc"||E==="escape"?p==="escape":E==="space"?p===" ":E==="tab"?p==="tab":E==="delete"?p==="delete":E==="backspace"?p==="backspace":E==="up"?p==="arrowup":E==="down"?p==="arrowdown":E==="left"?p==="arrowleft":E==="right"?p==="arrowright":E==="alpha"?/^[a-z]$/.test(p):E==="numbers"?/^[0-9]$/.test(p):E===p))return}}try{let w=s;if(o){let $=x(o.listName,{state:s,global:N,self:r})[o.index];w=Object.assign(Object.create(s),{[o.name]:$})}x(u.value,{state:w,global:N,self:n,event:h,$event:h},!0)}catch(w){W(w,r,"event-handler")}}})}}if(n.hasAttribute("b-sync")){let d=n.getAttribute("b-sync"),u=()=>{try{let f,g;if(o&&d.startsWith(`${o.name}.`)){let w=x(o.listName,{state:s,global:N,self:r})[o.index];if(!w)return;let p=d.split(".").slice(1);g=p.pop(),f=p.reduce(($,E)=>$[E],w)}else{let h=d.split(".");g=h.pop(),f=h.reduce((w,p)=>w[p],s)}let b=n.type==="checkbox"?n.checked:n.value;f&&f[g]!==b&&(f[g]=b)}catch(f){m.onError(f,"sync-update",n)}};n.addEventListener("input",u),n.addEventListener("change",u)}if(n.hasAttribute("b-var")){let d=n.getAttribute("b-var");s.$vars&&(s.$vars[d]=n)}}c.bound=!0}};e instanceof Element&&t(e);let a=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT),i;for(;i=a.nextNode();)t(i)},Je=e=>{let r=[],o=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT),s,t=[];for(;s=o.nextNode();){let a=s;if((c=>{let l=c.parentNode;for(;l&&l!==e;){if(l.hasAttribute&&l.hasAttribute("b-for"))return!0;l=l.parentNode}return!1})(s))continue;let n=c=>{if(/\bglobal\b/.test(c)){let l=e.host||e;q.add(l)}};if(s.nodeType===Node.ELEMENT_NODE){for(let[l,d]of Object.entries(G))if(d.scan){let u=d.scan(s,{checkGlobal:n,getPrivateData:M,pendingOperations:t,current:a});u&&r.push(u)}let[c]=U();[...s.attributes].forEach(l=>{l.value.includes(c)&&(n(l.value),r.push({type:"attr",node:s,attrName:l.name,template:l.value}))})}else if(s.nodeType===Node.TEXT_NODE){let[c]=U();s.textContent.includes(c)&&(n(s.textContent),r.push({type:"text",node:s,template:s.textContent}))}}return t.forEach(a=>a()),r},Ve=(e,r)=>{let o=a=>{if(a.nodeType===Node.TEXT_NODE){a._tpl===void 0&&(a._tpl=a.textContent);let i=a._tpl.replace(j(),(n,c)=>x(c.trim(),{state:r,global:N,self:a})??"");a.textContent!==i&&(a.textContent=i)}else if(a.nodeType===Node.ELEMENT_NODE){let[i]=U();[...a.attributes].forEach(n=>{if(n._tpl===void 0&&(n._tpl=n.value),n._tpl.includes(i)){let c=n._tpl.replace(j(),(l,d)=>x(d.trim(),{state:r,global:N,self:a})??"");n.value!==c&&(n.value=c,n.name==="class"&&(a.className=c))}})}};o(e);let s=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT),t;for(;t=s.nextNode();)o(t)},H=e=>{m.debug&&console.log("[Lego Trace] render() called for:",e.tagName);let r=e._studs;if(!r)return;let o=M(e);if(!o.rendering){o.rendering=!0,m.metrics&&m.metrics.onRenderStart&&m.metrics.onRenderStart(e);try{let s=e.shadowRoot||e;o.bindings||(o.bindings=Je(s)),o.bindings.forEach(t=>{let a=G[t.type];if(a){a.execute({binding:t,state:r,global:N,helpers:{bind:se,updateNodeBindings:Ve,contextEl:e}});return}if(t.type==="text"){let i=t.template.replace(j(),(n,c)=>x(c.trim(),{state:r,global:N,self:t.node})??"");t.node.textContent!==i&&(t.node.textContent=i)}if(t.type==="attr"){let i=t.template.replace(j(),(n,c)=>x(c.trim(),{state:r,global:N,self:t.node})??"");t.node.getAttribute(t.attrName)!==i&&(t.node.setAttribute(t.attrName,i),t.attrName==="class"&&(t.node.className=i))}}),r===N&&q.forEach(t=>H(t))}catch(s){W(s,e,"render")}finally{m.metrics&&m.metrics.onRenderEnd&&m.metrics.onRenderEnd(e),o.rendering=!1}}};var Re=new Map,Pe={},Fe=e=>{Pe=e},ze=async()=>{let e=Object.entries(Pe).map(async([r,o])=>{let s=await Promise.all(o.map(async t=>{try{if(t instanceof CSSStyleSheet)return t;let a=await fetch(t);if(!a.ok)throw new Error(`Status ${a.status}`);let i=await a.text(),n=new CSSStyleSheet;return await n.replace(i),n}catch(a){return console.error(`[Lego] Failed to load stylesheet: ${t}`,a),null}}));Re.set(r,s.filter(t=>t!==null))});await Promise.all(e),m.debug&&console.log("[Lego Debug] Re-applying stylesheets to",_.size,"blocks"),_.forEach(r=>{pe(r)})},pe=e=>{if(!e.shadowRoot&&!e._legoShadow)return;let r=e.shadowRoot||e._legoShadow,o=e.tagName.toLowerCase(),s=y[o],t=(s&&s.getAttribute("b-stylesheets")||"").split(/\s+/).filter(Boolean),a=(e.getAttribute("b-stylesheets")||"").split(/\s+/).filter(Boolean),i=[],n=e.parentElement||(e.getRootNode?e.getRootNode().host:null);for(;n;){let u=(n.getAttribute("b-cascade")||"").split(/\s+/).filter(Boolean);u.length&&i.push(...u);let f=n.tagName?n.tagName.toLowerCase():"";if(y[f]){let g=(y[f].getAttribute("b-cascade")||"").split(/\s+/).filter(Boolean);g.length&&i.push(...g)}n=n.parentElement||n.getRootNode&&n.getRootNode().host}let c=[...new Set([...t,...a])],l=[...new Set(i)],d=[...new Set([...l,...c])];if(d.length>0){let u=d.flatMap(f=>Re.get(f)||[]);u.length>0&&m.debug&&console.log("[Lego Debug] Applying styles to",e.tagName,"- Names:",d,"- Sheets:",u.length),u.length>0&&(r.adoptedStyleSheets=[...u])}e._studsMeta={stylesheets:{explicit:c,inherited:l,applied:d}}};var S=e=>{if(m.debug&&console.log("[Lego Trace] snap() called for:",e.tagName),!e||e.nodeType!==Node.ELEMENT_NODE)return;let r=M(e),o=e.tagName.toLowerCase(),s=y[o];if(s&&!r.snapped){r.snapped=!0;let a=s.content.cloneNode(!0),i=e.shadowRoot;i?i.innerHTML="":i=e.attachShadow({mode:"open"}),pe(e);let n=F(e,"*")||F(e.getRootNode().host,"*"),c=n&&n.state?n.state:{},l=R.get(o)||{},d=le(s.getAttribute("b-logic")||"{}"),u=le(e.getAttribute("b-logic")||"{}",c),f={$vars:{},$element:e,get $parent(){return F(e,"*")},$emit:(h,w)=>{e.dispatchEvent(new CustomEvent(h,{detail:w,bubbles:!0,composed:!0}))},get $route(){return N.$route},get $go(){return N.$go}};P(l,f),P(d,f),P(u,f),e._studs=z(f,e),Object.defineProperty(e,"state",{get(){return this._studs},set(h){Object.assign(this._studs,h)},configurable:!0,enumerable:!1}),i.appendChild(a);let g=i.querySelector("style");g&&(g.textContent=g.textContent.replace(/\bself\b/g,":host")),se(i,e),_.add(e),H(e),e.setAttribute("b-id",o);let b=i.querySelectorAll("*");if(m.debug&&console.log("[Lego Debug] Nested scan in",e.tagName,"- Found elements:",b.length),b.forEach(h=>{let w=h.tagName.toLowerCase(),p=!!y[w];p&&m.debug&&console.log("[Lego Debug] Checking",w,"- Registered:",p),p&&S(h)}),e._legoShadow||(e._legoShadow=i),typeof e._studs.mounted=="function")try{e._studs.mounted.call(e._studs)}catch(h){W(h,e,"mounted")}}let t=e.parentElement;for(;t&&!t._studs;)t=t.parentElement;t&&t._studs&&se(e,t),[...e.children].forEach(S)},A=e=>{if(e._studs&&typeof e._studs.unmounted=="function")try{e._studs.unmounted.call(e._studs)}catch(o){W(o,e,"unmounted")}e.shadowRoot&&[...e.shadowRoot.children].forEach(A),_.delete(e),q.delete(e),e._studs&&(e._studs.$element=null,e._studs.$vars&&(Object.keys(e._studs.$vars).forEach(o=>{e._studs.$vars[o]=null}),e._studs.$vars=null),e._studs.$emit&&(e._studs.$emit=null),delete e._studs.$parent,delete e._studs.$route,delete e._studs.$go,e._studs=null),e.hasOwnProperty("state")&&delete e.state;let r=D.get(e);if(r&&(r.bindings&&(r.bindings.forEach(o=>{let s=G[o.type];if(s&&s.destroy)try{s.destroy({binding:o})}catch(t){console.error(`[Lego] Error in directive cleanup (${o.type}):`,t)}o.node=null,o.anchor=null}),r.bindings=null),r.anchor=null,D.delete(e)),C.has(e)){let o=C.get(e);o.forEach((s,t)=>{s&&s._studs&&(s._studs=null)}),o.clear(),C.delete(e)}[...e.children].forEach(A)};function We(e,r="block.lego"){let o={template:"",script:"",style:"",stylesAttr:"",cascadeAttr:"",errorAttr:"",blockName:be(r)},s=e,t=/<(template|script|style)\b((?:\s+(?:[^>"']|"[^"]*"|'[^']*')*)*)>/i;for(;s;){let a=s.match(t);if(!a)break;let i=a[1].toLowerCase(),n=a[2],c=a[0],l=a.index,d=`</${i}>`,u=l+c.length,f=s.indexOf(d,u);if(f===-1){console.warn(`[Lego] Unclosed <${i}> tag in ${r}`);break}let g=s.slice(u,f);if(i==="template"){o.template=g.trim();let b=n.match(/b-stylesheets=["']([^"']+)["']/);b&&(o.stylesAttr=b[1]);let h=n.match(/b-cascade=["']([^"']+)["']/);h&&(o.cascadeAttr=h[1]);let w=n.match(/b-error=["']([^"']+)["']/);w&&(o.errorAttr=w[1])}else if(i==="script"){o.script=g.trim();let b=n.match(/lang=["']([^"']+)["']/);b&&(o.scriptLang=b[1])}else i==="style"&&(o.style=g.trim());s=s.slice(f+d.length)}return o}var He=(e,r,o="block.lego")=>{let s=We(r,o),{blockName:t,template:a,script:i,style:n,stylesAttr:c,cascadeAttr:l,errorAttr:d}=s,u={};if(i)try{let g=i.trim(),b=g.match(/export\s+default\s+({[\s\S]*})/),h=b?b[1]:g;u=new Function("Lego","$db",`return ${h}`)(e,e.globals.$db)}catch(g){m.onError(g,"script",t)}let f=a;n&&(f=`<style>${n}</style>`+f),y[t]=document.createElement("template"),y[t].innerHTML=f,c&&y[t].setAttribute("b-stylesheets",c),l&&y[t].setAttribute("b-cascade",l),d&&y[t].setAttribute("b-error",d),R.set(t,u),document.querySelectorAll(t).forEach(g=>!M(g).snapped&&S(g))};re.setHandler(H);De(S,H);Ee(S);var Xe={url:typeof window<"u"?window.location.pathname:"/",route:"",params:{},query:{},method:"GET",body:null},k=z({$route:Xe,$go:(e,...r)=>te(e,...r)(document.body),$db:fe},typeof document<"u"?document.body:null);he(k);var v={db:fe,snap:S,unsnap:A,defineLegoFile:(e,r)=>He(v,e,r),block:(e,r,o={},s="",t="",a="")=>{let i=document.createElement("template");i.setAttribute("b-id",e),i.setAttribute("b-stylesheets",s),t&&i.setAttribute("b-cascade",t),a&&i.setAttribute("b-error",a),i.innerHTML=r,y[e]=i,R.set(e,o);try{let n={};P(o,n),Q.set(e.toLowerCase(),z(n,document.body))}catch(n){m.onError(n,"define",e)}if(customElements.get(e))document.querySelectorAll(e).forEach(n=>{A(n),S(n)}),_.forEach(n=>{n._legoShadow&&n._legoShadow.querySelectorAll(e).forEach(c=>{A(c),S(c)})});else try{customElements.define(e,class extends HTMLElement{connectedCallback(){y[e]&&S(this)}disconnectedCallback(){A(this)}})}catch(n){console.warn(`[Lego] Failed to register web component ${e}:`,n)}},getActiveBlocksCount:()=>_.size,getLegos:()=>Object.keys(y),config:m,globals:k,route:(e,r,o=null)=>{let s=[],t=e==="*"?".*":e;t=t.replace(/:([^\/]+)/g,(a,i)=>(s.push(i),"([^/]+)")),K.push({path:e,regex:new RegExp(`^${t}$`),tagName:r,paramNames:s,middleware:o})},debug:{stylesheets:e=>!e||!e._studsMeta?null:e._studsMeta.stylesheets},init:async(e=document.body,r={})=>{if((!e||typeof e.nodeType!="number")&&(e=document.body),Fe(r.styles||{}),m.loader=r.loader,m.debug=r.debug===!0,await ze(),K.length>0){let t=window.location.pathname,a=window.location.search,i=ce(t);if(i){let{match:n,params:c}=i,l=Object.fromEntries(new URLSearchParams(a));k.$route.url=t+a,k.$route.route=n.path,k.$route.params=c,k.$route.query=l,k.$route.method="GET",k.$route.body=null}}document.querySelectorAll("template[b-id]").forEach(t=>{let a=t.getAttribute("b-id");y[a]=t,customElements.get(a)||customElements.define(a,class extends HTMLElement{connectedCallback(){y[a]&&S(this)}disconnectedCallback(){A(this)}})});let o=t=>{if(!t)return{};let a={};return Object.entries(t).forEach(([i,n])=>{if(typeof n=="function")a[i]=n();else if(typeof n=="string"&&n.startsWith("$db.")){let c=n.slice(4);a[i]=v.db(c).get()}else if(typeof n=="string"&&n.startsWith("$globals.")){let c=n.slice(9);a[i]=N[c]}else a[i]=n}),a},s=(t,a,i={})=>{customElements.get(t)||customElements.define(t,class extends HTMLElement{async connectedCallback(){if(y[t]){S(this);return}if(v._fetching||(v._fetching=new Set),!v._fetching.has(t)){v._fetching.add(t);try{let n=a.startsWith("POST:")?"POST":"GET",c=a.replace(/^POST:/,""),l=o(i.headers),d={method:n,headers:l,credentials:i.credentials},f=await(await fetch(c,d)).text();v.defineLegoFile(f,`${t}.lego`),y[t]&&S(this)}catch(n){console.error(`[Lego] Failed to load manifest block ${t}:`,n)}finally{v._fetching.delete(t)}}}disconnectedCallback(){A(this)}})};if(r.manifest){let t=r.manifest;if(t.url&&typeof t.url=="string")try{let i=o(t.headers);t=await(await fetch(t.url,{headers:i,credentials:t.credentials})).json()}catch(i){console.error("[Lego] Failed to load manifest:",i),t=[]}else if(typeof t=="string")try{t=await(await fetch(t)).json()}catch(i){console.error("[Lego] Failed to load manifest:",i),t=[]}let a=Array.isArray(t)?t:[t];for(let i of a){let n=i.base||"",c=i.suffix===!0?".lego":i.suffix||"",l={headers:i.headers||{},credentials:i.credentials};i.legos&&i.legos.forEach(d=>{s(d,`${n}${d}${c}`,l)}),i.map&&Object.entries(i.map).forEach(([d,u])=>{let f=u.match(/^https?:|^\//)?u:`${n}${u}`;s(d,f,l)})}}if(m.loader){let t=n=>{if(n.nodeType!==Node.ELEMENT_NODE)return;let c=n.tagName.toLowerCase();if(c.includes("-")&&!y[c]&&!_.has(n)){if(v._fetching&&v._fetching.has(c))return;v._fetching||(v._fetching=new Set),v._fetching.add(c);let l=m.loader(c);if(l){let d=typeof l=="string"?fetch(l).then(u=>u.text()):l;Promise.resolve(d).then(u=>{v.defineLegoFile(u,`${c}.lego`),v._fetching.delete(c)}).catch(u=>{console.error(`[Lego] Failed to load ${c}:`,u),v._fetching.delete(c)})}}if(n.children.length>0){let l=n.firstElementChild;for(;l;)t(l),l=l.nextElementSibling}};new MutationObserver(n=>n.forEach(c=>{c.addedNodes.forEach(t)})).observe(e,{childList:!0,subtree:!0});let i=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT);for(;i.nextNode();)t(i.currentNode)}if(r.studio){if(!y["lego-studio"]){let t=document.createElement("script");t.src="https://unpkg.com/@legodom/studio@0.0.2/dist/lego-studio.js",t.onerror=()=>console.warn("[Lego] Failed to load Studio from CDN"),document.head.appendChild(t)}v.route("/_/studio","lego-studio"),v.route("/_/studio/:block","lego-studio")}K.length>0&&(window.addEventListener("popstate",t=>{let a=t.state?.legoTargets||null;ee(a)}),document.addEventListener("submit",t=>{t.preventDefault()}),document.addEventListener("click",t=>{let i=t.composedPath().find(n=>n.tagName==="A"&&(n.hasAttribute("b-target")||n.hasAttribute("b-link")));if(i){t.preventDefault();let n=i.getAttribute("href"),c=i.getAttribute("b-target"),l=c?c.split(/\s+/).filter(Boolean):[],d=i.getAttribute("b-link")!=="false";k.$go(n,...l).get(d)}}),ee()),Be()}};typeof window<"u"?window.Lego=v:typeof global<"u"&&(global.Lego=v);var ho=v;})();
|
package/dist/lego.mjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
var y={},I=new WeakMap,D=new WeakMap,C=new WeakMap,X=new WeakMap,_=new Set,q=new Set,R=new Map,Q=new Map;var M=e=>(D.has(e)||D.set(e,{snapped:!1,bindings:null,bound:!1,rendering:!1,anchor:null}),D.get(e));var N={},he=e=>{N=e};var m={onError:(e,r,o)=>{console.error(`[Lego Error] [${r}]`,e,o)},metrics:{},debug:!1,syntax:"brackets"},P=(e,r)=>{!e||!r||Object.getOwnPropertyNames(e).forEach(o=>{let s=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(r,o,s)})},U=()=>m.syntax==="brackets"?["[[","]]"]:["{{","}}"],ne=new Map,j=()=>{let[e,r]=U(),o=e+r;if(ne.has(o))return ne.get(o);let s=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),t=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`${s}(.*?)${t}`,"g");return ne.set(o,a),a},be=e=>{let o=e.split("/").pop().replace(/\.lego$/,"").replace(/_/g,"-").replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();if(!o.includes("-"))throw new Error(`[Lego] Invalid block definition: "${e}". Block names must contain a hyphen (e.g. user-card.lego or UserCard.lego).`);return o},B=(e,r)=>{if(!e)return"";let o=e.trim().split("."),s=r;for(let t of o){if(s==null)return"";s=s[t]}return s??""},ae=e=>typeof Node<"u"&&e instanceof Node,O=(e,r,o)=>{if(r.nodeType!==Node.ELEMENT_NODE)return;let s=[...r.attributes].find(a=>a.name===e||a.name.startsWith(`${e}.`));if(!s)return;let t={};return o&&(t=o(s,r)),{type:e,node:r,expr:s.value,modifiers:s.name.split(".").slice(1),...t}};var Z=class{constructor(r=1e3){this.limit=r,this.cache=new Map}get(r){if(!this.cache.has(r))return;let o=this.cache.get(r);return this.cache.delete(r),this.cache.set(r,o),o}set(r,o){if(this.cache.has(r))this.cache.delete(r);else if(this.cache.size>=this.limit){let s=this.cache.keys().next().value;this.cache.delete(s)}this.cache.set(r,o)}get size(){return this.cache.size}clear(){this.cache.clear()}};var F=(e,r)=>{if(!e)return;let o=e.parentElement||(e.getRootNode?e.getRootNode().host:null);for(;o;){let s=o.tagName?o.tagName.toLowerCase():"";if(s&&(r==="*"&&y[s]||s===r.toLowerCase()))return o;o=o.parentElement||o.getRootNode&&o.getRootNode().host}},ye=(e,r)=>{if(typeof e=="function"){let s=Array.from(document.querySelectorAll("*")).filter(t=>t.tagName.includes("-"));return[].concat(e(s))}if(e.startsWith("#")){let s=document.getElementById(e.slice(1));return s?[s]:[]}let o=r?.querySelectorAll(e)||[];return o.length>0?[...o]:[...document.querySelectorAll(e)]},Y=(e,r)=>{if(e.type==="checkbox")e.checked!==!!r&&(e.checked=!!r);else{let o=r==null?"":String(r);e.value!==o&&(e.value=o)}};var ie=null,Ee=e=>{ie=e},K=[],ce=e=>{let r=K.find(t=>t.regex.test(e));if(!r)return null;let o=e.match(r.regex).slice(1),s=r.paramNames?Object.fromEntries(r.paramNames.map((t,a)=>[t,o[a]])):{};return{match:r,params:s}},ee=async(e=null,r=null)=>{if(typeof window>"u")return;let o=window.location.pathname,s=ce(o);if(m.debug&&console.log("[Lego Trace] Matching route",o,s?.match),!s)return;let{match:t,params:a}=s;if(t.middleware&&!await t.middleware({path:o,params:a}))return;let i=e&&e.length?e:["lego-router"],n=r||document;t.tagName&&i.flatMap(l=>ye(l,n)).forEach(l=>{l&&(l.innerHTML=`<${t.tagName}></${t.tagName}>`,ie&&ie(l.firstElementChild))})},te=(e,...r)=>o=>{let s=async(t,a=null,i=!0,n={})=>{if(i&&typeof history<"u"){let l={legoTargets:r.filter(d=>typeof d=="string"),method:t,body:a};history.pushState(l,"",e)}await ee(r.length?r:null,o)};return{get:(t=!0,a={})=>s("GET",null,t,a),post:(t,a=!0,i={})=>s("POST",t,a,i),put:(t,a=!0,i={})=>s("PUT",t,a,i),patch:(t,a=!0,i={})=>s("PATCH",t,a,i),delete:(t=!0,a={})=>s("DELETE",null,t,a)}};var we=new Z(1e3),x=(e,r,o=!0)=>{if(/\b(function|eval|import|class|module|deploy|constructor|__proto__)\b/.test(e)){console.warn(`[Lego] Security Warning: Blocked dangerous expression "${e}"`);return}try{let s=r.state||{},t=we.get(e);t||(t=new Function("global","self","event","helpers",`
|
|
2
|
+
with(this) {
|
|
3
|
+
with(helpers) {
|
|
4
|
+
return ${e}
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
`),we.set(e,t));let a={$ancestors:n=>F(r.self,n),$registry:n=>Q.get(n.toLowerCase()),$element:r.self,$route:N.$route,$go:(n,...c)=>te(n,...c)(r.self),$db:N.$db,$emit:(n,c)=>{r.self.dispatchEvent(new CustomEvent(n,{detail:c,bubbles:!0,composed:!0}))}},i=t.call(s,r.global,r.self,r.event,a);return typeof i=="function"?i.call(s,r.event):i}catch(s){if(o)throw s;m.onError(s,"render-error",r.self);return}},le=(e,r={})=>{if(!e||e.trim()==="{}")return{};let o=s=>new Function("scope","global",`with(global) { with(scope) { return (${s}); } }`)(r,N);try{return o(e)}catch(s){let t=e.trim();if(!t.startsWith("{")&&t.includes(":"))try{return o(`{${e}}`)}catch{}return console.error(`[Lego] Error parsing b-logic: "${e.length>80?e.slice(0,80)+"...":e}"`,s),{}}};var xe={name:"b-if",scan:(e,{checkGlobal:r,getPrivateData:o})=>O("b-if",e,(s,t)=>{r(s.value);let a=document.createComment(`b-if: ${s.value}`),i=o(t);return i.anchor=a,{anchor:a}}),execute({binding:e,state:r,global:o}){let{node:s,anchor:t,expr:a}=e,i=!!x(a,{state:r,global:o,self:s}),n=!!s.parentNode;i&&!n?t.parentNode&&t.parentNode.replaceChild(s,t):!i&&n&&s.parentNode.replaceChild(t,s)}};var ve={name:"b-show",scan(e,{checkGlobal:r}){if(e.nodeType===Node.ELEMENT_NODE&&e.hasAttribute("b-show")){let o=e.getAttribute("b-show");return r(o),{type:"b-show",node:e,expr:o}}},execute({binding:e,state:r,global:o}){let{node:s,expr:t}=e;s.style.display=x(t,{state:r,global:o,self:s})?"":"none"}};var Ne={name:"b-text",scan(e){if(e.nodeType===Node.ELEMENT_NODE&&e.hasAttribute("b-text"))return{type:"b-text",node:e,path:e.getAttribute("b-text")}},execute({binding:e,state:r}){let{node:o,path:s}=e;o.textContent=B(s,r)}};var Se={name:"b-html",scan:e=>O("b-html",e),execute:({binding:e,state:r,global:o})=>{let{node:s,expr:t}=e;s.innerHTML=x(t,{state:r,global:o,self:s})||""}};var $e={name:"b-sync",scan(e){if(e.nodeType===Node.ELEMENT_NODE&&e.hasAttribute("b-sync"))return{type:"b-sync",node:e}},execute({binding:e,state:r}){let{node:o}=e;Y(o,B(o.getAttribute("b-sync"),r))}};var Le={name:"b-for",scan(e,{checkGlobal:r,pendingOperations:o}){if(e.nodeType!==Node.ELEMENT_NODE)return;let s=e.getAttribute("b-for")?.match(/^\s*(\w+)\s+in\s+([\s\S]+?)\s*$/);if(s){r(s[2]);let t=document.createComment(`b-for: ${s[1]} in ${s[2].trim()}`);return o.push(()=>{e.parentNode&&(e.parentNode.insertBefore(t,e),e.remove())}),{type:"b-for",anchor:t,itemName:s[1],listName:s[2].trim(),keyPath:e.getAttribute("b-key")||null,template:e.cloneNode(!0)}}},execute({binding:e,state:r,global:o,helpers:s}){let{bind:t,updateNodeBindings:a}=s;if(!t||!a){console.error("[Lego] b-for directive missing required helpers: bind, updateNodeBindings");return}let{anchor:i,listName:n,itemName:c,keyPath:l,template:d}=e,{contextEl:u}=s,f=x(n,{state:r,global:o,self:u})||[];C.has(i)||C.set(i,new Map);let g=C.get(i),b=new Set,h=[];f.forEach((p,$)=>{let E;l?(E=B(l,p),E===void 0&&console.warn(`[Lego] b-key="${l}" resolved to undefined for item at index ${$}. Check for typos.`)):p&&typeof p=="object"?(E=p.id||p._id||p.uuid||p.key,E===void 0&&(X.has(p)||X.set(p,Math.random()),E=X.get(p))):E=`${$}-${p}`,b.add(E);let T=g.get(E);T||(T=d.cloneNode(!0),T.removeAttribute("b-for"),T.removeAttribute("b-key"),g.set(E,T),t(T,u,{name:c,listName:n,index:$}));let Ie=Object.assign(Object.create(r),{[c]:p,$index:$});a(T,Ie),T.querySelectorAll("[b-sync]").forEach(me=>{let ge=me.getAttribute("b-sync");if(ge.startsWith(`${c}.`)){let qe=x(n,{state:r,global:o,self:u});Y(me,B(ge.split(".").slice(1).join("."),qe[$]))}}),h.push(T)});let w=i.nextSibling;h.forEach(p=>{p!==w?i.parentNode.insertBefore(p,w):w=w.nextSibling});for(let[p,$]of g.entries())b.has(p)||($.remove(),g.delete(p))}};var _e={scan:e=>{if(e.hasAttribute("b-init"))return{type:"b-init",node:e,expr:e.getAttribute("b-init")}},execute:({binding:e,state:r})=>{e.initialized||(e.initialized=!0,x(e.expr,{state:r}))}};var Te={name:"b-enter",scan:e=>O("b-enter",e,(r,o)=>({modifiers:r.name.split(".").slice(1)})),execute:({binding:e,state:r})=>{if(e.observer||e.done)return;let o=new IntersectionObserver((s,t)=>{s[0].isIntersecting&&(x(e.expr,{state:r}),e.modifiers.includes("once")&&(t.disconnect(),e.observer=null,e.done=!0))});o.observe(e.node),e.observer=o},destroy:({binding:e})=>{e.observer&&(e.observer.disconnect(),e.observer=null)}};var Ae={scan:e=>O("b-leave",e),execute:({binding:e,state:r})=>{if(e.observer||e.done)return;let o=new IntersectionObserver((s,t)=>{let a=s[0];a.isIntersecting&&(e.activated=!0),!a.isIntersecting&&e.activated&&(x(e.expr,{state:r}),e.modifiers.includes("once")&&(t.disconnect(),e.observer=null,e.done=!0))});o.observe(e.node),e.observer=o},destroy:({binding:e})=>{e.observer&&(e.observer.disconnect(),e.observer=null)}};var G={"b-if":xe,"b-show":ve,"b-text":Ne,"b-html":Se,"b-sync":$e,"b-for":Le,"b-init":_e,"b-enter":Te,"b-leave":Ae};var Ue=(e=null)=>{let r=!1,o=new Set,s=!1,t=e,a=new Set,i=null,n=new Set,c=f=>{t=f},l=()=>{i&&clearTimeout(i),i=setTimeout(()=>{n.forEach(f=>{let g=f._studs;if(g&&typeof g.updated=="function")try{g.updated.call(g)}catch(b){console.error("[Lego] Error in updated hook:",b)}}),n.clear(),i=null},50)},d=()=>{a.size>0&&(a.forEach(f=>o.add(f)),a.clear(),!r&&o.size>0&&(r=!0,requestAnimationFrame(u)))},u=()=>{s=!0;let f=Array.from(o);o.clear(),r=!1,f.forEach(g=>{g.isConnected&&t&&t(g)}),f.forEach(g=>n.add(g)),l(),s=!1,d()};return{add:f=>{if(f){if(s){a.add(f);return}o.add(f),!r&&(r=!0,requestAnimationFrame(u))}},setHandler:c}},re=Ue();var L=new Map,oe=new Map,J=new WeakMap,V=new Map,je=e=>{let r=Array.from(V.entries()).filter(([t])=>t.startsWith("lego:")).sort((t,a)=>t[1].timestamp-a[1].timestamp),o=0,s=[];for(let[t,a]of r){if(o>=e)break;try{localStorage.removeItem(t),o+=a.size,s.push(t),V.delete(t)}catch(i){console.error(`[Lego] Failed to evict ${t}:`,i)}}return s},ke=(e,r,o)=>{m.debug&&console.log("[Lego Trace] scheduleSave",e,r,o),oe.has(e)&&clearTimeout(oe.get(e));let s=()=>{try{let t=JSON.stringify(r),a=new Blob([t]).size;localStorage.setItem(e,t);let i=e.startsWith("lego:")?e:`lego:${e}`;V.set(i,{timestamp:Date.now(),size:a}),oe.delete(e)}catch(t){if(t.name==="QuotaExceededError"){console.warn(`[Lego] Storage quota exceeded for key: ${e}`);try{let a=JSON.stringify(r),i=new Blob([a]).size,n=je(i*2);if(n.length>0){m.debug&&console.log(`[Lego] Evicted ${n.length} old keys, retrying save`),localStorage.setItem(e,a);let c=e.startsWith("lego:")?e:`lego:${e}`;V.set(c,{timestamp:Date.now(),size:i})}else m.onError(new Error("Storage quota exceeded and no keys available for eviction"),"quota",e)}catch{m.onError(new Error(`Critical: Could not save ${e} even after eviction`),"quota-critical",e)}}else console.error(`[Lego] Storage Error (${e}):`,t)}};o>0?oe.set(e,setTimeout(s,o)):s()},fe=e=>({__type:"lego-db",key:e,_default:void 0,_debounce:0,default(o){return this._default=o,this},debounce(o){return this._debounce=o,this},set(o,s=0){return ke(e,o,s),L.has(e)&&L.get(e).forEach(({target:t,prop:a,el:i,batcher:n})=>{t[a]=o,i&&n&&n.add(i)}),o},get(){try{let o=localStorage.getItem(e);return o!==null?JSON.parse(o):null}catch(o){return console.warn(`[Lego] Failed to get localStorage value for key "${e}":`,o),null}},delete(){try{return localStorage.removeItem(e),V.delete(e.startsWith("lego:")?e:`lego:${e}`),L.has(e)&&L.get(e).forEach(({target:o,prop:s,el:t,batcher:a})=>{o[s]=null,t&&a&&a.add(t)}),!0}catch(o){return console.error(`[Lego] Failed to delete localStorage key "${e}":`,o),!1}}}),Ce=e=>e&&e.__type==="lego-db",Me=(e,r,o,s,t)=>{m.debug&&console.log("[Lego Trace] Reactive DB Init:",r,o);let a=o._default;try{let i=localStorage.getItem(o.key);i!==null&&(a=JSON.parse(i))}catch(i){console.warn(`[Lego] Failed to parse localStorage value for key "${o.key}":`,i)}e[r]=a,J.has(e)||J.set(e,{}),J.get(e)[r]={key:o.key,debounce:o._debounce},m.debug&&console.log("[Lego Trace] DB Metadata Set:",r,J.get(e)[r]),L.has(o.key)||L.set(o.key,new Set),L.get(o.key).add({target:e,prop:r,el:s,batcher:t})},Oe=(e,r,o)=>{let s=J.get(e);if(s&&s[r]){let t=s[r].key;ke(t,o,s[r].debounce),L.has(t)&&L.get(t).forEach(({target:a,prop:i,el:n,batcher:c})=>{a!==e&&(a[i]=o,n&&c&&c.add(n))})}},Be=()=>{typeof window<"u"&&window.addEventListener("storage",e=>{if(!(!e.key||!L.has(e.key)))try{let r=JSON.parse(e.newValue);L.get(e.key).forEach(({target:o,prop:s,el:t,batcher:a})=>{o[s]=r,t&&a&&a.add(t)})}catch(r){console.warn("[Lego] Cross-tab sync error:",r)}})};var z=(e,r,o=re)=>{if(e===null||typeof e!="object"||ae(e))return e;if(I.has(e))return I.get(e);for(let a in e){let i=Object.getOwnPropertyDescriptor(e,a);if(i&&i.get)continue;let n=e[a];Ce(n)&&Me(e,a,n,r,o)}let s={get:(a,i)=>{let n=Reflect.get(a,i);return n!==null&&typeof n=="object"&&!ae(n)?z(n,r,o):n},set:(a,i,n,c)=>{let l=a[i];m.debug&&l!==n&&console.log("[Lego Trace] Reactive SET:",i,"Old:",l,"New:",n,"Target:",a);let d=Reflect.set(a,i,n,c);return c===I.get(a)&&l!==n&&(o.add(r),Oe(a,i,n)),d},deleteProperty:(a,i)=>{let n=Reflect.deleteProperty(a,i);return o.add(r),n}},t=new Proxy(e,s);return I.set(e,t),t};var Ke=e=>{let r=e;for(;r;){let o=r.getAttribute&&r.getAttribute("b-error");if(o)return{errorTag:o,targetElement:r};let s=r.tagName?r.tagName.toLowerCase():"",t=y[s];if(t){let a=t.getAttribute("b-error");if(a)return{errorTag:a,targetElement:r}}r=r.parentElement||r.getRootNode&&r.getRootNode().host}return null},de=null,ue=null,De=(e,r)=>{de=e,ue=r},Ge=(e,r,o)=>{if(!de||!ue){console.error("[Lego] Error boundary dependencies not loaded",o);return}try{r.shadowRoot?r.shadowRoot.innerHTML="":r.innerHTML="";let s=document.createElement(e);(r.shadowRoot||r).appendChild(s),de(s),s._studs&&(s._studs.$error={message:o.message,stack:o.stack,component:r.tagName.toLowerCase()},ue(s))}catch(s){console.error("[Lego] Error boundary failed to render:",s),console.error("[Lego] Original error:",o)}},W=(e,r,o)=>{let s=Ke(r);s?Ge(s.errorTag,s.targetElement,e):m.onError(e,o,r)};var se=(e,r,o=null)=>{let s=r._studs,t=n=>{let c=M(n);if(!c.bound){if(n.hasAttributes()){let l=n.attributes;for(let d=0;d<l.length;d++){let u=l[d];if(u.name.startsWith("@")){let f=u.name.slice(1).split("."),g=f[0],b=f.slice(1);n.addEventListener(g,h=>{if(b.includes("prevent")&&h.preventDefault(),b.includes("stop")&&h.stopPropagation(),!(b.includes("self")&&h.target!==h.currentTarget)){if(typeof KeyboardEvent<"u"&&h instanceof KeyboardEvent){if(b.includes("ctrl")&&!h.ctrlKey||b.includes("alt")&&!h.altKey||b.includes("shift")&&!h.shiftKey||b.includes("meta")&&!h.metaKey)return;let w=b.filter(p=>!["prevent","stop","self","ctrl","alt","shift","meta","capture","once","passive"].includes(p));if(w.length>0){let p=h.key.toLowerCase();if(!w.some(E=>E==="enter"?p==="enter":E==="esc"||E==="escape"?p==="escape":E==="space"?p===" ":E==="tab"?p==="tab":E==="delete"?p==="delete":E==="backspace"?p==="backspace":E==="up"?p==="arrowup":E==="down"?p==="arrowdown":E==="left"?p==="arrowleft":E==="right"?p==="arrowright":E==="alpha"?/^[a-z]$/.test(p):E==="numbers"?/^[0-9]$/.test(p):E===p))return}}try{let w=s;if(o){let $=x(o.listName,{state:s,global:N,self:r})[o.index];w=Object.assign(Object.create(s),{[o.name]:$})}x(u.value,{state:w,global:N,self:n,event:h,$event:h},!0)}catch(w){W(w,r,"event-handler")}}})}}if(n.hasAttribute("b-sync")){let d=n.getAttribute("b-sync"),u=()=>{try{let f,g;if(o&&d.startsWith(`${o.name}.`)){let w=x(o.listName,{state:s,global:N,self:r})[o.index];if(!w)return;let p=d.split(".").slice(1);g=p.pop(),f=p.reduce(($,E)=>$[E],w)}else{let h=d.split(".");g=h.pop(),f=h.reduce((w,p)=>w[p],s)}let b=n.type==="checkbox"?n.checked:n.value;f&&f[g]!==b&&(f[g]=b)}catch(f){m.onError(f,"sync-update",n)}};n.addEventListener("input",u),n.addEventListener("change",u)}if(n.hasAttribute("b-var")){let d=n.getAttribute("b-var");s.$vars&&(s.$vars[d]=n)}}c.bound=!0}};e instanceof Element&&t(e);let a=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT),i;for(;i=a.nextNode();)t(i)},Je=e=>{let r=[],o=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT),s,t=[];for(;s=o.nextNode();){let a=s;if((c=>{let l=c.parentNode;for(;l&&l!==e;){if(l.hasAttribute&&l.hasAttribute("b-for"))return!0;l=l.parentNode}return!1})(s))continue;let n=c=>{if(/\bglobal\b/.test(c)){let l=e.host||e;q.add(l)}};if(s.nodeType===Node.ELEMENT_NODE){for(let[l,d]of Object.entries(G))if(d.scan){let u=d.scan(s,{checkGlobal:n,getPrivateData:M,pendingOperations:t,current:a});u&&r.push(u)}let[c]=U();[...s.attributes].forEach(l=>{l.value.includes(c)&&(n(l.value),r.push({type:"attr",node:s,attrName:l.name,template:l.value}))})}else if(s.nodeType===Node.TEXT_NODE){let[c]=U();s.textContent.includes(c)&&(n(s.textContent),r.push({type:"text",node:s,template:s.textContent}))}}return t.forEach(a=>a()),r},Ve=(e,r)=>{let o=a=>{if(a.nodeType===Node.TEXT_NODE){a._tpl===void 0&&(a._tpl=a.textContent);let i=a._tpl.replace(j(),(n,c)=>x(c.trim(),{state:r,global:N,self:a})??"");a.textContent!==i&&(a.textContent=i)}else if(a.nodeType===Node.ELEMENT_NODE){let[i]=U();[...a.attributes].forEach(n=>{if(n._tpl===void 0&&(n._tpl=n.value),n._tpl.includes(i)){let c=n._tpl.replace(j(),(l,d)=>x(d.trim(),{state:r,global:N,self:a})??"");n.value!==c&&(n.value=c,n.name==="class"&&(a.className=c))}})}};o(e);let s=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT),t;for(;t=s.nextNode();)o(t)},H=e=>{m.debug&&console.log("[Lego Trace] render() called for:",e.tagName);let r=e._studs;if(!r)return;let o=M(e);if(!o.rendering){o.rendering=!0,m.metrics&&m.metrics.onRenderStart&&m.metrics.onRenderStart(e);try{let s=e.shadowRoot||e;o.bindings||(o.bindings=Je(s)),o.bindings.forEach(t=>{let a=G[t.type];if(a){a.execute({binding:t,state:r,global:N,helpers:{bind:se,updateNodeBindings:Ve,contextEl:e}});return}if(t.type==="text"){let i=t.template.replace(j(),(n,c)=>x(c.trim(),{state:r,global:N,self:t.node})??"");t.node.textContent!==i&&(t.node.textContent=i)}if(t.type==="attr"){let i=t.template.replace(j(),(n,c)=>x(c.trim(),{state:r,global:N,self:t.node})??"");t.node.getAttribute(t.attrName)!==i&&(t.node.setAttribute(t.attrName,i),t.attrName==="class"&&(t.node.className=i))}}),r===N&&q.forEach(t=>H(t))}catch(s){W(s,e,"render")}finally{m.metrics&&m.metrics.onRenderEnd&&m.metrics.onRenderEnd(e),o.rendering=!1}}};var Re=new Map,Pe={},Fe=e=>{Pe=e},ze=async()=>{let e=Object.entries(Pe).map(async([r,o])=>{let s=await Promise.all(o.map(async t=>{try{if(t instanceof CSSStyleSheet)return t;let a=await fetch(t);if(!a.ok)throw new Error(`Status ${a.status}`);let i=await a.text(),n=new CSSStyleSheet;return await n.replace(i),n}catch(a){return console.error(`[Lego] Failed to load stylesheet: ${t}`,a),null}}));Re.set(r,s.filter(t=>t!==null))});await Promise.all(e),m.debug&&console.log("[Lego Debug] Re-applying stylesheets to",_.size,"blocks"),_.forEach(r=>{pe(r)})},pe=e=>{if(!e.shadowRoot&&!e._legoShadow)return;let r=e.shadowRoot||e._legoShadow,o=e.tagName.toLowerCase(),s=y[o],t=(s&&s.getAttribute("b-stylesheets")||"").split(/\s+/).filter(Boolean),a=(e.getAttribute("b-stylesheets")||"").split(/\s+/).filter(Boolean),i=[],n=e.parentElement||(e.getRootNode?e.getRootNode().host:null);for(;n;){let u=(n.getAttribute("b-cascade")||"").split(/\s+/).filter(Boolean);u.length&&i.push(...u);let f=n.tagName?n.tagName.toLowerCase():"";if(y[f]){let g=(y[f].getAttribute("b-cascade")||"").split(/\s+/).filter(Boolean);g.length&&i.push(...g)}n=n.parentElement||n.getRootNode&&n.getRootNode().host}let c=[...new Set([...t,...a])],l=[...new Set(i)],d=[...new Set([...l,...c])];if(d.length>0){let u=d.flatMap(f=>Re.get(f)||[]);u.length>0&&m.debug&&console.log("[Lego Debug] Applying styles to",e.tagName,"- Names:",d,"- Sheets:",u.length),u.length>0&&(r.adoptedStyleSheets=[...u])}e._studsMeta={stylesheets:{explicit:c,inherited:l,applied:d}}};var S=e=>{if(m.debug&&console.log("[Lego Trace] snap() called for:",e.tagName),!e||e.nodeType!==Node.ELEMENT_NODE)return;let r=M(e),o=e.tagName.toLowerCase(),s=y[o];if(s&&!r.snapped){r.snapped=!0;let a=s.content.cloneNode(!0),i=e.shadowRoot;i?i.innerHTML="":i=e.attachShadow({mode:"open"}),pe(e);let n=F(e,"*")||F(e.getRootNode().host,"*"),c=n&&n.state?n.state:{},l=R.get(o)||{},d=le(s.getAttribute("b-logic")||"{}"),u=le(e.getAttribute("b-logic")||"{}",c),f={$vars:{},$element:e,get $parent(){return F(e,"*")},$emit:(h,w)=>{e.dispatchEvent(new CustomEvent(h,{detail:w,bubbles:!0,composed:!0}))},get $route(){return N.$route},get $go(){return N.$go}};P(l,f),P(d,f),P(u,f),e._studs=z(f,e),Object.defineProperty(e,"state",{get(){return this._studs},set(h){Object.assign(this._studs,h)},configurable:!0,enumerable:!1}),i.appendChild(a);let g=i.querySelector("style");g&&(g.textContent=g.textContent.replace(/\bself\b/g,":host")),se(i,e),_.add(e),H(e),e.setAttribute("b-id",o);let b=i.querySelectorAll("*");if(m.debug&&console.log("[Lego Debug] Nested scan in",e.tagName,"- Found elements:",b.length),b.forEach(h=>{let w=h.tagName.toLowerCase(),p=!!y[w];p&&m.debug&&console.log("[Lego Debug] Checking",w,"- Registered:",p),p&&S(h)}),e._legoShadow||(e._legoShadow=i),typeof e._studs.mounted=="function")try{e._studs.mounted.call(e._studs)}catch(h){W(h,e,"mounted")}}let t=e.parentElement;for(;t&&!t._studs;)t=t.parentElement;t&&t._studs&&se(e,t),[...e.children].forEach(S)},A=e=>{if(e._studs&&typeof e._studs.unmounted=="function")try{e._studs.unmounted.call(e._studs)}catch(o){W(o,e,"unmounted")}e.shadowRoot&&[...e.shadowRoot.children].forEach(A),_.delete(e),q.delete(e),e._studs&&(e._studs.$element=null,e._studs.$vars&&(Object.keys(e._studs.$vars).forEach(o=>{e._studs.$vars[o]=null}),e._studs.$vars=null),e._studs.$emit&&(e._studs.$emit=null),delete e._studs.$parent,delete e._studs.$route,delete e._studs.$go,e._studs=null),e.hasOwnProperty("state")&&delete e.state;let r=D.get(e);if(r&&(r.bindings&&(r.bindings.forEach(o=>{let s=G[o.type];if(s&&s.destroy)try{s.destroy({binding:o})}catch(t){console.error(`[Lego] Error in directive cleanup (${o.type}):`,t)}o.node=null,o.anchor=null}),r.bindings=null),r.anchor=null,D.delete(e)),C.has(e)){let o=C.get(e);o.forEach((s,t)=>{s&&s._studs&&(s._studs=null)}),o.clear(),C.delete(e)}[...e.children].forEach(A)};function We(e,r="block.lego"){let o={template:"",script:"",style:"",stylesAttr:"",cascadeAttr:"",errorAttr:"",blockName:be(r)},s=e,t=/<(template|script|style)\b((?:\s+(?:[^>"']|"[^"]*"|'[^']*')*)*)>/i;for(;s;){let a=s.match(t);if(!a)break;let i=a[1].toLowerCase(),n=a[2],c=a[0],l=a.index,d=`</${i}>`,u=l+c.length,f=s.indexOf(d,u);if(f===-1){console.warn(`[Lego] Unclosed <${i}> tag in ${r}`);break}let g=s.slice(u,f);if(i==="template"){o.template=g.trim();let b=n.match(/b-stylesheets=["']([^"']+)["']/);b&&(o.stylesAttr=b[1]);let h=n.match(/b-cascade=["']([^"']+)["']/);h&&(o.cascadeAttr=h[1]);let w=n.match(/b-error=["']([^"']+)["']/);w&&(o.errorAttr=w[1])}else if(i==="script"){o.script=g.trim();let b=n.match(/lang=["']([^"']+)["']/);b&&(o.scriptLang=b[1])}else i==="style"&&(o.style=g.trim());s=s.slice(f+d.length)}return o}var He=(e,r,o="block.lego")=>{let s=We(r,o),{blockName:t,template:a,script:i,style:n,stylesAttr:c,cascadeAttr:l,errorAttr:d}=s,u={};if(i)try{let g=i.trim(),b=g.match(/export\s+default\s+({[\s\S]*})/),h=b?b[1]:g;u=new Function("Lego","$db",`return ${h}`)(e,e.globals.$db)}catch(g){m.onError(g,"script",t)}let f=a;n&&(f=`<style>${n}</style>`+f),y[t]=document.createElement("template"),y[t].innerHTML=f,c&&y[t].setAttribute("b-stylesheets",c),l&&y[t].setAttribute("b-cascade",l),d&&y[t].setAttribute("b-error",d),R.set(t,u),document.querySelectorAll(t).forEach(g=>!M(g).snapped&&S(g))};re.setHandler(H);De(S,H);Ee(S);var Xe={url:typeof window<"u"?window.location.pathname:"/",route:"",params:{},query:{},method:"GET",body:null},k=z({$route:Xe,$go:(e,...r)=>te(e,...r)(document.body),$db:fe},typeof document<"u"?document.body:null);he(k);var v={db:fe,snap:S,unsnap:A,defineLegoFile:(e,r)=>He(v,e,r),block:(e,r,o={},s="",t="",a="")=>{let i=document.createElement("template");i.setAttribute("b-id",e),i.setAttribute("b-stylesheets",s),t&&i.setAttribute("b-cascade",t),a&&i.setAttribute("b-error",a),i.innerHTML=r,y[e]=i,R.set(e,o);try{let n={};P(o,n),Q.set(e.toLowerCase(),z(n,document.body))}catch(n){m.onError(n,"define",e)}if(customElements.get(e))document.querySelectorAll(e).forEach(n=>{A(n),S(n)}),_.forEach(n=>{n._legoShadow&&n._legoShadow.querySelectorAll(e).forEach(c=>{A(c),S(c)})});else try{customElements.define(e,class extends HTMLElement{connectedCallback(){y[e]&&S(this)}disconnectedCallback(){A(this)}})}catch(n){console.warn(`[Lego] Failed to register web component ${e}:`,n)}},getActiveBlocksCount:()=>_.size,getLegos:()=>Object.keys(y),config:m,globals:k,route:(e,r,o=null)=>{let s=[],t=e==="*"?".*":e;t=t.replace(/:([^\/]+)/g,(a,i)=>(s.push(i),"([^/]+)")),K.push({path:e,regex:new RegExp(`^${t}$`),tagName:r,paramNames:s,middleware:o})},debug:{stylesheets:e=>!e||!e._studsMeta?null:e._studsMeta.stylesheets},init:async(e=document.body,r={})=>{if((!e||typeof e.nodeType!="number")&&(e=document.body),Fe(r.styles||{}),m.loader=r.loader,m.debug=r.debug===!0,await ze(),K.length>0){let t=window.location.pathname,a=window.location.search,i=ce(t);if(i){let{match:n,params:c}=i,l=Object.fromEntries(new URLSearchParams(a));k.$route.url=t+a,k.$route.route=n.path,k.$route.params=c,k.$route.query=l,k.$route.method="GET",k.$route.body=null}}document.querySelectorAll("template[b-id]").forEach(t=>{let a=t.getAttribute("b-id");y[a]=t,customElements.get(a)||customElements.define(a,class extends HTMLElement{connectedCallback(){y[a]&&S(this)}disconnectedCallback(){A(this)}})});let o=t=>{if(!t)return{};let a={};return Object.entries(t).forEach(([i,n])=>{if(typeof n=="function")a[i]=n();else if(typeof n=="string"&&n.startsWith("$db.")){let c=n.slice(4);a[i]=v.db(c).get()}else if(typeof n=="string"&&n.startsWith("$globals.")){let c=n.slice(9);a[i]=N[c]}else a[i]=n}),a},s=(t,a,i={})=>{customElements.get(t)||customElements.define(t,class extends HTMLElement{async connectedCallback(){if(y[t]){S(this);return}if(v._fetching||(v._fetching=new Set),!v._fetching.has(t)){v._fetching.add(t);try{let n=a.startsWith("POST:")?"POST":"GET",c=a.replace(/^POST:/,""),l=o(i.headers),d={method:n,headers:l,credentials:i.credentials},f=await(await fetch(c,d)).text();v.defineLegoFile(f,`${t}.lego`),y[t]&&S(this)}catch(n){console.error(`[Lego] Failed to load manifest block ${t}:`,n)}finally{v._fetching.delete(t)}}}disconnectedCallback(){A(this)}})};if(r.manifest){let t=r.manifest;if(t.url&&typeof t.url=="string")try{let i=o(t.headers);t=await(await fetch(t.url,{headers:i,credentials:t.credentials})).json()}catch(i){console.error("[Lego] Failed to load manifest:",i),t=[]}else if(typeof t=="string")try{t=await(await fetch(t)).json()}catch(i){console.error("[Lego] Failed to load manifest:",i),t=[]}let a=Array.isArray(t)?t:[t];for(let i of a){let n=i.base||"",c=i.suffix===!0?".lego":i.suffix||"",l={headers:i.headers||{},credentials:i.credentials};i.legos&&i.legos.forEach(d=>{s(d,`${n}${d}${c}`,l)}),i.map&&Object.entries(i.map).forEach(([d,u])=>{let f=u.match(/^https?:|^\//)?u:`${n}${u}`;s(d,f,l)})}}if(m.loader){let t=n=>{if(n.nodeType!==Node.ELEMENT_NODE)return;let c=n.tagName.toLowerCase();if(c.includes("-")&&!y[c]&&!_.has(n)){if(v._fetching&&v._fetching.has(c))return;v._fetching||(v._fetching=new Set),v._fetching.add(c);let l=m.loader(c);if(l){let d=typeof l=="string"?fetch(l).then(u=>u.text()):l;Promise.resolve(d).then(u=>{v.defineLegoFile(u,`${c}.lego`),v._fetching.delete(c)}).catch(u=>{console.error(`[Lego] Failed to load ${c}:`,u),v._fetching.delete(c)})}}if(n.children.length>0){let l=n.firstElementChild;for(;l;)t(l),l=l.nextElementSibling}};new MutationObserver(n=>n.forEach(c=>{c.addedNodes.forEach(t)})).observe(e,{childList:!0,subtree:!0});let i=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT);for(;i.nextNode();)t(i.currentNode)}if(r.studio){if(!y["lego-studio"]){let t=document.createElement("script");t.src="https://unpkg.com/@legodom/studio@0.0.2/dist/lego-studio.js",t.onerror=()=>console.warn("[Lego] Failed to load Studio from CDN"),document.head.appendChild(t)}v.route("/_/studio","lego-studio"),v.route("/_/studio/:block","lego-studio")}K.length>0&&(window.addEventListener("popstate",t=>{let a=t.state?.legoTargets||null;ee(a)}),document.addEventListener("submit",t=>{t.preventDefault()}),document.addEventListener("click",t=>{let i=t.composedPath().find(n=>n.tagName==="A"&&(n.hasAttribute("b-target")||n.hasAttribute("b-link")));if(i){t.preventDefault();let n=i.getAttribute("href"),c=i.getAttribute("b-target"),l=c?c.split(/\s+/).filter(Boolean):[],d=i.getAttribute("b-link")!=="false";k.$go(n,...l).get(d)}}),ee()),Be()}};typeof window<"u"?window.Lego=v:typeof global<"u"&&(global.Lego=v);var ho=v;export{v as Lego,ho as default};
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
(()=>{var w={},I=new WeakMap,D=new WeakMap,A=new WeakMap,X=new WeakMap,L=new Set,q=new Set,B=new Map,Q=new Map;var $=e=>(D.has(e)||D.set(e,{snapped:!1,bindings:null,bound:!1,rendering:!1,anchor:null}),D.get(e));var x={},he=e=>{x=e};var f={onError:(e,t,r)=>{console.error(`[Lego Error] [${t}]`,e,r)},metrics:{},debug:!1,syntax:"brackets"},R=(e,t)=>{!e||!t||Object.getOwnPropertyNames(e).forEach(r=>{let o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o)})},K=()=>f.syntax==="brackets"?["[[","]]"]:["{{","}}"],ne=new Map,U=()=>{let[e,t]=K(),r=e+t;if(ne.has(r))return ne.get(r);let o=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),s=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),n=new RegExp(`${o}(.*?)${s}`,"g");return ne.set(r,n),n},be=e=>{let r=e.split("/").pop().replace(/\.lego$/,"").replace(/_/g,"-").replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();if(!r.includes("-"))throw new Error(`[Lego] Invalid block definition: "${e}". Block names must contain a hyphen (e.g. user-card.lego or UserCard.lego).`);return r},M=(e,t)=>{if(!e)return"";let r=e.trim().split("."),o=t;for(let s of r){if(o==null)return"";o=o[s]}return o??""},ae=e=>typeof Node<"u"&&e instanceof Node,k=(e,t,r)=>{if(t.nodeType!==Node.ELEMENT_NODE)return;let o=[...t.attributes].find(n=>n.name===e||n.name.startsWith(`${e}.`));if(!o)return;let s={};return r&&(s=r(o,t)),{type:e,node:t,expr:o.value,modifiers:o.name.split(".").slice(1),...s}};var Z=class{constructor(t=1e3){this.limit=t,this.cache=new Map}get(t){if(!this.cache.has(t))return;let r=this.cache.get(t);return this.cache.delete(t),this.cache.set(t,r),r}set(t,r){if(this.cache.has(t))this.cache.delete(t);else if(this.cache.size>=this.limit){let o=this.cache.keys().next().value;this.cache.delete(o)}this.cache.set(t,r)}get size(){return this.cache.size}clear(){this.cache.clear()}};var P=(e,t)=>{if(!e)return;let r=e.parentElement||(e.getRootNode?e.getRootNode().host:null);for(;r;){let o=r.tagName?r.tagName.toLowerCase():"";if(o&&(t==="*"&&w[o]||o===t.toLowerCase()))return r;r=r.parentElement||r.getRootNode&&r.getRootNode().host}},ye=(e,t)=>{if(typeof e=="function"){let o=Array.from(document.querySelectorAll("*")).filter(s=>s.tagName.includes("-"));return[].concat(e(o))}if(e.startsWith("#")){let o=document.getElementById(e.slice(1));return o?[o]:[]}let r=t?.querySelectorAll(e)||[];return r.length>0?[...r]:[...document.querySelectorAll(e)]},Y=(e,t)=>{if(e.type==="checkbox")e.checked!==!!t&&(e.checked=!!t);else{let r=t==null?"":String(t);e.value!==r&&(e.value=r)}};var ie=null,Ee=e=>{ie=e},j=[],ce=e=>{let t=j.find(s=>s.regex.test(e));if(!t)return null;let r=e.match(t.regex).slice(1),o=t.paramNames?Object.fromEntries(t.paramNames.map((s,n)=>[s,r[n]])):{};return{match:t,params:o}},ee=async(e=null,t=null)=>{if(typeof window>"u")return;let r=window.location.pathname,o=ce(r);if(f.debug&&console.log("[Lego Trace] Matching route",r,o?.match),!o)return;let{match:s,params:n}=o;if(s.middleware&&!await s.middleware({path:r,params:n}))return;let i=e&&e.length?e:["lego-router"],a=t||document;s.tagName&&i.flatMap(l=>ye(l,a)).forEach(l=>{l&&(l.innerHTML=`<${s.tagName}></${s.tagName}>`,ie&&ie(l.firstElementChild))})},te=(e,...t)=>r=>{let o=async(s,n=null,i=!0,a={})=>{if(i&&typeof history<"u"){let l={legoTargets:t.filter(m=>typeof m=="string"),method:s,body:n};history.pushState(l,"",e)}await ee(t.length?t:null,r)};return{get:(s=!0,n={})=>o("GET",null,s,n),post:(s,n=!0,i={})=>o("POST",s,n,i),put:(s,n=!0,i={})=>o("PUT",s,n,i),patch:(s,n=!0,i={})=>o("PATCH",s,n,i),delete:(s=!0,n={})=>o("DELETE",null,s,n)}};var we=new Z(1e3),N=(e,t,r=!0)=>{if(/\b(function|eval|import|class|module|deploy|constructor|__proto__)\b/.test(e)){console.warn(`[Lego] Security Warning: Blocked dangerous expression "${e}"`);return}try{let o=t.state||{},s=we.get(e);s||(s=new Function("global","self","event","helpers",`
|
|
2
|
+
with(this) {
|
|
3
|
+
with(helpers) {
|
|
4
|
+
return ${e}
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
`),we.set(e,s));let n={$ancestors:a=>P(t.self,a),$registry:a=>Q.get(a.toLowerCase()),$element:t.self,$route:x.$route,$go:(a,...c)=>te(a,...c)(t.self),$emit:(a,c)=>{t.self.dispatchEvent(new CustomEvent(a,{detail:c,bubbles:!0,composed:!0}))}},i=s.call(o,t.global,t.self,t.event,n);return typeof i=="function"?i.call(o,t.event):i}catch(o){if(r)throw o;f.onError(o,"render-error",t.self);return}},le=(e,t={})=>{if(!e||e.trim()==="{}")return{};try{return new Function("scope","global",`with(global) { with(scope) { return (${e}); } }`)(t,x)}catch(r){return console.error(`[Lego] Error parsing b-data/b-logic: "${e.length>80?e.slice(0,80)+"...":e}"`,r),{}}};var Ne={name:"b-if",scan:(e,{checkGlobal:t,getPrivateData:r})=>k("b-if",e,(o,s)=>{t(o.value);let n=document.createComment(`b-if: ${o.value}`),i=r(s);return i.anchor=n,{anchor:n}}),execute({binding:e,state:t,global:r}){let{node:o,anchor:s,expr:n}=e,i=!!N(n,{state:t,global:r,self:o}),a=!!o.parentNode;i&&!a?s.parentNode&&s.parentNode.replaceChild(o,s):!i&&a&&o.parentNode.replaceChild(s,o)}};var xe={name:"b-show",scan(e,{checkGlobal:t}){if(e.nodeType===Node.ELEMENT_NODE&&e.hasAttribute("b-show")){let r=e.getAttribute("b-show");return t(r),{type:"b-show",node:e,expr:r}}},execute({binding:e,state:t,global:r}){let{node:o,expr:s}=e;o.style.display=N(s,{state:t,global:r,self:o})?"":"none"}};var ve={name:"b-text",scan(e){if(e.nodeType===Node.ELEMENT_NODE&&e.hasAttribute("b-text"))return{type:"b-text",node:e,path:e.getAttribute("b-text")}},execute({binding:e,state:t}){let{node:r,path:o}=e;r.textContent=M(o,t)}};var Se={name:"b-html",scan:e=>k("b-html",e),execute:({binding:e,state:t,global:r})=>{let{node:o,expr:s}=e;o.innerHTML=N(s,{state:t,global:r,self:o})||""}};var Le={name:"b-sync",scan(e){if(e.nodeType===Node.ELEMENT_NODE&&e.hasAttribute("b-sync"))return{type:"b-sync",node:e}},execute({binding:e,state:t}){let{node:r}=e;Y(r,M(r.getAttribute("b-sync"),t))}};var $e={name:"b-for",scan(e,{checkGlobal:t,pendingOperations:r}){if(e.nodeType!==Node.ELEMENT_NODE)return;let o=e.getAttribute("b-for")?.match(/^\s*(\w+)\s+in\s+([\s\S]+?)\s*$/);if(o){t(o[2]);let s=document.createComment(`b-for: ${o[1]} in ${o[2].trim()}`);return r.push(()=>{e.parentNode&&(e.parentNode.insertBefore(s,e),e.remove())}),{type:"b-for",anchor:s,itemName:o[1],listName:o[2].trim(),keyPath:e.getAttribute("b-key")||null,template:e.cloneNode(!0)}}},execute({binding:e,state:t,global:r,helpers:o}){let{bind:s,updateNodeBindings:n}=o;if(!s||!n){console.error("[Lego] b-for directive missing required helpers: bind, updateNodeBindings");return}let{anchor:i,listName:a,itemName:c,keyPath:l,template:m}=e,{contextEl:g}=o,d=N(a,{state:t,global:r,self:g})||[];A.has(i)||A.set(i,new Map);let p=A.get(i),y=new Set,h=[];d.forEach((u,v)=>{let b;l?(b=M(l,u),b===void 0&&console.warn(`[Lego] b-key="${l}" resolved to undefined for item at index ${v}. Check for typos.`)):u&&typeof u=="object"?(b=u.id||u._id||u.uuid||u.key,b===void 0&&(X.has(u)||X.set(u,Math.random()),b=X.get(u))):b=`${v}-${u}`,y.add(b);let T=p.get(b);T||(T=m.cloneNode(!0),T.removeAttribute("b-for"),T.removeAttribute("b-key"),p.set(b,T),s(T,g,{name:c,listName:a,index:v}));let We=Object.assign(Object.create(t),{[c]:u,$index:v});n(T,We),T.querySelectorAll("[b-sync]").forEach(me=>{let ge=me.getAttribute("b-sync");if(ge.startsWith(`${c}.`)){let Ie=N(a,{state:t,global:r,self:g});Y(me,M(ge.split(".").slice(1).join("."),Ie[v]))}}),h.push(T)});let E=i.nextSibling;h.forEach(u=>{u!==E?i.parentNode.insertBefore(u,E):E=E.nextSibling});for(let[u,v]of p.entries())y.has(u)||(v.remove(),p.delete(u))}};var _e={scan:e=>{if(e.hasAttribute("b-init"))return{type:"b-init",node:e,expr:e.getAttribute("b-init")}},execute:({binding:e,state:t})=>{e.initialized||(e.initialized=!0,N(e.expr,{state:t}))}};var Te={name:"b-enter",scan:e=>k("b-enter",e,(t,r)=>({modifiers:t.name.split(".").slice(1)})),execute:({binding:e,state:t})=>{if(e.observer||e.done)return;let r=new IntersectionObserver((o,s)=>{o[0].isIntersecting&&(N(e.expr,{state:t}),e.modifiers.includes("once")&&(s.disconnect(),e.observer=null,e.done=!0))});r.observe(e.node),e.observer=r},destroy:({binding:e})=>{e.observer&&(e.observer.disconnect(),e.observer=null)}};var Ae={scan:e=>k("b-leave",e),execute:({binding:e,state:t})=>{if(e.observer||e.done)return;let r=new IntersectionObserver((o,s)=>{let n=o[0];n.isIntersecting&&(e.activated=!0),!n.isIntersecting&&e.activated&&(N(e.expr,{state:t}),e.modifiers.includes("once")&&(s.disconnect(),e.observer=null,e.done=!0))});r.observe(e.node),e.observer=r},destroy:({binding:e})=>{e.observer&&(e.observer.disconnect(),e.observer=null)}};var G={"b-if":Ne,"b-show":xe,"b-text":ve,"b-html":Se,"b-sync":Le,"b-for":$e,"b-init":_e,"b-enter":Te,"b-leave":Ae};var qe=(e=null)=>{let t=!1,r=new Set,o=!1,s=e,n=new Set,i=null,a=new Set,c=d=>{s=d},l=()=>{i&&clearTimeout(i),i=setTimeout(()=>{a.forEach(d=>{let p=d._studs;if(p&&typeof p.updated=="function")try{p.updated.call(p)}catch(y){console.error("[Lego] Error in updated hook:",y)}}),a.clear(),i=null},50)},m=()=>{n.size>0&&(n.forEach(d=>r.add(d)),n.clear(),!t&&r.size>0&&(t=!0,requestAnimationFrame(g)))},g=()=>{o=!0;let d=Array.from(r);r.clear(),t=!1,d.forEach(p=>{p.isConnected&&s&&s(p)}),d.forEach(p=>a.add(p)),l(),o=!1,m()};return{add:d=>{if(d){if(o){n.add(d);return}r.add(d),!t&&(t=!0,requestAnimationFrame(g))}},setHandler:c}},re=qe();var V=new Map,oe=new Map,J=new WeakMap,se=new Map,Ke=e=>{let t=Array.from(se.entries()).filter(([s])=>s.startsWith("lego:")).sort((s,n)=>s[1].timestamp-n[1].timestamp),r=0,o=[];for(let[s,n]of t){if(r>=e)break;try{localStorage.removeItem(s),r+=n.size,o.push(s),se.delete(s)}catch(i){console.error(`[Lego] Failed to evict ${s}:`,i)}}return o},Ue=(e,t,r)=>{f.debug&&console.log("[Lego Trace] scheduleSave",e,t,r),oe.has(e)&&clearTimeout(oe.get(e));let o=()=>{try{let s=JSON.stringify(t),n=new Blob([s]).size;localStorage.setItem(e,s);let i=e.startsWith("lego:")?e:`lego:${e}`;se.set(i,{timestamp:Date.now(),size:n}),oe.delete(e)}catch(s){if(s.name==="QuotaExceededError"){console.warn(`[Lego] Storage quota exceeded for key: ${e}`);try{let n=JSON.stringify(t),i=new Blob([n]).size,a=Ke(i*2);if(a.length>0){f.debug&&console.log(`[Lego] Evicted ${a.length} old keys, retrying save`),localStorage.setItem(e,n);let c=e.startsWith("lego:")?e:`lego:${e}`;se.set(c,{timestamp:Date.now(),size:i})}else f.onError(new Error("Storage quota exceeded and no keys available for eviction"),"quota",e)}catch{f.onError(new Error(`Critical: Could not save ${e} even after eviction`),"quota-critical",e)}}else console.error(`[Lego] Storage Error (${e}):`,s)}};r>0?oe.set(e,setTimeout(o,r)):o()},ue=e=>({__type:"lego-db",key:e,_default:void 0,_debounce:0,default(t){return this._default=t,this},debounce(t){return this._debounce=t,this}}),ke=e=>e&&e.__type==="lego-db",Me=(e,t,r,o,s)=>{f.debug&&console.log("[Lego Trace] Reactive DB Init:",t,r);let n=r._default;try{let i=localStorage.getItem(r.key);i!==null&&(n=JSON.parse(i))}catch(i){console.warn(`[Lego] Failed to parse localStorage value for key "${r.key}":`,i)}e[t]=n,J.has(e)||J.set(e,{}),J.get(e)[t]={key:r.key,debounce:r._debounce},f.debug&&console.log("[Lego Trace] DB Metadata Set:",t,J.get(e)[t]),V.has(r.key)||V.set(r.key,new Set),V.get(r.key).add({target:e,prop:t,el:o,batcher:s})},Ce=(e,t,r)=>{let o=J.get(e);o&&o[t]&&Ue(o[t].key,r,o[t].debounce)},Oe=()=>{typeof window<"u"&&window.addEventListener("storage",e=>{if(!(!e.key||!V.has(e.key)))try{let t=JSON.parse(e.newValue);V.get(e.key).forEach(({target:r,prop:o,el:s,batcher:n})=>{r[o]=t,s&&n&&n.add(s)})}catch(t){console.warn("[Lego] Cross-tab sync error:",t)}})};var F=(e,t,r=re)=>{if(e===null||typeof e!="object"||ae(e))return e;if(I.has(e))return I.get(e);for(let n in e){let i=Object.getOwnPropertyDescriptor(e,n);if(i&&i.get)continue;let a=e[n];ke(a)&&Me(e,n,a,t,r)}let o={get:(n,i)=>{let a=Reflect.get(n,i);return a!==null&&typeof a=="object"&&!ae(a)?F(a,t,r):a},set:(n,i,a,c)=>{let l=n[i];f.debug&&l!==a&&console.log("[Lego Trace] Reactive SET:",i,"Old:",l,"New:",a,"Target:",n);let m=Reflect.set(n,i,a,c);return c===I.get(n)&&l!==a&&(r.add(t),Ce(n,i,a)),m},deleteProperty:(n,i)=>{let a=Reflect.deleteProperty(n,i);return r.add(t),a}},s=new Proxy(e,o);return I.set(e,s),s};var je=e=>{let t=e;for(;t;){let r=t.getAttribute&&t.getAttribute("b-error");if(r)return{errorTag:r,targetElement:t};let o=t.tagName?t.tagName.toLowerCase():"",s=w[o];if(s){let n=s.getAttribute("b-error");if(n)return{errorTag:n,targetElement:t}}t=t.parentElement||t.getRootNode&&t.getRootNode().host}return null},de=null,fe=null,De=(e,t)=>{de=e,fe=t},Ge=(e,t,r)=>{if(!de||!fe){console.error("[Lego] Error boundary dependencies not loaded",r);return}try{t.shadowRoot?t.shadowRoot.innerHTML="":t.innerHTML="";let o=document.createElement(e);(t.shadowRoot||t).appendChild(o),de(o),o._studs&&(o._studs.$error={message:r.message,stack:r.stack,component:t.tagName.toLowerCase()},fe(o))}catch(o){console.error("[Lego] Error boundary failed to render:",o),console.error("[Lego] Original error:",r)}},z=(e,t,r)=>{let o=je(t);o?Ge(o.errorTag,o.targetElement,e):f.onError(e,r,t)};var H=(e,t,r=null)=>{let o=t._studs,s=a=>{let c=$(a);if(!c.bound){if(a.hasAttributes()){let l=a.attributes;for(let m=0;m<l.length;m++){let g=l[m];if(g.name.startsWith("@")){let d=g.name.slice(1).split("."),p=d[0],y=d.slice(1);a.addEventListener(p,h=>{if(y.includes("prevent")&&h.preventDefault(),y.includes("stop")&&h.stopPropagation(),!(y.includes("self")&&h.target!==h.currentTarget)){if(typeof KeyboardEvent<"u"&&h instanceof KeyboardEvent){if(y.includes("ctrl")&&!h.ctrlKey||y.includes("alt")&&!h.altKey||y.includes("shift")&&!h.shiftKey||y.includes("meta")&&!h.metaKey)return;let E=y.filter(u=>!["prevent","stop","self","ctrl","alt","shift","meta","capture","once","passive"].includes(u));if(E.length>0){let u=h.key.toLowerCase();if(!E.some(b=>b==="enter"?u==="enter":b==="esc"||b==="escape"?u==="escape":b==="space"?u===" ":b==="tab"?u==="tab":b==="delete"?u==="delete":b==="backspace"?u==="backspace":b==="up"?u==="arrowup":b==="down"?u==="arrowdown":b==="left"?u==="arrowleft":b==="right"?u==="arrowright":b==="alpha"?/^[a-z]$/.test(u):b==="numbers"?/^[0-9]$/.test(u):b===u))return}}try{let E=o;if(r){let v=N(r.listName,{state:o,global:x,self:t})[r.index];E=Object.assign(Object.create(o),{[r.name]:v})}N(g.value,{state:E,global:x,self:a,event:h,$event:h},!0)}catch(E){z(E,t,"event-handler")}}})}}if(a.hasAttribute("b-sync")){let m=a.getAttribute("b-sync"),g=()=>{try{let d,p;if(r&&m.startsWith(`${r.name}.`)){let E=N(r.listName,{state:o,global:x,self:t})[r.index];if(!E)return;let u=m.split(".").slice(1);p=u.pop(),d=u.reduce((v,b)=>v[b],E)}else{let h=m.split(".");p=h.pop(),d=h.reduce((E,u)=>E[u],o)}let y=a.type==="checkbox"?a.checked:a.value;d&&d[p]!==y&&(d[p]=y)}catch(d){f.onError(d,"sync-update",a)}};a.addEventListener("input",g),a.addEventListener("change",g)}if(a.hasAttribute("b-var")){let m=a.getAttribute("b-var");o.$vars&&(o.$vars[m]=a)}}c.bound=!0}};e instanceof Element&&s(e);let n=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT),i;for(;i=n.nextNode();)s(i)},Je=e=>{let t=[],r=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT),o,s=[];for(;o=r.nextNode();){let n=o;if((c=>{let l=c.parentNode;for(;l&&l!==e;){if(l.hasAttribute&&l.hasAttribute("b-for"))return!0;l=l.parentNode}return!1})(o))continue;let a=c=>{if(/\bglobal\b/.test(c)){let l=e.host||e;q.add(l)}};if(o.nodeType===Node.ELEMENT_NODE){for(let[l,m]of Object.entries(G))if(m.scan){let g=m.scan(o,{checkGlobal:a,getPrivateData:$,pendingOperations:s,current:n});g&&t.push(g)}let[c]=K();[...o.attributes].forEach(l=>{l.value.includes(c)&&(a(l.value),t.push({type:"attr",node:o,attrName:l.name,template:l.value}))})}else if(o.nodeType===Node.TEXT_NODE){let[c]=K();o.textContent.includes(c)&&(a(o.textContent),t.push({type:"text",node:o,template:o.textContent}))}}return s.forEach(n=>n()),t},Ve=(e,t)=>{let r=n=>{if(n.nodeType===Node.TEXT_NODE){n._tpl===void 0&&(n._tpl=n.textContent);let i=n._tpl.replace(U(),(a,c)=>N(c.trim(),{state:t,global:x,self:n})??"");n.textContent!==i&&(n.textContent=i)}else if(n.nodeType===Node.ELEMENT_NODE){let[i]=K();[...n.attributes].forEach(a=>{if(a._tpl===void 0&&(a._tpl=a.value),a._tpl.includes(i)){let c=a._tpl.replace(U(),(l,m)=>N(m.trim(),{state:t,global:x,self:n})??"");a.value!==c&&(a.value=c,a.name==="class"&&(n.className=c))}})}};r(e);let o=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT),s;for(;s=o.nextNode();)r(s)},C=e=>{f.debug&&console.log("[Lego Trace] render() called for:",e.tagName);let t=e._studs;if(!t)return;let r=$(e);if(!r.rendering){r.rendering=!0,f.metrics&&f.metrics.onRenderStart&&f.metrics.onRenderStart(e);try{let o=e.shadowRoot||e;r.bindings||(r.bindings=Je(o)),r.bindings.forEach(s=>{let n=G[s.type];if(n){n.execute({binding:s,state:t,global:x,helpers:{bind:H,updateNodeBindings:Ve,contextEl:e}});return}if(s.type==="text"){let i=s.template.replace(U(),(a,c)=>N(c.trim(),{state:t,global:x,self:s.node})??"");s.node.textContent!==i&&(s.node.textContent=i)}if(s.type==="attr"){let i=s.template.replace(U(),(a,c)=>N(c.trim(),{state:t,global:x,self:s.node})??"");s.node.getAttribute(s.attrName)!==i&&(s.node.setAttribute(s.attrName,i),s.attrName==="class"&&(s.node.className=i))}}),t===x&&q.forEach(s=>C(s))}catch(o){z(o,e,"render")}finally{f.metrics&&f.metrics.onRenderEnd&&f.metrics.onRenderEnd(e),r.rendering=!1}}};var Be=new Map,Re={},Pe=e=>{Re=e},Fe=async()=>{let e=Object.entries(Re).map(async([t,r])=>{let o=await Promise.all(r.map(async s=>{try{let i=await(await fetch(s)).text(),a=new CSSStyleSheet;return await a.replace(i),a}catch(n){return console.error(`[Lego] Failed to load stylesheet: ${s}`,n),null}}));Be.set(t,o.filter(s=>s!==null))});await Promise.all(e),f.debug&&console.log("[Lego Debug] Re-applying stylesheets to",L.size,"blocks"),L.forEach(t=>{pe(t)})},pe=e=>{if(!e.shadowRoot&&!e._legoShadow)return;let t=e.shadowRoot||e._legoShadow,r=e.tagName.toLowerCase(),o=w[r],s=(o&&o.getAttribute("b-stylesheets")||"").split(/\s+/).filter(Boolean),n=(e.getAttribute("b-stylesheets")||"").split(/\s+/).filter(Boolean),i=[],a=e.parentElement||(e.getRootNode?e.getRootNode().host:null);for(;a;){let g=(a.getAttribute("b-cascade")||"").split(/\s+/).filter(Boolean);g.length&&i.push(...g);let d=a.tagName?a.tagName.toLowerCase():"";if(w[d]){let p=(w[d].getAttribute("b-cascade")||"").split(/\s+/).filter(Boolean);p.length&&i.push(...p)}a=a.parentElement||a.getRootNode&&a.getRootNode().host}let c=[...new Set([...s,...n])],l=[...new Set(i)],m=[...new Set([...l,...c])];if(m.length>0){let g=m.flatMap(d=>Be.get(d)||[]);g.length>0&&f.debug&&console.log("[Lego Debug] Applying styles to",e.tagName,"- Names:",m,"- Sheets:",g.length),g.length>0&&(t.adoptedStyleSheets=[...g])}e._studsMeta={stylesheets:{explicit:c,inherited:l,applied:m}}};var S=e=>{if(f.debug&&console.log("[Lego Trace] snap() called for:",e.tagName),!e||e.nodeType!==Node.ELEMENT_NODE)return;let t=$(e),r=e.tagName.toLowerCase(),o=w[r];if(o&&!t.snapped){t.snapped=!0;let n=o.content.cloneNode(!0),i=e.shadowRoot;i?i.innerHTML="":i=e.attachShadow({mode:"open"}),pe(e);let a=P(e,"*")||P(e.getRootNode().host,"*"),c=a&&a.state?a.state:{},l=B.get(r)||{},m=le(o.getAttribute("b-logic")||o.getAttribute("b-data")||"{}"),g=le(e.getAttribute("b-logic")||e.getAttribute("b-data")||"{}",c),d={$vars:{},$element:e,get $parent(){return P(e,"*")},$emit:(h,E)=>{e.dispatchEvent(new CustomEvent(h,{detail:E,bubbles:!0,composed:!0}))},get $route(){return x.$route},get $go(){return x.$go}};R(l,d),R(m,d),R(g,d),e._studs=F(d,e),Object.defineProperty(e,"state",{get(){return this._studs},set(h){Object.assign(this._studs,h)},configurable:!0,enumerable:!1}),i.appendChild(n);let p=i.querySelector("style");p&&(p.textContent=p.textContent.replace(/\bself\b/g,":host")),H(i,e),L.add(e),C(e);let y=i.querySelectorAll("*");if(f.debug&&console.log("[Lego Debug] Nested scan in",e.tagName,"- Found elements:",y.length),y.forEach(h=>{let E=h.tagName.toLowerCase(),u=!!w[E];u&&f.debug&&console.log("[Lego Debug] Checking",E,"- Registered:",u),u&&S(h)}),e._legoShadow||(e._legoShadow=i),typeof e._studs.mounted=="function")try{e._studs.mounted.call(e._studs)}catch(h){z(h,e,"mounted")}}let s=e.parentElement;for(;s&&!s._studs;)s=s.parentElement;s&&s._studs&&H(e,s),[...e.children].forEach(S)},W=e=>{if(e._studs&&typeof e._studs.unmounted=="function")try{e._studs.unmounted.call(e._studs)}catch(r){z(r,e,"unmounted")}e.shadowRoot&&[...e.shadowRoot.children].forEach(W),L.delete(e),q.delete(e),e._studs&&(e._studs.$element=null,e._studs.$vars&&(Object.keys(e._studs.$vars).forEach(r=>{e._studs.$vars[r]=null}),e._studs.$vars=null),e._studs.$emit&&(e._studs.$emit=null),delete e._studs.$parent,delete e._studs.$route,delete e._studs.$go,e._studs=null),e.hasOwnProperty("state")&&delete e.state;let t=D.get(e);if(t&&(t.bindings&&(t.bindings.forEach(r=>{let o=G[r.type];if(o&&o.destroy)try{o.destroy({binding:r})}catch(s){console.error(`[Lego] Error in directive cleanup (${r.type}):`,s)}r.node=null,r.anchor=null}),t.bindings=null),t.anchor=null,D.delete(e)),A.has(e)){let r=A.get(e);r.forEach((o,s)=>{o&&o._studs&&(o._studs=null)}),r.clear(),A.delete(e)}[...e.children].forEach(W)};function ze(e,t="block.lego"){let r={template:"",script:"",style:"",stylesAttr:"",cascadeAttr:"",errorAttr:"",blockName:be(t)},o=e,s=/<(template|script|style)\b((?:\s+(?:[^>"']|"[^"]*"|'[^']*')*)*)>/i;for(;o;){let n=o.match(s);if(!n)break;let i=n[1].toLowerCase(),a=n[2],c=n[0],l=n.index,m=`</${i}>`,g=l+c.length,d=o.indexOf(m,g);if(d===-1){console.warn(`[Lego] Unclosed <${i}> tag in ${t}`);break}let p=o.slice(g,d);if(i==="template"){r.template=p.trim();let y=a.match(/b-stylesheets=["']([^"']+)["']/);y&&(r.stylesAttr=y[1]);let h=a.match(/b-cascade=["']([^"']+)["']/);h&&(r.cascadeAttr=h[1]);let E=a.match(/b-error=["']([^"']+)["']/);E&&(r.errorAttr=E[1])}else i==="script"?r.script=p.trim():i==="style"&&(r.style=p.trim());o=o.slice(d+m.length)}return r}var He=(e,t,r="block.lego")=>{let o=ze(t,r),{blockName:s,template:n,script:i,style:a,stylesAttr:c,cascadeAttr:l,errorAttr:m}=o,g={};if(i)try{let p=i.trim(),y=p.match(/export\s+default\s+({[\s\S]*})/),h=y?y[1]:p;g=new Function("Lego","$db",`return ${h}`)(e,e.globals.$db)}catch(p){f.onError(p,"script",s)}let d=n;a&&(d=`<style>${a}</style>`+d),w[s]=document.createElement("template"),w[s].innerHTML=d,c&&w[s].setAttribute("b-stylesheets",c),l&&w[s].setAttribute("b-cascade",l),m&&w[s].setAttribute("b-error",m),B.set(s,g),document.querySelectorAll(s).forEach(p=>!$(p).snapped&&S(p))};re.setHandler(C);De(S,C);Ee(S);var Xe={url:typeof window<"u"?window.location.pathname:"/",route:"",params:{},query:{},method:"GET",body:null},_=F({$route:Xe,$go:(e,...t)=>te(e,...t)(document.body),$db:ue},typeof document<"u"?document.body:null);he(_);var O={db:ue,snap:S,unsnap:W,defineLegoFile:(e,t)=>He(O,e,t),block:(e,t,r={},o="",s="",n="")=>{let i=document.createElement("template");i.setAttribute("b-id",e),i.setAttribute("b-stylesheets",o),s&&i.setAttribute("b-cascade",s),n&&i.setAttribute("b-error",n),i.innerHTML=t,w[e]=i,B.set(e,r);try{let a={};R(r,a),Q.set(e.toLowerCase(),F(a,document.body))}catch(a){f.onError(a,"define",e)}document.querySelectorAll(e).forEach(S),[...L].forEach(a=>{a.tagName.toLowerCase()===e.toLowerCase()&&(f.debug&&console.log("[Lego HMR] Reloading",e),W(a),S(a))}),L.forEach(a=>{a._legoShadow&&a._legoShadow.querySelectorAll(e).forEach(c=>{$(c).snapped||(f.debug&&console.log("[Lego Debug] Lazy-initializing",e,"in",a.tagName),S(c))})})},getActiveBlocksCount:()=>L.size,getLegos:()=>Object.keys(w),config:f,globals:_,route:(e,t,r=null)=>{let o=[],s=e==="*"?".*":e;s=s.replace(/:([^\/]+)/g,(n,i)=>(o.push(i),"([^/]+)")),j.push({path:e,regex:new RegExp(`^${s}$`),tagName:t,paramNames:o,middleware:r})},debug:{stylesheets:e=>!e||!e._studsMeta?null:e._studsMeta.stylesheets},init:async(e=document.body,t={})=>{if((!e||typeof e.nodeType!="number")&&(e=document.body),Pe(t.styles||{}),f.loader=t.loader,f.debug=t.debug===!0,await Fe(),j.length>0){let n=window.location.pathname,i=window.location.search,a=ce(n);if(a){let{match:c,params:l}=a,m=Object.fromEntries(new URLSearchParams(i));_.$route.url=n+i,_.$route.route=c.path,_.$route.params=l,_.$route.query=m,_.$route.method="GET",_.$route.body=null}}document.querySelectorAll("template[b-id]").forEach(n=>{w[n.getAttribute("b-id")]=n});let r=n=>{if(n.nodeType!==Node.ELEMENT_NODE)return;S(n);let i=n.tagName.toLowerCase();if(i.includes("-")&&!w[i]&&f.loader&&!L.has(n)){let a=f.loader(i);if(a){let c=typeof a=="string"?fetch(a).then(l=>l.text()):a;Promise.resolve(c).then(l=>O.defineLegoFile(l,`${i}.lego`)).catch(l=>console.error(`[Lego] Failed to load ${i}:`,l))}}};new MutationObserver(n=>n.forEach(i=>{i.addedNodes.forEach(r),i.removedNodes.forEach(a=>a.nodeType===Node.ELEMENT_NODE&&W(a))})).observe(e,{childList:!0,subtree:!0});let s=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT);for(r(e);s.nextNode();)r(s.currentNode);if(e._studs=_,H(e,e),C(e),t.studio){if(!w["lego-studio"]){let n=document.createElement("script");n.src="https://unpkg.com/@legodom/studio@0.0.2/dist/lego-studio.js",n.onerror=()=>console.warn("[Lego] Failed to load Studio from CDN"),document.head.appendChild(n)}O.route("/_/studio","lego-studio"),O.route("/_/studio/:block","lego-studio")}j.length>0&&(window.addEventListener("popstate",n=>{let i=n.state?.legoTargets||null;ee(i)}),document.addEventListener("submit",n=>{n.preventDefault()}),document.addEventListener("click",n=>{let a=n.composedPath().find(c=>c.tagName==="A"&&(c.hasAttribute("b-target")||c.hasAttribute("b-link")));if(a){n.preventDefault();let c=a.getAttribute("href"),l=a.getAttribute("b-target"),m=l?l.split(/\s+/).filter(Boolean):[],g=a.getAttribute("b-link")!=="false";_.$go(c,...m).get(g)}}),ee()),Oe()}};typeof window<"u"?window.Lego=O:typeof global<"u"&&(global.Lego=O);var go=O;})();
|
package/package.json
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lego-dom",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "A feature-rich web components + SFC frontend framework",
|
|
6
|
-
"main": "
|
|
6
|
+
"main": "dist/lego.min.js",
|
|
7
|
+
"module": "dist/lego.mjs",
|
|
8
|
+
"types": "./dist/lego.d.ts",
|
|
7
9
|
"type": "module",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"src",
|
|
13
|
+
"vite-plugin.js"
|
|
14
|
+
],
|
|
8
15
|
"exports": {
|
|
9
|
-
".":
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
".": {
|
|
17
|
+
"import": "./dist/lego.mjs",
|
|
18
|
+
"require": "./dist/lego.min.js",
|
|
19
|
+
"default": "./dist/lego.min.js"
|
|
20
|
+
},
|
|
21
|
+
"./vite-plugin": "./vite-plugin.js"
|
|
14
22
|
},
|
|
15
23
|
"keywords": [
|
|
16
24
|
"framework",
|
|
@@ -22,7 +30,8 @@
|
|
|
22
30
|
"author": "Tersoo Ortserga",
|
|
23
31
|
"scripts": {
|
|
24
32
|
"test": "vitest run",
|
|
25
|
-
"build": "
|
|
33
|
+
"build": "node build.js",
|
|
34
|
+
"clean": "rm -rf dist",
|
|
26
35
|
"docs:dev": "vitepress dev docs",
|
|
27
36
|
"docs:build": "vitepress build docs",
|
|
28
37
|
"docs:preview": "vitepress preview docs"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export const createBatcher = (renderHandler = null) => {
|
|
2
|
+
let queued = false;
|
|
3
|
+
const blocksToUpdate = new Set();
|
|
4
|
+
let isProcessing = false;
|
|
5
|
+
let _render = renderHandler;
|
|
6
|
+
|
|
7
|
+
// FIX: Queue for updates arriving during processing (prevents drops)
|
|
8
|
+
const pendingQueue = new Set();
|
|
9
|
+
|
|
10
|
+
// Optimization: Single timer for all updated hooks instead of one per block
|
|
11
|
+
let updatedTimer = null;
|
|
12
|
+
const pendingUpdated = new Set();
|
|
13
|
+
|
|
14
|
+
const setHandler = (fn) => { _render = fn; };
|
|
15
|
+
|
|
16
|
+
const scheduleUpdatedHooks = () => {
|
|
17
|
+
if (updatedTimer) clearTimeout(updatedTimer);
|
|
18
|
+
updatedTimer = setTimeout(() => {
|
|
19
|
+
pendingUpdated.forEach(el => {
|
|
20
|
+
const state = el._studs;
|
|
21
|
+
if (state && typeof state.updated === 'function') {
|
|
22
|
+
try { state.updated.call(state); } catch (e) { console.error(`[Lego] Error in updated hook:`, e); }
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
pendingUpdated.clear();
|
|
26
|
+
updatedTimer = null;
|
|
27
|
+
}, 50); // Global debounce
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const processPendingQueue = () => {
|
|
31
|
+
if (pendingQueue.size > 0) {
|
|
32
|
+
// Move pending to main queue
|
|
33
|
+
pendingQueue.forEach(el => blocksToUpdate.add(el));
|
|
34
|
+
pendingQueue.clear();
|
|
35
|
+
|
|
36
|
+
// Trigger next batch if not already queued
|
|
37
|
+
if (!queued && blocksToUpdate.size > 0) {
|
|
38
|
+
queued = true;
|
|
39
|
+
requestAnimationFrame(processBatch);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const processBatch = () => {
|
|
45
|
+
isProcessing = true;
|
|
46
|
+
const batch = Array.from(blocksToUpdate);
|
|
47
|
+
blocksToUpdate.clear();
|
|
48
|
+
queued = false;
|
|
49
|
+
|
|
50
|
+
// Simple synchronous render loop (faster for small batches, no overhead)
|
|
51
|
+
batch.forEach(el => {
|
|
52
|
+
if (el.isConnected && _render) _render(el);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Batch complete: Queue updated hooks efficiently
|
|
56
|
+
batch.forEach(el => pendingUpdated.add(el));
|
|
57
|
+
scheduleUpdatedHooks();
|
|
58
|
+
isProcessing = false;
|
|
59
|
+
|
|
60
|
+
// FIX: Process pending queue after batch completes
|
|
61
|
+
processPendingQueue();
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
add: (el) => {
|
|
66
|
+
if (!el) return;
|
|
67
|
+
|
|
68
|
+
// FIX: Queue during processing instead of dropping
|
|
69
|
+
if (isProcessing) {
|
|
70
|
+
pendingQueue.add(el);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
blocksToUpdate.add(el);
|
|
75
|
+
if (queued) return;
|
|
76
|
+
queued = true;
|
|
77
|
+
|
|
78
|
+
requestAnimationFrame(processBatch);
|
|
79
|
+
},
|
|
80
|
+
setHandler
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const globalBatcher = createBatcher();
|