lego-dom 1.0.0 → 1.3.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.
Files changed (74) hide show
  1. package/.legodom +87 -0
  2. package/CHANGELOG.md +87 -3
  3. package/cdn.html +10 -5
  4. package/docs/.vitepress/config.js +23 -7
  5. package/docs/api/config.md +95 -0
  6. package/docs/api/define.md +29 -2
  7. package/docs/api/directives.md +10 -2
  8. package/docs/api/index.md +1 -0
  9. package/docs/contributing/01-welcome.md +2 -0
  10. package/docs/contributing/02-registry.md +37 -3
  11. package/docs/contributing/06-init.md +13 -2
  12. package/docs/contributing/07-observer.md +3 -0
  13. package/docs/contributing/08-snap.md +15 -1
  14. package/docs/contributing/10-studs.md +3 -1
  15. package/docs/contributing/11-scanner.md +13 -0
  16. package/docs/contributing/12-render.md +32 -10
  17. package/docs/contributing/13-directives.md +19 -1
  18. package/docs/contributing/14-events.md +1 -1
  19. package/docs/contributing/15-router.md +49 -1
  20. package/docs/contributing/16-state.md +9 -10
  21. package/docs/contributing/17-legodom.md +1 -8
  22. package/docs/contributing/index.md +23 -4
  23. package/docs/examples/form.md +1 -1
  24. package/docs/examples/index.md +3 -3
  25. package/docs/examples/routing.md +10 -10
  26. package/docs/examples/sfc-showcase.md +1 -1
  27. package/docs/examples/todo-app.md +7 -7
  28. package/docs/guide/cdn-usage.md +44 -18
  29. package/docs/guide/components.md +18 -12
  30. package/docs/guide/directives.md +131 -22
  31. package/docs/guide/directory-structure.md +248 -0
  32. package/docs/guide/faq.md +210 -0
  33. package/docs/guide/getting-started.md +14 -10
  34. package/docs/guide/index.md +1 -1
  35. package/docs/guide/lifecycle.md +32 -0
  36. package/docs/guide/quick-start.md +4 -4
  37. package/docs/guide/reactivity.md +2 -2
  38. package/docs/guide/routing.md +69 -8
  39. package/docs/guide/server-side.md +134 -0
  40. package/docs/guide/sfc.md +96 -13
  41. package/docs/guide/templating.md +62 -57
  42. package/docs/index.md +9 -9
  43. package/docs/router/basic-routing.md +8 -8
  44. package/docs/router/cold-entry.md +2 -2
  45. package/docs/router/history.md +7 -7
  46. package/docs/router/index.md +1 -1
  47. package/docs/router/resolver.md +5 -5
  48. package/docs/router/surgical-swaps.md +5 -5
  49. package/docs/tutorial/01-project-setup.md +152 -0
  50. package/docs/tutorial/02-your-first-component.md +226 -0
  51. package/docs/tutorial/03-adding-routes.md +279 -0
  52. package/docs/tutorial/04-multi-page-app.md +329 -0
  53. package/docs/tutorial/05-state-and-globals.md +285 -0
  54. package/docs/tutorial/index.md +40 -0
  55. package/examples/vite-app/index.html +1 -0
  56. package/examples/vite-app/src/app.js +2 -2
  57. package/examples/vite-app/src/components/side-menu.lego +46 -0
  58. package/examples/vite-app/vite.config.js +2 -1
  59. package/main.js +261 -72
  60. package/main.min.js +7 -0
  61. package/monitoring-plugin.js +111 -0
  62. package/package.json +4 -2
  63. package/parse-lego.js +49 -22
  64. package/tests/error.test.js +74 -0
  65. package/tests/main.test.js +2 -2
  66. package/tests/memory.test.js +68 -0
  67. package/tests/monitoring.test.js +74 -0
  68. package/tests/naming.test.js +74 -0
  69. package/tests/parse-lego.test.js +2 -2
  70. package/tests/security.test.js +67 -0
  71. package/tests/server.test.js +114 -0
  72. package/tests/syntax.test.js +67 -0
  73. package/vite-plugin.js +3 -2
  74. package/docs/guide/contributing.md +0 -32
