@wcstack/server 0.3.1 → 1.8.5

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 CHANGED
@@ -1,354 +1,354 @@
1
- # @wcstack/server
2
-
3
- **What if Web Components rendered on the server?**
4
-
5
- Imagine a future where your `<wcs-state>` templates are fully rendered before they reach the browser — data is fetched, bindings are resolved, lists are expanded, conditionals are evaluated. The user sees content instantly, and the client picks up exactly where the server left off.
6
-
7
- That's what `@wcstack/server` explores. It runs your existing `@wcstack/state` templates through happy-dom, produces fully-rendered HTML with embedded hydration data, and lets the client resume reactivity with zero flicker. No special template syntax, no server-specific markup — just the same HTML you already write.
8
-
9
- ## Features
10
-
11
- ### Basic Features
12
- - **Full Template Rendering**: Runs `@wcstack/state` bindings server-side — text, attributes, `for` loops, `if`/`elseif`/`else` conditionals, filters, and mustache `{{ }}` syntax.
13
- - **Automatic Hydration Data**: Generates `<wcs-ssr>` elements containing state snapshots, template fragments, and property maps for seamless client-side hydration.
14
- - **Async Data Fetching**: Supports `$connectedCallback` with `fetch()` — server waits for all async operations before rendering.
15
- - **RenderCore**: A headless, event-driven rendering class that follows the `wc-bindable` protocol for observable `html` / `loading` / `error` state.
16
- - **Zero Browser Dependencies**: Runs in Node.js with happy-dom as the only runtime dependency.
17
-
18
- ### Unique Features
19
- - **Drop-in SSR**: No changes to your client-side templates. Add `enable-ssr` to `<wcs-state>` and render with `renderToString()`.
20
- - **Template Fragment Preservation**: `for`/`if` template sources are captured with UUID references so the client can re-execute structural directives.
21
- - **Property Hydration**: DOM properties that can't be expressed as attributes (e.g., `innerHTML`) are serialized separately and restored during hydration.
22
- - **wc-bindable Protocol**: `RenderCore` exposes rendering state via the standard protocol, enabling the same `bind()` pattern on both server and client.
23
-
24
- ## Installation
25
-
26
- ```bash
27
- npm install @wcstack/server
28
- ```
29
-
30
- ## Quick Start
31
-
32
- ### `renderToString()` — One-shot rendering
33
-
34
- ```javascript
35
- import { renderToString } from "@wcstack/server";
36
-
37
- const html = await renderToString(`
38
- <wcs-state json='{"items":["Apple","Banana","Cherry"]}' enable-ssr>
39
- </wcs-state>
40
- <ul>
41
- <template data-wcs="for: items">
42
- <li data-wcs="textContent: items.*"></li>
43
- </template>
44
- </ul>
45
- `);
46
-
47
- console.log(html);
48
- // Fully rendered HTML with <wcs-ssr> hydration data
49
- ```
50
-
51
- ### `RenderCore` — Observable rendering with caching
52
-
53
- ```javascript
54
- import { RenderCore } from "@wcstack/server";
55
-
56
- const renderer = new RenderCore();
57
-
58
- // Listen to state changes via wc-bindable protocol
59
- renderer.addEventListener("wcs-render:loading-changed", (e) => {
60
- console.log("loading:", e.detail);
61
- });
62
-
63
- renderer.addEventListener("wcs-render:html-changed", (e) => {
64
- console.log("rendered:", e.detail.length, "bytes");
65
- });
66
-
67
- // Render and cache
68
- await renderer.render(templateHtml);
69
-
70
- // Subsequent reads use the cached result
71
- console.log(renderer.html);
72
- ```
73
-
74
- ## API Reference
75
-
76
- ### `renderToString(html: string): Promise<string>`
77
-
78
- Renders an HTML string containing `@wcstack/state` templates. Returns fully-rendered HTML with hydration data for any `<wcs-state enable-ssr>` elements.
79
-
80
- **Rendering pipeline:**
81
- 1. Creates a happy-dom window and installs browser globals
82
- 2. Parses HTML and triggers `connectedCallback` on all `<wcs-state>` elements
83
- 3. Awaits all `$connectedCallback` promises (including `fetch()` calls)
84
- 4. Waits for `buildBindings` to complete
85
- 5. Generates `<wcs-ssr>` elements for states with `enable-ssr`
86
- 6. Restores globals and returns the rendered HTML
87
-
88
- ### `RenderCore`
89
-
90
- Headless rendering class extending `EventTarget`. Implements the `wc-bindable` protocol.
91
-
92
- | Property | Type | Description |
93
- |----------|------|-------------|
94
- | `html` | `string \| null` | Rendered HTML (cached after `render()`) |
95
- | `loading` | `boolean` | `true` while rendering is in progress |
96
- | `error` | `Error \| null` | Error from the last `render()` call, if any |
97
-
98
- | Method | Returns | Description |
99
- |--------|---------|-------------|
100
- | `render(html)` | `Promise<string \| null>` | Renders the template and caches the result. Returns `null` on error. |
101
-
102
- | Event | Detail | Description |
103
- |-------|--------|-------------|
104
- | `wcs-render:html-changed` | `string` | Fired when rendering completes successfully |
105
- | `wcs-render:loading-changed` | `boolean` | Fired when loading state changes |
106
- | `wcs-render:error` | `Error` | Fired when rendering fails |
107
-
108
- **wc-bindable declaration:**
109
-
110
- ```typescript
111
- static wcBindable = {
112
- protocol: "wc-bindable",
113
- version: 1,
114
- properties: [
115
- { name: "html", event: "wcs-render:html-changed" },
116
- { name: "loading", event: "wcs-render:loading-changed" },
117
- { name: "error", event: "wcs-render:error" },
118
- ],
119
- };
120
- ```
121
-
122
- ### Helper Functions
123
-
124
- | Function | Description |
125
- |----------|-------------|
126
- | `installGlobals(window)` | Installs happy-dom globals on `globalThis`. Returns a restore function. |
127
- | `extractStateData(stateEl)` | Extracts data properties from a `<wcs-state>` element (excludes `$`-prefixed keys and functions). |
128
-
129
- ### Constants
130
-
131
- | Name | Description |
132
- |------|-------------|
133
- | `GLOBALS_KEYS` | Array of browser global keys installed during SSR (`document`, `HTMLElement`, `Node`, etc.) |
134
- | `VERSION` | Package version string from `package.json` |
135
-
136
- ## SSR Output Structure
137
-
138
- When a `<wcs-state>` has the `enable-ssr` attribute, `renderToString()` inserts a `<wcs-ssr>` element immediately before it containing all hydration data:
139
-
140
- ```html
141
- <!-- Generated by renderToString() -->
142
- <wcs-ssr name="default" version="0.1.0">
143
-
144
- <!-- State snapshot -->
145
- <script type="application/json">{"items":["Apple","Banana","Cherry"]}</script>
146
-
147
- <!-- Template fragments (for client-side re-execution) -->
148
- <template id="uuid-1234" data-wcs="for: items">
149
- <li data-wcs="textContent: items.*"></li>
150
- </template>
151
-
152
- <!-- Non-attribute properties (optional) -->
153
- <script type="application/json" data-wcs-ssr-props>
154
- {"wcs-ssr-0": {"innerHTML": "<b>rich</b>"}}
155
- </script>
156
-
157
- </wcs-ssr>
158
-
159
- <wcs-state json='...' enable-ssr></wcs-state>
160
-
161
- <!-- Rendered output (visible immediately) -->
162
- <ul>
163
- <li>Apple</li>
164
- <li>Banana</li>
165
- <li>Cherry</li>
166
- </ul>
167
- ```
168
-
169
- The client-side `@wcstack/state` reads the `<wcs-ssr>` element during hydration, restores state and templates, and resumes reactivity without re-rendering.
170
-
171
- ## Server Integration Example
172
-
173
- ```javascript
174
- import { createServer } from "node:http";
175
- import { RenderCore } from "@wcstack/server";
176
-
177
- const renderer = new RenderCore();
178
-
179
- const template = `
180
- <wcs-state enable-ssr>
181
- <script type="module">
182
- export default {
183
- async $connectedCallback() {
184
- const res = await fetch("http://localhost:3000/api/data");
185
- this.items = await res.json();
186
- },
187
- items: []
188
- };
189
- </script>
190
- </wcs-state>
191
- <ul>
192
- <template data-wcs="for: items">
193
- <li data-wcs="textContent: items.*"></li>
194
- </template>
195
- </ul>
196
- `;
197
-
198
- createServer(async (req, res) => {
199
- if (!renderer.html) {
200
- await renderer.render(template);
201
- }
202
- res.writeHead(200, { "Content-Type": "text/html" });
203
- res.end(renderer.html);
204
- }).listen(3000);
205
- ```
206
-
207
- ## Input HTML Rules
208
-
209
- - Pass only the contents of `<body>` — do not include `<html>`, `<head>`, or `<body>` tags.
210
- - `<script>` / `<link>` external resource loading is not executed.
211
- → Provide required packages via `options.bootstraps`.
212
-
213
- ## What SSR Can Do
214
-
215
- ### State Initialization & Data Fetching
216
-
217
- - Load `<wcs-state>` from `json` attribute, `src` attribute, or inline `<script type="module">`
218
- - Execute `$connectedCallback` for server-side fetch (API calls, etc.)
219
-
220
- ```html
221
- <!-- Direct JSON -->
222
- <wcs-state enable-ssr json='{"title":"Hello"}'></wcs-state>
223
-
224
- <!-- Fetch data from API in $connectedCallback -->
225
- <!-- $connectedCallback is defined as a method on the state object; `this` is the state proxy -->
226
- <wcs-state enable-ssr>
227
- <script type="module">
228
- export default {
229
- async $connectedCallback() {
230
- const res = await fetch('/api/users');
231
- this.users = await res.json();
232
- }
233
- };
234
- </script>
235
- </wcs-state>
236
- ```
237
-
238
- ### Server Communication with wcs-fetch
239
-
240
- - `<wcs-fetch>` auto-fetch (without `manual`) also executes on the server
241
- - Use `manual` + `$connectedCallback` for explicit control:
242
-
243
- ```html
244
- <wcs-fetch id="api" url="/api/users" manual></wcs-fetch>
245
- <wcs-state enable-ssr>
246
- <script type="module">
247
- export default {
248
- async $connectedCallback() {
249
- const el = document.getElementById('api');
250
- this.users = await el.fetch();
251
- }
252
- };
253
- </script>
254
- </wcs-state>
255
- ```
256
-
257
- > Note: `bootstrapFetch` must be included in the `bootstraps` option.
258
-
259
- ### Bindings & Structural Rendering
260
-
261
- - `data-wcs` binding application (text, attribute, class, style, property)
262
- - `<template data-wcs="for:">` / `if:` / `elseif:` / `else:` structural rendering
263
-
264
- ```html
265
- <ul>
266
- <template data-wcs="for: users">
267
- <li data-wcs="textContent: .name"></li>
268
- </template>
269
- </ul>
270
- <template data-wcs="if: isAdmin">
271
- <div class="admin-panel">...</div>
272
- </template>
273
- ```
274
-
275
- ### Hydration
276
-
277
- - Automatic `<wcs-ssr>` metadata generation for `<wcs-state enable-ssr>`
278
- - Client-side hydration restores bindings without re-rendering
279
- - `<wcs-state>` without `enable-ssr` runs client-only (partial CSR)
280
-
281
- ### Custom Element Waiting
282
-
283
- - Automatically awaits all custom elements with `static hasConnectedCallbackPromise = true`
284
-
285
- ## What SSR Cannot Do
286
-
287
- - Execute `<script src="...">` or `<link>` in `<head>`
288
- - Access browser-specific APIs (localStorage, sessionStorage, navigator, etc.)
289
- - Render Shadow DOM (Declarative Shadow DOM not supported)
290
- - Register event handlers (restored via client-side hydration)
291
- - Load components dynamically via `<wcs-autoloader>`
292
-
293
- ## HTML Splitting Pattern
294
-
295
- `renderToString` receives only the `<body>` contents. Wrap the result with `<head>` and `<script>` tags on the outside:
296
-
297
- ```javascript
298
- // server.js
299
- const ssrBody = await renderToString(template, {
300
- baseUrl: 'http://localhost:3001',
301
- });
302
- const page = `<!DOCTYPE html>
303
- <html lang="en">
304
- <head>
305
- <script type="module" src="/packages/state/dist/auto.js"></script>
306
- </head>
307
- <body>${ssrBody}</body>
308
- </html>`;
309
- ```
310
-
311
- ### Using Multiple Packages
312
-
313
- ```javascript
314
- import { bootstrapState, getBindingsReady } from '@wcstack/state';
315
- import { bootstrapFetch } from '@wcstack/fetch';
316
-
317
- const ssrBody = await renderToString(template, {
318
- baseUrl: 'http://localhost:3001',
319
- bootstraps: [bootstrapState, bootstrapFetch],
320
- ready: [(doc) => getBindingsReady(doc)],
321
- });
322
- ```
323
-
324
- ## How It Works
325
-
326
- ### Rendering Pipeline
327
-
328
- 1. **Global Setup**: Creates a happy-dom `Window` and temporarily installs browser globals (`document`, `HTMLElement`, `MutationObserver`, etc.) on `globalThis`. Disables `URL.createObjectURL` to force the base64 data URL fallback for inline scripts.
329
-
330
- 2. **SSR Mode**: Sets `data-wcs-server` attribute on the `<html>` element. `@wcstack/state` detects this attribute to enable SSR behavior.
331
-
332
- 3. **Bootstrap**: Calls user-provided bootstrap functions (defaults to `bootstrapState()` if omitted).
333
-
334
- 4. **HTML Parse & Callback**: Sets `document.body.innerHTML`, which triggers happy-dom's element lifecycle. Each `<wcs-state>` loads its data source and runs `$connectedCallback`. Awaits all custom elements with `hasConnectedCallbackPromise`.
335
-
336
- 5. **Ready**: Awaits user-provided ready functions (defaults to `getBindingsReady()`) — text interpolation, attribute mapping, list expansion, conditional evaluation.
337
-
338
- 6. **SSR Metadata**: Each `<wcs-state enable-ssr>` automatically generates a `<wcs-ssr>` element in its `connectedCallback`.
339
-
340
- 7. **Cleanup**: Restores original globals and closes the happy-dom window.
341
-
342
- ### Client-Side Hydration
343
-
344
- The client-side `@wcstack/state` detects `<wcs-ssr>` elements and:
345
- 1. Restores state from the JSON snapshot (skipping network requests)
346
- 2. Re-attaches template fragments using UUID references
347
- 3. Applies non-attribute properties from the props script
348
- 4. Resumes normal reactive binding
349
-
350
- The rendered DOM is visible immediately — hydration only wires up interactivity.
351
-
352
- ## License
353
-
354
- MIT
1
+ # @wcstack/server
2
+
3
+ **What if Web Components rendered on the server?**
4
+
5
+ Imagine a future where your `<wcs-state>` templates are fully rendered before they reach the browser — data is fetched, bindings are resolved, lists are expanded, conditionals are evaluated. The user sees content instantly, and the client picks up exactly where the server left off.
6
+
7
+ That's what `@wcstack/server` explores. It runs your existing `@wcstack/state` templates through happy-dom, produces fully-rendered HTML with embedded hydration data, and lets the client resume reactivity with zero flicker. No special template syntax, no server-specific markup — just the same HTML you already write.
8
+
9
+ ## Features
10
+
11
+ ### Basic Features
12
+ - **Full Template Rendering**: Runs `@wcstack/state` bindings server-side — text, attributes, `for` loops, `if`/`elseif`/`else` conditionals, filters, and mustache `{{ }}` syntax.
13
+ - **Automatic Hydration Data**: Generates `<wcs-ssr>` elements containing state snapshots, template fragments, and property maps for seamless client-side hydration.
14
+ - **Async Data Fetching**: Supports `$connectedCallback` with `fetch()` — server waits for all async operations before rendering.
15
+ - **RenderCore**: A headless, event-driven rendering class that follows the `wc-bindable` protocol for observable `html` / `loading` / `error` state.
16
+ - **Zero Browser Dependencies**: Runs in Node.js with happy-dom as the only runtime dependency.
17
+
18
+ ### Unique Features
19
+ - **Drop-in SSR**: No changes to your client-side templates. Add `enable-ssr` to `<wcs-state>` and render with `renderToString()`.
20
+ - **Template Fragment Preservation**: `for`/`if` template sources are captured with UUID references so the client can re-execute structural directives.
21
+ - **Property Hydration**: DOM properties that can't be expressed as attributes (e.g., `innerHTML`) are serialized separately and restored during hydration.
22
+ - **wc-bindable Protocol**: `RenderCore` exposes rendering state via the standard protocol, enabling the same `bind()` pattern on both server and client.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @wcstack/server
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ### `renderToString()` — One-shot rendering
33
+
34
+ ```javascript
35
+ import { renderToString } from "@wcstack/server";
36
+
37
+ const html = await renderToString(`
38
+ <wcs-state json='{"items":["Apple","Banana","Cherry"]}' enable-ssr>
39
+ </wcs-state>
40
+ <ul>
41
+ <template data-wcs="for: items">
42
+ <li data-wcs="textContent: items.*"></li>
43
+ </template>
44
+ </ul>
45
+ `);
46
+
47
+ console.log(html);
48
+ // Fully rendered HTML with <wcs-ssr> hydration data
49
+ ```
50
+
51
+ ### `RenderCore` — Observable rendering with caching
52
+
53
+ ```javascript
54
+ import { RenderCore } from "@wcstack/server";
55
+
56
+ const renderer = new RenderCore();
57
+
58
+ // Listen to state changes via wc-bindable protocol
59
+ renderer.addEventListener("wcs-render:loading-changed", (e) => {
60
+ console.log("loading:", e.detail);
61
+ });
62
+
63
+ renderer.addEventListener("wcs-render:html-changed", (e) => {
64
+ console.log("rendered:", e.detail.length, "bytes");
65
+ });
66
+
67
+ // Render and cache
68
+ await renderer.render(templateHtml);
69
+
70
+ // Subsequent reads use the cached result
71
+ console.log(renderer.html);
72
+ ```
73
+
74
+ ## API Reference
75
+
76
+ ### `renderToString(html: string): Promise<string>`
77
+
78
+ Renders an HTML string containing `@wcstack/state` templates. Returns fully-rendered HTML with hydration data for any `<wcs-state enable-ssr>` elements.
79
+
80
+ **Rendering pipeline:**
81
+ 1. Creates a happy-dom window and installs browser globals
82
+ 2. Parses HTML and triggers `connectedCallback` on all `<wcs-state>` elements
83
+ 3. Awaits all `$connectedCallback` promises (including `fetch()` calls)
84
+ 4. Waits for `buildBindings` to complete
85
+ 5. Generates `<wcs-ssr>` elements for states with `enable-ssr`
86
+ 6. Restores globals and returns the rendered HTML
87
+
88
+ ### `RenderCore`
89
+
90
+ Headless rendering class extending `EventTarget`. Implements the `wc-bindable` protocol.
91
+
92
+ | Property | Type | Description |
93
+ |----------|------|-------------|
94
+ | `html` | `string \| null` | Rendered HTML (cached after `render()`) |
95
+ | `loading` | `boolean` | `true` while rendering is in progress |
96
+ | `error` | `Error \| null` | Error from the last `render()` call, if any |
97
+
98
+ | Method | Returns | Description |
99
+ |--------|---------|-------------|
100
+ | `render(html)` | `Promise<string \| null>` | Renders the template and caches the result. Returns `null` on error. |
101
+
102
+ | Event | Detail | Description |
103
+ |-------|--------|-------------|
104
+ | `wcs-render:html-changed` | `string` | Fired when rendering completes successfully |
105
+ | `wcs-render:loading-changed` | `boolean` | Fired when loading state changes |
106
+ | `wcs-render:error` | `Error` | Fired when rendering fails |
107
+
108
+ **wc-bindable declaration:**
109
+
110
+ ```typescript
111
+ static wcBindable = {
112
+ protocol: "wc-bindable",
113
+ version: 1,
114
+ properties: [
115
+ { name: "html", event: "wcs-render:html-changed" },
116
+ { name: "loading", event: "wcs-render:loading-changed" },
117
+ { name: "error", event: "wcs-render:error" },
118
+ ],
119
+ };
120
+ ```
121
+
122
+ ### Helper Functions
123
+
124
+ | Function | Description |
125
+ |----------|-------------|
126
+ | `installGlobals(window)` | Installs happy-dom globals on `globalThis`. Returns a restore function. |
127
+ | `extractStateData(stateEl)` | Extracts data properties from a `<wcs-state>` element (excludes `$`-prefixed keys and functions). |
128
+
129
+ ### Constants
130
+
131
+ | Name | Description |
132
+ |------|-------------|
133
+ | `GLOBALS_KEYS` | Array of browser global keys installed during SSR (`document`, `HTMLElement`, `Node`, etc.) |
134
+ | `VERSION` | Package version string from `package.json` |
135
+
136
+ ## SSR Output Structure
137
+
138
+ When a `<wcs-state>` has the `enable-ssr` attribute, `renderToString()` inserts a `<wcs-ssr>` element immediately before it containing all hydration data:
139
+
140
+ ```html
141
+ <!-- Generated by renderToString() -->
142
+ <wcs-ssr name="default" version="0.1.0">
143
+
144
+ <!-- State snapshot -->
145
+ <script type="application/json">{"items":["Apple","Banana","Cherry"]}</script>
146
+
147
+ <!-- Template fragments (for client-side re-execution) -->
148
+ <template id="uuid-1234" data-wcs="for: items">
149
+ <li data-wcs="textContent: items.*"></li>
150
+ </template>
151
+
152
+ <!-- Non-attribute properties (optional) -->
153
+ <script type="application/json" data-wcs-ssr-props>
154
+ {"wcs-ssr-0": {"innerHTML": "<b>rich</b>"}}
155
+ </script>
156
+
157
+ </wcs-ssr>
158
+
159
+ <wcs-state json='...' enable-ssr></wcs-state>
160
+
161
+ <!-- Rendered output (visible immediately) -->
162
+ <ul>
163
+ <li>Apple</li>
164
+ <li>Banana</li>
165
+ <li>Cherry</li>
166
+ </ul>
167
+ ```
168
+
169
+ The client-side `@wcstack/state` reads the `<wcs-ssr>` element during hydration, restores state and templates, and resumes reactivity without re-rendering.
170
+
171
+ ## Server Integration Example
172
+
173
+ ```javascript
174
+ import { createServer } from "node:http";
175
+ import { RenderCore } from "@wcstack/server";
176
+
177
+ const renderer = new RenderCore();
178
+
179
+ const template = `
180
+ <wcs-state enable-ssr>
181
+ <script type="module">
182
+ export default {
183
+ async $connectedCallback() {
184
+ const res = await fetch("http://localhost:3000/api/data");
185
+ this.items = await res.json();
186
+ },
187
+ items: []
188
+ };
189
+ </script>
190
+ </wcs-state>
191
+ <ul>
192
+ <template data-wcs="for: items">
193
+ <li data-wcs="textContent: items.*"></li>
194
+ </template>
195
+ </ul>
196
+ `;
197
+
198
+ createServer(async (req, res) => {
199
+ if (!renderer.html) {
200
+ await renderer.render(template);
201
+ }
202
+ res.writeHead(200, { "Content-Type": "text/html" });
203
+ res.end(renderer.html);
204
+ }).listen(3000);
205
+ ```
206
+
207
+ ## Input HTML Rules
208
+
209
+ - Pass only the contents of `<body>` — do not include `<html>`, `<head>`, or `<body>` tags.
210
+ - `<script>` / `<link>` external resource loading is not executed.
211
+ → Provide required packages via `options.bootstraps`.
212
+
213
+ ## What SSR Can Do
214
+
215
+ ### State Initialization & Data Fetching
216
+
217
+ - Load `<wcs-state>` from `json` attribute, `src` attribute, or inline `<script type="module">`
218
+ - Execute `$connectedCallback` for server-side fetch (API calls, etc.)
219
+
220
+ ```html
221
+ <!-- Direct JSON -->
222
+ <wcs-state enable-ssr json='{"title":"Hello"}'></wcs-state>
223
+
224
+ <!-- Fetch data from API in $connectedCallback -->
225
+ <!-- $connectedCallback is defined as a method on the state object; `this` is the state proxy -->
226
+ <wcs-state enable-ssr>
227
+ <script type="module">
228
+ export default {
229
+ async $connectedCallback() {
230
+ const res = await fetch('/api/users');
231
+ this.users = await res.json();
232
+ }
233
+ };
234
+ </script>
235
+ </wcs-state>
236
+ ```
237
+
238
+ ### Server Communication with wcs-fetch
239
+
240
+ - `<wcs-fetch>` auto-fetch (without `manual`) also executes on the server
241
+ - Use `manual` + `$connectedCallback` for explicit control:
242
+
243
+ ```html
244
+ <wcs-fetch id="api" url="/api/users" manual></wcs-fetch>
245
+ <wcs-state enable-ssr>
246
+ <script type="module">
247
+ export default {
248
+ async $connectedCallback() {
249
+ const el = document.getElementById('api');
250
+ this.users = await el.fetch();
251
+ }
252
+ };
253
+ </script>
254
+ </wcs-state>
255
+ ```
256
+
257
+ > Note: `bootstrapFetch` must be included in the `bootstraps` option.
258
+
259
+ ### Bindings & Structural Rendering
260
+
261
+ - `data-wcs` binding application (text, attribute, class, style, property)
262
+ - `<template data-wcs="for:">` / `if:` / `elseif:` / `else:` structural rendering
263
+
264
+ ```html
265
+ <ul>
266
+ <template data-wcs="for: users">
267
+ <li data-wcs="textContent: .name"></li>
268
+ </template>
269
+ </ul>
270
+ <template data-wcs="if: isAdmin">
271
+ <div class="admin-panel">...</div>
272
+ </template>
273
+ ```
274
+
275
+ ### Hydration
276
+
277
+ - Automatic `<wcs-ssr>` metadata generation for `<wcs-state enable-ssr>`
278
+ - Client-side hydration restores bindings without re-rendering
279
+ - `<wcs-state>` without `enable-ssr` runs client-only (partial CSR)
280
+
281
+ ### Custom Element Waiting
282
+
283
+ - Automatically awaits all custom elements with `static hasConnectedCallbackPromise = true`
284
+
285
+ ## What SSR Cannot Do
286
+
287
+ - Execute `<script src="...">` or `<link>` in `<head>`
288
+ - Access browser-specific APIs (localStorage, sessionStorage, navigator, etc.)
289
+ - Render Shadow DOM (Declarative Shadow DOM not supported)
290
+ - Register event handlers (restored via client-side hydration)
291
+ - Load components dynamically via `<wcs-autoloader>`
292
+
293
+ ## HTML Splitting Pattern
294
+
295
+ `renderToString` receives only the `<body>` contents. Wrap the result with `<head>` and `<script>` tags on the outside:
296
+
297
+ ```javascript
298
+ // server.js
299
+ const ssrBody = await renderToString(template, {
300
+ baseUrl: 'http://localhost:3001',
301
+ });
302
+ const page = `<!DOCTYPE html>
303
+ <html lang="en">
304
+ <head>
305
+ <script type="module" src="/packages/state/dist/auto.js"></script>
306
+ </head>
307
+ <body>${ssrBody}</body>
308
+ </html>`;
309
+ ```
310
+
311
+ ### Using Multiple Packages
312
+
313
+ ```javascript
314
+ import { bootstrapState, getBindingsReady } from '@wcstack/state';
315
+ import { bootstrapFetch } from '@wcstack/fetch';
316
+
317
+ const ssrBody = await renderToString(template, {
318
+ baseUrl: 'http://localhost:3001',
319
+ bootstraps: [bootstrapState, bootstrapFetch],
320
+ ready: [(doc) => getBindingsReady(doc)],
321
+ });
322
+ ```
323
+
324
+ ## How It Works
325
+
326
+ ### Rendering Pipeline
327
+
328
+ 1. **Global Setup**: Creates a happy-dom `Window` and temporarily installs browser globals (`document`, `HTMLElement`, `MutationObserver`, etc.) on `globalThis`. Disables `URL.createObjectURL` to force the base64 data URL fallback for inline scripts.
329
+
330
+ 2. **SSR Mode**: Sets `data-wcs-server` attribute on the `<html>` element. `@wcstack/state` detects this attribute to enable SSR behavior.
331
+
332
+ 3. **Bootstrap**: Calls user-provided bootstrap functions (defaults to `bootstrapState()` if omitted).
333
+
334
+ 4. **HTML Parse & Callback**: Sets `document.body.innerHTML`, which triggers happy-dom's element lifecycle. Each `<wcs-state>` loads its data source and runs `$connectedCallback`. Awaits all custom elements with `hasConnectedCallbackPromise`.
335
+
336
+ 5. **Ready**: Awaits user-provided ready functions (defaults to `getBindingsReady()`) — text interpolation, attribute mapping, list expansion, conditional evaluation.
337
+
338
+ 6. **SSR Metadata**: Each `<wcs-state enable-ssr>` automatically generates a `<wcs-ssr>` element in its `connectedCallback`.
339
+
340
+ 7. **Cleanup**: Restores original globals and closes the happy-dom window.
341
+
342
+ ### Client-Side Hydration
343
+
344
+ The client-side `@wcstack/state` detects `<wcs-ssr>` elements and:
345
+ 1. Restores state from the JSON snapshot (skipping network requests)
346
+ 2. Re-attaches template fragments using UUID references
347
+ 3. Applies non-attribute properties from the props script
348
+ 4. Resumes normal reactive binding
349
+
350
+ The rendered DOM is visible immediately — hydration only wires up interactivity.
351
+
352
+ ## License
353
+
354
+ MIT