@@ -0,0 +1,210 @@
1
+ # Frequently Asked Questions
2
+
3
+ Quick answers to the most common LegoDOM questions.
4
+
5
+ ## Project Setup
6
+
7
+ ### Where do I define my routes?
8
+
9
+ **In your entry file (`app.js` or `main.js`), before `Lego.init()`.**
10
+
11
+ ```javascript
12
+ // src/app.js
13
+ import { Lego } from 'lego-dom';
14
+ import registerComponents from 'virtual:lego-components';
15
+
16
+ registerComponents();
17
+
18
+ // Define routes HERE ⭐
19
+ Lego.route('/', 'home-page');
20
+ Lego.route('/login', 'login-page');
21
+ Lego.route('/users/:id', 'user-profile');
22
+
23
+ await Lego.init(); // Must come AFTER routes
24
+ ```
25
+
26
+ ### Where does my config go?
27
+
28
+ Everything related to app configuration belongs in your entry file:
29
+
30
+ | What | Where |
31
+ |------|-------|
32
+ | Component registration | `registerComponents()` |
33
+ | Route definitions | `Lego.route(...)` |
34
+ | Global state init | `Lego.globals.user = null` |
35
+ | Engine start | `Lego.init()` |
36
+
37
+ ### Should I use `main.js` or `app.js`?
38
+
39
+ Either works! It's just a naming convention. Use whatever your Vite template created, or rename it. Just make sure your `index.html` points to it:
40
+
41
+ ```html
42
+ <script type="module" src="/src/app.js"></script>
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Components
48
+
49
+ ### Why isn't my component showing?
50
+
51
+ Check these common issues:
52
+
53
+ 1. **No route defined** – Did you add `Lego.route('/', 'my-component')`?
54
+ 2. **Missing `<lego-router>`** – Your HTML needs `<lego-router></lego-router>`
55
+ 3. **Wrong component name** – Filename `user-card.lego` → component `<user-card>`
56
+ 4. **Not registered** – Did you call `registerComponents()` before `init()`?
57
+
58
+ ### Why do component names need hyphens?
59
+
60
+ It's a Web Components standard! Custom elements must contain a hyphen to avoid conflicts with future HTML elements.
61
+
62
+ ✅ Valid: `user-card`, `app-nav`, `my-button`
63
+ ❌ Invalid: `usercard`, `Card`, `button`
64
+
65
+ ### Can I use PascalCase filenames?
66
+
67
+ Yes! LegoDOM automatically converts:
68
+ - `UserCard.lego` → `<user-card>`
69
+ - `AppNav.lego` → `<app-nav>`
70
+ - `my_component.lego` → `<my-component>`
71
+
72
+ ---
73
+
74
+ ## Navigation
75
+
76
+ ### How do I navigate between pages?
77
+
78
+ **Option 1: Declarative (in templates)**
79
+ ```html
80
+ <a href="/login" b-link>Go to Login</a>
81
+ ```
82
+
83
+ **Option 2: Programmatic (in JavaScript)**
84
+ ```javascript
85
+ this.$go('/login').get();
86
+ ```
87
+
88
+ ### What's the difference between `b-link` and `b-target`?
89
+
90
+ | Attribute | What it does |
91
+ |-----------|--------------|
92
+ | `b-link` | SPA navigation, updates URL, swaps `<lego-router>` |
93
+ | `b-target="#id"` | Swaps content of specific element, updates URL |
94
+ | `b-target="#id" b-link="false"` | Swaps content, does NOT update URL |
95
+
96
+ ### How do I pass data when navigating?
97
+
98
+ Use global state:
99
+
100
+ ```javascript
101
+ // Before navigating
102
+ Lego.globals.selectedUser = { id: 42, name: 'John' };
103
+ this.$go('/user-details').get();
104
+
105
+ // In the target component
106
+ mounted() {
107
+ console.log(Lego.globals.selectedUser.name); // 'John'
108
+ }
109
+ ```
110
+
111
+ Or use route parameters:
112
+
113
+ ```javascript
114
+ // Route: Lego.route('/users/:id', 'user-profile')
115
+ this.$go('/users/42').get();
116
+
117
+ // In user-profile component
118
+ mounted() {
119
+ const userId = this.$route.params.id; // '42'
120
+ }
121
+ ```
122
+
123
+ ---
124
+
125
+ ## State
126
+
127
+ ### How do I share data between components?
128
+
129
+ Use `Lego.globals`:
130
+
131
+ ```javascript
132
+ // Component A sets it
133
+ Lego.globals.user = { name: 'John' };
134
+
135
+ // Component B reads it
136
+ console.log(Lego.globals.user.name); // 'John'
137
+
138
+ // In templates
139
+ <p>Hello, [[ global.user.name ]]!</p>
140
+ ```
141
+
142
+ ### Why isn't my data updating the view?
143
+
144
+ Make sure you're mutating the reactive object, not replacing references:
145
+
146
+ ```javascript
147
+ // ✅ This works - mutating property
148
+ this.items.push(newItem);
149
+ this.user.name = 'Jane';
150
+
151
+ // ❌ This might not work - reassigning local variable
152
+ let items = this.items;
153
+ items.push(newItem); // Won't trigger re-render!
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Styling
159
+
160
+ ### What is `self` in styles?
161
+
162
+ `self` is a special keyword that targets the component's root element (like `:host` in Shadow DOM):
163
+
164
+ ```html
165
+ <style>
166
+ self {
167
+ display: block;
168
+ padding: 1rem;
169
+ }
170
+ </style>
171
+ ```
172
+
173
+ LegoDOM automatically transforms this to `:host` for Shadow DOM.
174
+
175
+ ### Do styles leak to other components?
176
+
177
+ No! Styles are scoped via Shadow DOM. Your `.button` class won't affect buttons in other components.
178
+
179
+ ---
180
+
181
+ ## Build & Development
182
+
183
+ ### Can I use LegoDOM without Vite?
184
+
185
+ Yes! Use the CDN approach:
186
+
187
+ ```html
188
+ <script src="https://unpkg.com/lego-dom/main.js"></script>
189
+ <template b-id="my-component">...</template>
190
+ <my-component></my-component>
191
+ <script>Lego.init();</script>
192
+ ```
193
+
194
+ See [CDN Usage](/guide/cdn-usage) for details.
195
+
196
+ ### Why use Vite?
197
+
198
+ Benefits of Vite + `.lego` files:
199
+ - **Hot reload** – Changes appear instantly
200
+ - **Auto-discovery** – No manual component registration
201
+ - **Better organization** – One file per component
202
+ - **Syntax highlighting** – Editor support for `.lego` files
203
+
204
+ ---
205
+
206
+ ## Still Stuck?
207
+
208
+ - 📖 [Complete Tutorial](/tutorial/) – Build an app step-by-step
209
+ - 💬 [GitHub Discussions](https://github.com/rayattack/LegoDOM/discussions)
210
+ - 🐛 [Report Issues](https://github.com/rayattack/LegoDOM/issues)
@@ -2,6 +2,10 @@
2
2
 
3
3
  Get up and running with Lego in under 5 minutes.
4
4
 
5
+ ::: tip 🚀 Want a Complete Walkthrough?
6
+ Check out our **[Step-by-Step Tutorial](/tutorial/)** – build a full multi-page app from scratch in 15 minutes!
7
+ :::
8
+
5
9
  ## Installation
6
10
 
7
11
  ### Option 1: CDN (No Build Tools)
@@ -21,7 +25,7 @@ The fastest way to try Lego is via CDN:
21
25
  <template b-id="my-component" b-data="{ count: 0 }">
22
26
  <h1>Hello Lego!</h1>
23
27
  <button @click="count++">Click me</button>
24
- <p>Count: {{ count }}</p>
28
+ <p>Count: [[ count ]]</p>
25
29
  </template>
26
30
  </body>
27
31
  </html>
@@ -45,7 +49,7 @@ import { Lego } from 'lego-dom';
45
49
  Lego.define('my-component', `
46
50
  <h1>Hello Lego!</h1>
47
51
  <button @click="count++">Click me</button>
48
- <p>Count: {{ count }}</p>
52
+ <p>Count: [[ count ]]</p>
49
53
  `, {
50
54
  count: 0,
51
55
  });
@@ -144,8 +148,8 @@ Lego.define('click-counter', `
144
148
  }
145
149
  </style>
146
150
 
147
- <h2>{{ message }}</h2>
148
- <p>Count: {{ count }}</p>
151
+ <h2>[[ message ]]</h2>
152
+ <p>Count: [[ count ]]</p>
149
153
  <button @click="increment()">Click Me!</button>
150
154
  `, {
151
155
  message: 'Welcome!',
@@ -175,8 +179,8 @@ Create `src/components/click-counter.lego`:
175
179
  }
176
180
  </style>
177
181
 
178
- <h2>{{ message }}</h2>
179
- <p>Count: {{ count }}</p>
182
+ <h2>[[ message ]]</h2>
183
+ <p>Count: [[ count ]]</p>
180
184
  <button @click="increment()">Click Me!</button>
181
185
  </template>
182
186
 
@@ -197,11 +201,11 @@ The Vite plugin automatically discovers and registers it!
197
201
 
198
202
  ### 1. Templates
199
203
 
200
- Templates define what your component looks like. Use `{{ }}` for dynamic content:
204
+ Templates define what your component looks like. Use `[[ ]]` for dynamic content:
201
205
 
202
206
  ```html
203
- <h1>Hello {{ name }}!</h1>
204
- <p>{{ calculateAge() }} years old</p>
207
+ <h1>Hello [[ name ]]!</h1>
208
+ <p>[[ calculateAge() ]] years old</p>
205
209
  ```
206
210
 
207
211
  ### 2. State (Studs)
@@ -238,7 +242,7 @@ Special attributes for common patterns:
238
242
 
239
243
  ```html
240
244
  <p b-show="isLoggedIn">Welcome back!</p>
241
- <li b-for="item in items">{{ item.name }}</li>
245
+ <li b-for="item in items">[[ item.name ]]</li>
242
246
  <input b-sync="username" />
243
247
  ```
244
248
 
@@ -77,7 +77,7 @@ Compare that to:
77
77
  | Aspect | Lego | Traditional Frameworks |
78
78
  |--------|--------|----------------------|
79
79
  | **Reactivity** | Direct object mutation | setState / dispatch / ref() |
80
- | **Templates** | HTML with <code v-pre>{{ }}</code> | JSX / template syntax |
80
+ | **Templates** | HTML with <code v-pre>[[ ]]</code> | JSX / template syntax |
81
81
  | **Styles** | Shadow DOM (native) | CSS-in-JS / scoped CSS |
82
82
  | **Build** | Optional | Required |
83
83
  | **Learning Curve** | Hours | Days/Weeks |
@@ -233,6 +233,38 @@ Called when the component is removed from the DOM.
233
233
  }
234
234
  }
235
235
  }
236
+ }
237
+ }
238
+ ```
239
+
240
+ ## Performance Hooks (Metrics)
241
+
242
+ For advanced monitoring, LegoDOM provides global hooks in `Lego.config.metrics`. These run for **every** component.
243
+
244
+ ### `onRenderStart` & `onRenderEnd`
245
+
246
+ Useful for tracking how long renders take, which helps identify slow components.
247
+
248
+ ```javascript
249
+ Lego.config.metrics = {
250
+ onRenderStart(el) {
251
+ console.time(`render-${el.tagName}`);
252
+ },
253
+ onRenderEnd(el) {
254
+ console.timeEnd(`render-${el.tagName}`);
255
+ }
256
+ };
257
+ ```
258
+
259
+ ### `onAllSettled` (New in v2.0)
260
+
261
+ Called when the entire component tree (including Shadow DOM children) has finished its initial render pass. This is perfect for removing loading spinners or measuring "Time to Interactive".
262
+
263
+ ```javascript
264
+ /* main.js */
265
+ Lego.init(document.body).then(() => {
266
+ document.getElementById('global-loader').remove();
267
+ });
236
268
  ```
237
269
 
238
270
  ## Lifecycle Flow
@@ -12,16 +12,16 @@ The fastest way to get started with Lego is using the CDN. No build tools requir
12
12
  <title>Lego Quick Start</title>
13
13
  </head>
14
14
  <body>
15
- <!-- 2. Use your component -->
16
- <hello-world></hello-world>
15
+ <!-- Interpolation works here too -->
16
+ <hello-world name="Lego"></hello-world>
17
17
 
18
18
  <!-- 3. Define the template -->
19
19
  <template b-id="hello-world">
20
20
  <style>
21
21
  h1 { color: #646cff; }
22
22
  </style>
23
- <h1>Hello, {{ name }}!</h1>
24
- <button @click="toggle()">Toggle Name</button>
23
+ <h1>Hello [[ name ]]!</h1>
24
+ <button @click="count++">Count is [[ count ]]</button>
25
25
  </template>
26
26
 
27
27
  <!-- 4. Load Lego -->
@@ -189,7 +189,7 @@ Nested objects are automatically reactive:
189
189
 
190
190
  ```html
191
191
  <!-- All reactive -->
192
- <p>{{ user.profile.settings.theme }}</p>
192
+ <p>[[ user.profile.settings.theme ]]</p>
193
193
  ```
194
194
 
195
195
  ```js
@@ -381,7 +381,7 @@ Use methods:
381
381
  ```
382
382
 
383
383
  ```html
384
- <p>{{ fullName() }}</p>
384
+ <p>[[ fullName() ]]</p>
385
385
  ```
386
386
 
387
387
  ### Debouncing Updates
@@ -1,14 +1,75 @@
1
1
  # Surgical Routing
2
2
 
3
- LegoDOM features a powerful "Surgical Router" that sets it apart from typical SPA routers. Instead of just replacing a single `<router-outlet>`, Lego allows you to swap **any** part of your page from **any** link, giving you the feel of a complex SPA with the simplicity of old-school HTML frames.
3
+ **Stop rebuilding your entire page just to change one div.**
4
4
 
5
- ## The Concept
5
+ LegoDOM's router is different. It doesn't have a single "Root Outlet". Instead, **any element** can be a router target. This allows you to build **Persistent Layouts** (like Sidebars, Music Players, or Chat Windows) that never reload or lose state while the user navigates.
6
6
 
7
- In traditional SPAs, you have one `RouterView` that swaps out entire pages.
8
- In Lego, every link can be a router trigger, and every element can be a target.
7
+ ::: tip 🚀 Just Want to Navigate Between Pages?
8
+ Here's the quick answer:
9
9
 
10
- - **`b-target`**: "Where should this content go?"
11
- - **`b-link`**: "Should this update the URL history?"
10
+ **1. Define routes in `app.js`:**
11
+ ```javascript
12
+ Lego.route('/', 'home-page');
13
+ Lego.route('/login', 'login-page');
14
+ Lego.route('/dashboard', 'dashboard-page');
15
+ ```
16
+
17
+ **2. Navigate with links:**
18
+ ```html
19
+ <a href="/login" b-link>Go to Login</a>
20
+ ```
21
+
22
+ **3. Or navigate with JavaScript:**
23
+ ```javascript
24
+ this.$go('/login').get();
25
+ ```
26
+
27
+ That's it! For the full tutorial, see [Adding Routes](/tutorial/03-adding-routes).
28
+ :::
29
+
30
+ ## Quick Reference
31
+
32
+ | I want to... | Code |
33
+ |--------------|------|
34
+ | Define a route | `Lego.route('/path', 'component-name')` |
35
+ | Link to a page | `<a href="/path" b-link>Click</a>` |
36
+ | Navigate via JS | `this.$go('/path').get()` |
37
+ | Get URL params | `this.$route.params.id` |
38
+ | Update only one div | `<a href="/x" b-target="#myDiv">` |
39
+ | Navigate without URL change | `this.$go('/x').get(false)` |
40
+
41
+ ---
42
+
43
+ ## The Architecture: "The Persistent Shell"
44
+
45
+ The best way to use LegoDOM is to define a static "Shell" that holds your persistent tools, and standard outlets for your content.
46
+
47
+ ```html
48
+ <body>
49
+ <!-- 1. The Shell (Sidebar): Never reloads. Keeps scroll pos & draft state. -->
50
+ <aside id="sidebar">
51
+ <file-tree></file-tree>
52
+ </aside>
53
+
54
+ <!-- 2. The Stage (Main Content): This changes when URL changes. -->
55
+ <lego-router id="stage"></lego-router>
56
+
57
+ <!-- 3. The Context (Right Panel): Tools based on selection. -->
58
+ <aside id="tools"></aside>
59
+ </body>
60
+ ```
61
+
62
+ Then, you simply tell links *where* to render their content:
63
+
64
+ ```html
65
+ <!-- Updates component in #stage (Default URL navigation) -->
66
+ <a href="/dashboard" b-target="#stage">Dashboard</a>
67
+
68
+ <!-- Updates component in #tools (Keeps URL sync, but only touches right panel) -->
69
+ <a href="/tools/settings" b-target="#tools">Settings</a>
70
+ ```
71
+
72
+ This feels like a native app. The Sidebar doesn't flicker. The scroll position isn't lost.
12
73
 
13
74
  ---
14
75
 
@@ -130,7 +191,7 @@ Keep a persistent Main Content while swapping sidebars.
130
191
  <!-- Context specific tools render here -->
131
192
  <template b-id="user-profile">
132
193
  <h1>User Profile</h1>
133
- <p>User ID: {{ $route.params.id }}</p>
194
+ <p>User ID: [[ $route.params.id ]]</p>
134
195
  <button @click="loadUser()">Load User</button>
135
196
  </template>
136
197
  </aside>
@@ -213,7 +274,7 @@ Lego.route('/customers/:id/orders/:orderId', 'order-details-page');
213
274
  <!-- order-details-page.lego -->
214
275
  <template>
215
276
  <customers-layout>
216
- <order-info id="{{ $route.params.orderId }}"></order-info>
277
+ <order-info id="[[ $route.params.orderId ]]"></order-info>
217
278
  </customers-layout>
218
279
  </template>
219
280
  ```
@@ -0,0 +1,134 @@
1
+ # Server-Side Architecture
2
+
3
+ LegoDOM is designed to play nicely with backend frameworks like **Heaven**, **Django**, **Rails**, or **Go**.
4
+
5
+ Unlike typical SPAs that require a massive build step, LegoDOM can fetch components **on-demand** from your server. This gives you the routing simplicity of a backend with the interactivity of a frontend framework.
6
+
7
+ ## The Auto-Loader Pattern
8
+
9
+ Instead of bundling every component `users-list`, `chat-widget`, `billing-modal` into one big `app.js` file, you can load them lazily.
10
+
11
+ Configure the `loader` hook in your main entry file:
12
+
13
+ ```javascript
14
+ Lego.init(document.body, {
15
+ loader: (tagName) => `/components/${tagName}.lego`
16
+ });
17
+ ```
18
+
19
+ Now, your HTML can just use tags that haven't been defined yet:
20
+
21
+ ```html
22
+ <!-- index.html (Server Rendered) -->
23
+ <h1>Dashboard</h1>
24
+
25
+ <!-- LegoDOM sees this, fetches /components/user-feed.lego, and upgrades it -->
26
+ <user-feed></user-feed>
27
+ ```
28
+
29
+ ## Power Mode: Authentication & State
30
+
31
+ Often you need to pass **Authentication Tokens** or **Global State** to the server to get a personalized component.
32
+
33
+ Return a `Promise` from your loader to take full control of the fetch:
34
+
35
+ ```javascript
36
+ Lego.init(document.body, {
37
+ loader: async (tagName) => {
38
+ const token = localStorage.getItem('jwt');
39
+
40
+ const response = await fetch(`/components/${tagName}`, {
41
+ headers: {
42
+ 'Authorization': `Bearer ${token}`,
43
+ 'X-Theme': Lego.globals.theme
44
+ }
45
+ });
46
+
47
+ if (!response.ok) return null;
48
+ return await response.text(); // Return the SFC content
49
+ }
50
+ });
51
+ ```
52
+
53
+ ## Server-Side State Injection
54
+
55
+ Since the server generates the `.lego` file, it can inject data before sending it to the browser.
56
+
57
+ **Example (Backend Pseudocode):**
58
+
59
+ ```python
60
+ # GET /components/user-card ("Flask-like" syntax)
61
+ @app.route('/components/user-card')
62
+ def get_user_card():
63
+ user = db.get_current_user()
64
+
65
+ # We bake the data right into the template!
66
+ return f"""
67
+ <template>
68
+ <div class="card">
69
+ <h2>[[ name ]]</h2>
70
+ <p>Balance: $[[ balance ]]</p>
71
+ </div>
72
+ </template>
73
+
74
+ <script>
75
+ export default {
76
+ name: "{user.name}",
77
+ balance: {user.balance}
78
+ }
79
+ </script>
80
+ """
81
+ ```
82
+
83
+ The browser receives a component that **already has the data**. No second API call needed!
84
+
85
+ ## Runtime Components
86
+
87
+ If you fetch component code manually (e.g. via WebSockets or a custom pipeline), you can register it using `Lego.defineSFC`:
88
+
89
+ ```javascript
90
+ socket.on('component_update', (msg) => {
91
+ // msg.code contains the <template>... string
92
+ Lego.defineSFC(msg.code, msg.name + '.lego');
93
+ });
94
+ ```
95
+ ```
96
+
97
+ ## Production Considerations
98
+
99
+ ### 1. Error Handling
100
+ What if the server returns a 404 or 500? Your `loader` should handle this gracefully so the user isn't stuck with a blank screen.
101
+
102
+ ```javascript
103
+ loader: async (tagName) => {
104
+ try {
105
+ const res = await fetch(`/components/${tagName}.lego`);
106
+ if (!res.ok) {
107
+ console.error(`Component ${tagName} failed: ${res.status}`);
108
+ // Fallback to a generic error component
109
+ return `<template><div class="error">Failed to load ${tagName}</div></template>`;
110
+ }
111
+ return await res.text();
112
+ } catch (err) {
113
+ return `<template><div class="error">Network Error</div></template>`;
114
+ }
115
+ }
116
+ ```
117
+
118
+ ### 2. Caching Strategy
119
+ LegoDOM caches the *compiled class* of a component once loaded. It does **not** re-fetch the `.lego` file for every instance.
120
+ However, if you want browser-level caching for the HTTP requests, ensure your server sends correct headers:
121
+
122
+ ```http
123
+ Cache-Control: public, max-age=3600, immutable
124
+ ```
125
+
126
+ ### 3. Preloading
127
+ If you know a user is about to visit a page (e.g. hovering a link), you can preload the component SFC:
128
+
129
+ ```javascript
130
+ const preload = (tagName) => {
131
+ // Just calling the loader puts it in the browser's fetch cache
132
+ Lego.config.loader(tagName);
133
+ };
134
+ ```