@wcstack/server 1.32.0 → 2.0.0
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.ja.md +4 -2
- package/README.md +4 -2
- package/dist/index.d.ts +25 -4
- package/dist/index.esm.js +45 -47
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/package.json +2 -2
package/README.ja.md
CHANGED
|
@@ -50,6 +50,8 @@ console.log(html);
|
|
|
50
50
|
// ハイドレーションデータ付きのレンダリング済み HTML
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
同じ呼び出しはページの**スナップショットテスト**にもなります。vitest で `expect(await renderToString(html)).toMatchSnapshot()` と書けば、ブラウザなしで描画結果を固定できます。書き込みやハンドラまで動かすヘッドレス DOM テストでは、後述の `installGlobals()` がそのまま再利用できるグローバル差し替えです — state README の[ページをテストする](../state/README.ja.md#ページをテストする)を参照してください。
|
|
54
|
+
|
|
53
55
|
### `RenderCore` — 監視可能なレンダリング(キャッシュ付き)
|
|
54
56
|
|
|
55
57
|
```javascript
|
|
@@ -135,7 +137,7 @@ static wcBindable = {
|
|
|
135
137
|
| 関数 | 説明 |
|
|
136
138
|
|----------|-------------|
|
|
137
139
|
| `installGlobals(window)` | happy-dom のグローバルを `globalThis` にインストール。復元関数を返す |
|
|
138
|
-
| `
|
|
140
|
+
| `waitForReady(root, { maxIterations? })` | `root`(`document` または `ShadowRoot`)配下で readiness プロトコルに従う全カスタム要素を待つ — `static hasConnectedCallbackPromise` を持つ要素の `connectedCallbackPromise`(待機中に増えた要素も再走査。`<wcs-router>` の初期ルートなど)、次に `static getBindingsReady(root)`(`<wcs-state>` のバインディング構築)。`renderToString` がシリアライズ前に行う待機そのもので、[`@wcstack/testing`](../testing/README.ja.md) の `mount()` が再利用する。バインディング初期化に失敗すると reject |
|
|
139
141
|
|
|
140
142
|
### 定数
|
|
141
143
|
|
|
@@ -150,7 +152,7 @@ static wcBindable = {
|
|
|
150
152
|
|
|
151
153
|
```html
|
|
152
154
|
<!-- renderToString() が生成 -->
|
|
153
|
-
<wcs-ssr
|
|
155
|
+
<wcs-ssr version="0.1.0">
|
|
154
156
|
|
|
155
157
|
<!-- 状態スナップショット -->
|
|
156
158
|
<script type="application/json">{"items":["Apple","Banana","Cherry"]}</script>
|
package/README.md
CHANGED
|
@@ -50,6 +50,8 @@ console.log(html);
|
|
|
50
50
|
// Fully rendered HTML with <wcs-ssr> hydration data
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
The same call doubles as a **snapshot test** for a page: `expect(await renderToString(html)).toMatchSnapshot()` in vitest pins the rendered markup without a browser. For headless DOM tests that also exercise writes and handlers, `installGlobals()` (below) is the globals swap to reuse — see [Testing Your Page](../state/README.md#testing-your-page) in the state README.
|
|
54
|
+
|
|
53
55
|
### `RenderCore` — Observable rendering with caching
|
|
54
56
|
|
|
55
57
|
```javascript
|
|
@@ -135,7 +137,7 @@ static wcBindable = {
|
|
|
135
137
|
| Function | Description |
|
|
136
138
|
|----------|-------------|
|
|
137
139
|
| `installGlobals(window)` | Installs happy-dom globals on `globalThis`. Returns a restore function. |
|
|
138
|
-
| `
|
|
140
|
+
| `waitForReady(root, { maxIterations? })` | Awaits every custom element under `root` (a `document` or a `ShadowRoot`) that follows the readiness protocols — `static hasConnectedCallbackPromise` elements' `connectedCallbackPromise` (re-scanning while new elements appear, e.g. `<wcs-router>`'s initial route), then `static getBindingsReady(root)` (`<wcs-state>`'s binding construction). This is the wait `renderToString` performs before serializing; [`@wcstack/testing`](../testing/README.md)'s `mount()` reuses it. Rejects if binding initialization fails. |
|
|
139
141
|
|
|
140
142
|
### Constants
|
|
141
143
|
|
|
@@ -150,7 +152,7 @@ When a `<wcs-state>` has the `enable-ssr` attribute, `renderToString()` inserts
|
|
|
150
152
|
|
|
151
153
|
```html
|
|
152
154
|
<!-- Generated by renderToString() -->
|
|
153
|
-
<wcs-ssr
|
|
155
|
+
<wcs-ssr version="0.1.0">
|
|
154
156
|
|
|
155
157
|
<!-- State snapshot -->
|
|
156
158
|
<script type="application/json">{"items":["Apple","Banana","Cherry"]}</script>
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,6 @@ import { Window } from 'happy-dom';
|
|
|
3
3
|
declare const GLOBALS_KEYS: string[];
|
|
4
4
|
declare function installGlobals(window: Window): () => void;
|
|
5
5
|
declare function installBaseUrl(baseUrl: string): () => void;
|
|
6
|
-
/** @deprecated Use Ssr.extractStateData() from @wcstack/state instead */
|
|
7
|
-
declare function extractStateData(stateEl: any): Record<string, any>;
|
|
8
6
|
/**
|
|
9
7
|
* 同期の bootstrap 関数、または非同期ローダー。
|
|
10
8
|
* `HTMLElement` を継承するクラスはモジュール評価時にグローバルの `HTMLElement` を
|
|
@@ -33,6 +31,29 @@ interface RenderOptions {
|
|
|
33
31
|
*/
|
|
34
32
|
baseHref?: string;
|
|
35
33
|
}
|
|
34
|
+
interface WaitForReadyOptions {
|
|
35
|
+
/**
|
|
36
|
+
* 安定化ループの上限。`$connectedCallback` が動的に追加した要素を拾うため、
|
|
37
|
+
* 新しい要素が見つからなくなるまで走査を繰り返す(既定 10)。
|
|
38
|
+
*/
|
|
39
|
+
maxIterations?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `root`(document / ShadowRoot)配下のカスタム要素が readiness プロトコルに従って
|
|
43
|
+
* 初期化を終えるまで待つ。renderToString がシリアライズ前に行う待機と同じ手順で、
|
|
44
|
+
* `@wcstack/testing` の `mount()` もこれを呼ぶ(docs/app-testing-and-typescript-impl-plan.md D11)。
|
|
45
|
+
*
|
|
46
|
+
* 1. `static hasConnectedCallbackPromise = true` を持つ全要素の `connectedCallbackPromise`
|
|
47
|
+
* を待つ。待っている間に追加された要素も拾う(安定化ループ)。
|
|
48
|
+
* `<wcs-router>` の初期ルート適用・`<wcs-state>` の状態ロードはここで完了する。
|
|
49
|
+
* 2. `static getBindingsReady(root)` を持つクラス(`<wcs-state>`)の、この root に対する
|
|
50
|
+
* バインディング構築完了を待つ。Promise の取得はループの後 — 実体は各要素の
|
|
51
|
+
* connectedCallback 内・最初の await より後に登録されるため、先に掴むと「まだ
|
|
52
|
+
* 登録前」の即時解決 Promise を取り逃す。
|
|
53
|
+
*
|
|
54
|
+
* バインディング初期化の失敗は reject として伝わる(state v1.26+)。
|
|
55
|
+
*/
|
|
56
|
+
declare function waitForReady(root: ParentNode & Node, options?: WaitForReadyOptions): Promise<void>;
|
|
36
57
|
/**
|
|
37
58
|
* HTML 文字列を SSR レンダリングして返す。
|
|
38
59
|
*
|
|
@@ -211,5 +232,5 @@ declare class RenderCore extends EventTarget {
|
|
|
211
232
|
render(html: string): Promise<string | null>;
|
|
212
233
|
}
|
|
213
234
|
|
|
214
|
-
export { GLOBALS_KEYS, RenderCore, VERSION,
|
|
215
|
-
export type { BootstrapFunction, IWcBindable, IWcBindableProperty, RenderOptions, WcsRenderValues };
|
|
235
|
+
export { GLOBALS_KEYS, RenderCore, VERSION, installBaseUrl, installGlobals, renderToString, waitForReady };
|
|
236
|
+
export type { BootstrapFunction, IWcBindable, IWcBindableProperty, RenderOptions, WaitForReadyOptions, WcsRenderValues };
|
package/dist/index.esm.js
CHANGED
|
@@ -130,23 +130,49 @@ function installBaseUrl(baseUrl) {
|
|
|
130
130
|
globalThis.URL.revokeObjectURL = OrigURL.revokeObjectURL;
|
|
131
131
|
return () => { globalThis.URL = OrigURL; };
|
|
132
132
|
}
|
|
133
|
-
/** @deprecated Use Ssr.extractStateData() from @wcstack/state instead */
|
|
134
|
-
function extractStateData(stateEl) {
|
|
135
|
-
const raw = stateEl.__state;
|
|
136
|
-
if (!raw || typeof raw !== 'object')
|
|
137
|
-
return {};
|
|
138
|
-
const data = {};
|
|
139
|
-
for (const [key, value] of Object.entries(raw)) {
|
|
140
|
-
if (!key.startsWith('$') && typeof value !== 'function') {
|
|
141
|
-
data[key] = value;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return data;
|
|
145
|
-
}
|
|
146
133
|
async function loadDefaultBootstraps() {
|
|
147
134
|
const { bootstrapState } = await import('@wcstack/state');
|
|
148
135
|
return [bootstrapState];
|
|
149
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* `root`(document / ShadowRoot)配下のカスタム要素が readiness プロトコルに従って
|
|
139
|
+
* 初期化を終えるまで待つ。renderToString がシリアライズ前に行う待機と同じ手順で、
|
|
140
|
+
* `@wcstack/testing` の `mount()` もこれを呼ぶ(docs/app-testing-and-typescript-impl-plan.md D11)。
|
|
141
|
+
*
|
|
142
|
+
* 1. `static hasConnectedCallbackPromise = true` を持つ全要素の `connectedCallbackPromise`
|
|
143
|
+
* を待つ。待っている間に追加された要素も拾う(安定化ループ)。
|
|
144
|
+
* `<wcs-router>` の初期ルート適用・`<wcs-state>` の状態ロードはここで完了する。
|
|
145
|
+
* 2. `static getBindingsReady(root)` を持つクラス(`<wcs-state>`)の、この root に対する
|
|
146
|
+
* バインディング構築完了を待つ。Promise の取得はループの後 — 実体は各要素の
|
|
147
|
+
* connectedCallback 内・最初の await より後に登録されるため、先に掴むと「まだ
|
|
148
|
+
* 登録前」の即時解決 Promise を取り逃す。
|
|
149
|
+
*
|
|
150
|
+
* バインディング初期化の失敗は reject として伝わる(state v1.26+)。
|
|
151
|
+
*/
|
|
152
|
+
async function waitForReady(root, options) {
|
|
153
|
+
const maxIterations = options?.maxIterations ?? 10;
|
|
154
|
+
const awaitedElements = new WeakSet();
|
|
155
|
+
const readyCtors = new Set();
|
|
156
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
157
|
+
const connectedPromises = [];
|
|
158
|
+
for (const el of root.querySelectorAll('*-*')) {
|
|
159
|
+
if (awaitedElements.has(el))
|
|
160
|
+
continue;
|
|
161
|
+
const ctor = el.constructor;
|
|
162
|
+
if (ctor.hasConnectedCallbackPromise) {
|
|
163
|
+
awaitedElements.add(el);
|
|
164
|
+
connectedPromises.push(el.connectedCallbackPromise);
|
|
165
|
+
}
|
|
166
|
+
if (typeof ctor.getBindingsReady === 'function') {
|
|
167
|
+
readyCtors.add(ctor);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (connectedPromises.length === 0)
|
|
171
|
+
break;
|
|
172
|
+
await Promise.all(connectedPromises);
|
|
173
|
+
}
|
|
174
|
+
await Promise.all(Array.from(readyCtors, (ctor) => ctor.getBindingsReady(root)));
|
|
175
|
+
}
|
|
150
176
|
/**
|
|
151
177
|
* HTML 文字列を SSR レンダリングして返す。
|
|
152
178
|
*
|
|
@@ -289,38 +315,10 @@ async function renderToString(html, options) {
|
|
|
289
315
|
// HTML をパース
|
|
290
316
|
// connectedCallback が自動発火 → state ロード → $connectedCallback 実行
|
|
291
317
|
document.body.innerHTML = html;
|
|
292
|
-
// connectedCallbackPromise / getBindingsReady
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
const awaitedElements = new WeakSet();
|
|
297
|
-
const readyCtors = new Set();
|
|
298
|
-
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
|
299
|
-
const connectedPromises = [];
|
|
300
|
-
for (const el of document.querySelectorAll('*-*')) {
|
|
301
|
-
if (awaitedElements.has(el))
|
|
302
|
-
continue;
|
|
303
|
-
const ctor = el.constructor;
|
|
304
|
-
if (ctor.hasConnectedCallbackPromise) {
|
|
305
|
-
awaitedElements.add(el);
|
|
306
|
-
connectedPromises.push(el.connectedCallbackPromise);
|
|
307
|
-
}
|
|
308
|
-
if (typeof ctor.getBindingsReady === 'function') {
|
|
309
|
-
readyCtors.add(ctor);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
if (connectedPromises.length === 0)
|
|
313
|
-
break;
|
|
314
|
-
await Promise.all(connectedPromises);
|
|
315
|
-
}
|
|
316
|
-
// 非同期初期化(バインディング構築)の完了を待機。Promise の**取得**は
|
|
317
|
-
// 安定化ループの後に行う — getBindingsReady の実体(rootNode ごとの ready)は
|
|
318
|
-
// 各要素の connectedCallback 内・最初の await より後に登録されるため、ループ
|
|
319
|
-
// 初回の収集では「まだ登録前」の即時解決 Promise を掴むことがある。inline
|
|
320
|
-
// スナップショット時代は state の connectedCallback 自身が bindings を待って
|
|
321
|
-
// いたため隠れていた(orchestrated 化で顕在化)。取り逃すと構築の続きが
|
|
322
|
-
// グローバル復元後に走り、document 消失でクラッシュする
|
|
323
|
-
await Promise.all(Array.from(readyCtors, (ctor) => ctor.getBindingsReady(document)));
|
|
318
|
+
// connectedCallbackPromise / getBindingsReady プロトコルを自動検出して待つ
|
|
319
|
+
// (安定化ループ + バインディング構築。取り逃すと構築の続きがグローバル復元後に
|
|
320
|
+
// 走り、document 消失でクラッシュする — 手順の詳細は waitForReady 参照)
|
|
321
|
+
await waitForReady(document);
|
|
324
322
|
// スナップショット最終パス(orchestrated): 全要素の完了とバインディング構築の
|
|
325
323
|
// 後に <wcs-ssr> を生成する。inline 生成(connectedCallback 内)が取り逃がす
|
|
326
324
|
// 「後から挿入されたルート内容の構造テンプレート」も、この時点なら確定している
|
|
@@ -359,7 +357,7 @@ async function renderToString(html, options) {
|
|
|
359
357
|
}
|
|
360
358
|
}
|
|
361
359
|
|
|
362
|
-
var version = "
|
|
360
|
+
var version = "2.0.0";
|
|
363
361
|
var pkg = {
|
|
364
362
|
version: version};
|
|
365
363
|
|
|
@@ -422,5 +420,5 @@ class RenderCore extends EventTarget {
|
|
|
422
420
|
}
|
|
423
421
|
}
|
|
424
422
|
|
|
425
|
-
export { GLOBALS_KEYS, RenderCore, VERSION,
|
|
423
|
+
export { GLOBALS_KEYS, RenderCore, VERSION, installBaseUrl, installGlobals, renderToString, waitForReady };
|
|
426
424
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../../src/protocol/ssrSnapshot.ts","../../src/render.ts","../../src/version.ts","../../src/RenderCore.ts"],"sourcesContent":["// ===========================================================================\n// AUTO-GENERATED FILE - DO NOT EDIT.\n// Generated from /protocol/ssr-snapshot.ts by scripts/sync-protocol-types.mjs.\n// Run `node scripts/sync-protocol-types.mjs` after editing the source.\n// ===========================================================================\n\n// ssr-snapshot protocol — how the SSR renderer asks whoever owns reactive\n// state to build hydration snapshots (<wcs-ssr>) as a final pass, after every\n// DOM inserter (router route content, late custom elements) has settled.\n//\n// Without this, the snapshot is built inside <wcs-state>'s connectedCallback\n// and races DOM inserted by other packages: whether a route's structural\n// templates make it into the snapshot depends on document order and state's\n// load mechanism (docs/ssr-router-design.md §5).\n//\n// The provider (@wcstack/state) installs itself on a well-known global symbol\n// at bootstrap. The renderer (@wcstack/server) looks the builder up after\n// running bootstraps: if present it announces orchestration by setting\n// `data-wcs-server=\"orchestrated\"` on the document element BEFORE parsing, and\n// calls build() right before serialization. The provider keeps its inline\n// per-element fallback whenever the attribute value is anything else, so:\n// - old renderer + new provider -> inline build, yesterday's behavior\n// - new renderer + old provider -> no builder found, attribute stays \"\",\n// the old provider builds inline as before\n// - new renderer + new provider -> orchestrated: snapshots are built last\n// and therefore always see settled DOM\n//\n// The symbol (rather than a package import) also pins the builder to the state\n// copy that actually runs on the page — its module-scoped fragment registries\n// are the ones the snapshot must read.\n//\n// SINGLE SOURCE OF TRUTH: edit only this file (/protocol/ssr-snapshot.ts), then\n// run `node scripts/sync-protocol-types.mjs` to regenerate the per-package\n// copies (packages/<pkg>/src/protocol/ssrSnapshot.ts). Those copies are\n// generated — do not edit them.\n\n/**\n * Global key the snapshot builder installs itself under. `Symbol.for` so\n * independently loaded copies of this file (state's and server's) still agree.\n */\nexport const SSR_SNAPSHOT_BUILDER_KEY = Symbol.for(\"wcstack.ssr.snapshotBuilder\");\n\n/**\n * `data-wcs-server` attribute value announcing that the renderer will call the\n * builder as a final pass. Providers must skip their inline per-element build\n * when they see this value, and keep it for any other value (including \"\").\n */\nexport const SSR_ORCHESTRATED_VALUE = \"orchestrated\";\n\nexport interface IWcsSsrSnapshotBuilder {\n readonly protocol: \"wcs-ssr-snapshot\";\n /** Integer protocol version. All versions >= 1 are participant-compatible. */\n readonly version: number;\n /**\n * Build a hydration snapshot for every enable-ssr state element in `root`\n * that does not already have one. Called once per render, after all\n * waiting-protocol elements have settled and bindings are ready. Must be\n * idempotent with respect to already-snapshotted elements.\n */\n build(root: Document): void;\n}\n\n/**\n * The installed builder, or null when there is none or it speaks a shape this\n * reader does not.\n */\nexport function getSsrSnapshotBuilder(): IWcsSsrSnapshotBuilder | null {\n const candidate = (globalThis as Record<symbol, unknown>)[\n SSR_SNAPSHOT_BUILDER_KEY\n ] as IWcsSsrSnapshotBuilder | undefined;\n if (candidate === undefined || candidate === null) return null;\n if (candidate.protocol !== \"wcs-ssr-snapshot\") return null;\n if (typeof candidate.version !== \"number\" || candidate.version < 1) return null;\n if (typeof candidate.build !== \"function\") return null;\n return candidate;\n}\n","import { Window } from 'happy-dom';\nimport { getSsrSnapshotBuilder, SSR_ORCHESTRATED_VALUE } from './protocol/ssrSnapshot';\n\n/**\n * globalThis を差し替える renderToString の並列実行を防止する Mutex。\n * 同一 Node プロセス内で複数リクエストが同時に renderToString を呼んでも\n * シリアライズされ、グローバル状態の衝突を防ぐ。\n */\nclass Mutex {\n private _queue: (() => void)[] = [];\n private _locked = false;\n\n async acquire(): Promise<() => void> {\n if (this._locked) {\n await new Promise<void>(resolve => this._queue.push(resolve));\n }\n this._locked = true;\n return () => {\n this._locked = false;\n this._queue.shift()?.();\n };\n }\n}\n\nconst renderMutex = new Mutex();\n\nexport const GLOBALS_KEYS = [\n 'document', 'customElements', 'HTMLElement',\n 'DocumentFragment', 'Node', 'NodeFilter', 'Comment', 'Text',\n 'MutationObserver', 'ShadowRoot', 'Element', 'HTMLTemplateElement',\n // URL を持つコンポーネント(@wcstack/router 等)が window.location /\n // history を読めるようにする(docs/ssr-router-design.md §3.1)。\n 'window', 'location', 'history',\n // コンポーネントが発火するイベントをレンダリングウィンドウの realm に揃える。\n // Node ネイティブの CustomEvent は happy-dom の EventTarget に拒否される\n // (\"parameter 1 is not of type 'Event'\")。vitest の happy-dom 環境では\n // グローバルが happy-dom 側なので隠れ、素の Node サーバーでだけ顕在化する\n 'Event', 'CustomEvent',\n];\n\nexport function installGlobals(window: Window): () => void {\n const saved: Record<string, any> = {};\n for (const key of GLOBALS_KEYS) {\n saved[key] = (globalThis as any)[key];\n (globalThis as any)[key] = (window as any)[key];\n }\n // URL.createObjectURL を無効化して、\n // loadFromInnerScript が base64 data: URL フォールバックを使うようにする\n const origCreateObjectURL = URL.createObjectURL;\n (URL as any).createObjectURL = undefined;\n return () => {\n URL.createObjectURL = origCreateObjectURL;\n for (const key of GLOBALS_KEYS) {\n (globalThis as any)[key] = saved[key];\n }\n };\n}\n\nexport function installBaseUrl(baseUrl: string): () => void {\n const OrigURL = globalThis.URL;\n const base = baseUrl;\n globalThis.URL = class extends OrigURL {\n constructor(input: string | URL, inputBase?: string | URL) {\n if (typeof input === 'string' && input.startsWith('/') && inputBase === undefined) {\n super(input, base);\n } else {\n super(input as string, inputBase);\n }\n }\n } as typeof URL;\n // 静的メソッドを引き継ぐ\n globalThis.URL.createObjectURL = OrigURL.createObjectURL;\n globalThis.URL.revokeObjectURL = OrigURL.revokeObjectURL;\n return () => { globalThis.URL = OrigURL; };\n}\n\n/** @deprecated Use Ssr.extractStateData() from @wcstack/state instead */\nexport function extractStateData(stateEl: any): Record<string, any> {\n const raw = (stateEl as any).__state;\n if (!raw || typeof raw !== 'object') return {};\n const data: Record<string, any> = {};\n for (const [key, value] of Object.entries(raw)) {\n if (!key.startsWith('$') && typeof value !== 'function') {\n data[key] = value;\n }\n }\n return data;\n}\n\n/**\n * 同期の bootstrap 関数、または非同期ローダー。\n * `HTMLElement` を継承するクラスはモジュール評価時にグローバルの `HTMLElement` を\n * 参照するため、純 Node 環境ではトップレベル import できないパッケージがある。\n * その場合は `async () => (await import('@wcstack/router')).bootstrapRouter()` の\n * ように非同期ローダーを渡す — 呼び出しは installGlobals の後なので、モジュール\n * 評価時にはグローバルが揃っている(docs/ssr-router-design.md §3.1)。\n */\nexport type BootstrapFunction = () => void | Promise<void>;\n\nexport interface RenderOptions {\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\")。省略時は `url` の origin */\n baseUrl?: string;\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\n bootstraps?: BootstrapFunction[];\n /**\n * このリクエストの完全 URL (例: \"http://localhost:3000/products/1\")。\n * `window.location` / `document.baseURI` に反映される。ルーティングする\n * コンポーネント(@wcstack/router 等)のサーバーレンダリングに必要\n * (docs/ssr-router-design.md §3.1)。\n */\n url?: string;\n /**\n * `<head>` へ注入する `<base href>` の値。`url` 指定時の既定は \"/\"。\n * ブラウザで `<base>` を置く SPA と同じ条件をサーバー内に再現する\n * (深い URL での basename 誤認を防ぐ)。サブパス配備では明示する。\n */\n baseHref?: string;\n}\n\nasync function loadDefaultBootstraps(): Promise<BootstrapFunction[]> {\n const { bootstrapState } = await import('@wcstack/state');\n return [bootstrapState];\n}\n\n\n/**\n * HTML 文字列を SSR レンダリングして返す。\n *\n * ## 入力 HTML のルール\n * - `<body>` の中身だけを渡す(`<html>`, `<head>`, `<body>` タグは含めない)\n * - `<script>` / `<link>` による外部リソース読み込みは実行されない\n * → 必要なパッケージは `options.bootstraps` で明示的に渡す\n *\n * ## SSR でできること\n *\n * ### 状態の初期化とデータ取得\n * - `<wcs-state>` の状態ロード(json 属性, src 属性, inline `<script type=\"module\">`)\n * - `$connectedCallback` でのサーバーサイド fetch(API 呼び出し、DB 問い合わせ等)\n *\n * ```html\n * <!-- JSON 直接指定 -->\n * <wcs-state enable-ssr json='{\"title\":\"Hello\"}'></wcs-state>\n *\n * <!-- $connectedCallback で API からデータ取得 -->\n * <!-- $connectedCallback は状態オブジェクトのメソッドとして定義し、this が state proxy -->\n * <wcs-state enable-ssr>\n * <script type=\"module\">\n * export default {\n * async $connectedCallback() {\n * const res = await fetch('/api/users');\n * this.users = await res.json();\n * }\n * };\n * </script>\n * </wcs-state>\n * ```\n *\n * ### wcs-fetch を使ったサーバー通信\n * - `<wcs-fetch>` の auto-fetch(`manual` なし)はサーバーでも実行される\n * - `manual` + `$connectedCallback` で明示的に制御する場合:\n *\n * ```html\n * <wcs-fetch id=\"api\" url=\"/api/users\" manual></wcs-fetch>\n * <wcs-state enable-ssr>\n * <script type=\"module\">\n * export default {\n * async $connectedCallback() {\n * const el = document.getElementById('api');\n * this.users = await el.fetch();\n * }\n * };\n * </script>\n * </wcs-state>\n * ```\n * ※ `bootstraps` に `bootstrapFetch` を含める必要あり\n *\n * ### バインディングと構造レンダリング\n * - `data-wcs` バインディングの適用(text, attribute, class, style, property)\n * - `<template data-wcs=\"for:\">` / `if:` / `elseif:` / `else:` の構造レンダリング\n *\n * ```html\n * <ul>\n * <template data-wcs=\"for: users\">\n * <li data-wcs=\"textContent: .name\"></li>\n * </template>\n * </ul>\n * <template data-wcs=\"if: isAdmin\">\n * <div class=\"admin-panel\">...</div>\n * </template>\n * ```\n *\n * ### ハイドレーション\n * - `enable-ssr` 付き `<wcs-state>` の `<wcs-ssr>` メタデータ自動生成\n * - クライアント側でのハイドレーション(再レンダリングなしでバインディング復元)\n * - `enable-ssr` を外した `<wcs-state>` はクライアントのみで動作(部分 CSR)\n *\n * ### カスタム要素の待機\n * - `static hasConnectedCallbackPromise = true` プロトコル準拠の全カスタム要素を自動待機\n * - `$connectedCallback` 中に動的追加されたカスタム要素も安定化ループで検出・待機(最大 10 回)\n *\n * ### router SSR\n * - `<wcs-router enable-ssr>` + `url` オプションで初期ルートをサーバー描画。\n * クライアント側 router は描画済み DOM を採用(adopt)する。\n * 詳細は README「Router SSR」/ docs/ssr-router-design.md\n *\n * ## SSR でできないこと\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\n * - guard 付きルートのサーバー描画(設計上・クライアントで guard 実行)、\n * `<wcs-layout>` ルートの採用(クライアント描画へフォールバック)、\n * `<wcs-head>` のサーバー反映(body のみの出力に head は載らない)\n *\n * ## HTML の分割パターン\n * ```\n * // server.js\n * const ssrBody = await renderToString(template, { ... });\n * const page = `<!DOCTYPE html>\n * <html lang=\"ja\">\n * <head>\n * <script type=\"module\" src=\"/packages/state/dist/auto.min.js\"></script>\n * </head>\n * <body>${ssrBody}</body>\n * </html>`;\n * ```\n * `renderToString` には `<body>` の中身だけを渡し、\n * `<head>` や `<script>` タグは外側のテンプレートで囲む。\n */\nexport async function renderToString(html: string, options?: RenderOptions): Promise<string> {\n // globalThis を差し替えるため、同時に1つしか実行できない\n const releaseMutex = await renderMutex.acquire();\n\n const window = options?.url ? new Window({ url: options.url }) : new Window();\n const restoreGlobals = installGlobals(window);\n const document = window.document;\n\n let restoreBaseUrl: (() => void) | null = null;\n\n try {\n // url 指定時は <base href> を注入する(既定 \"/\")。ブラウザで <base> を置く\n // SPA と同じ条件を再現し、深い URL での basename 誤認を防ぐ\n // (docs/ssr-router-design.md §3.1)。\n if (options?.url !== undefined || options?.baseHref !== undefined) {\n const base = document.createElement('base');\n base.setAttribute('href', options.baseHref ?? '/');\n document.head.appendChild(base);\n }\n\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール。\n // baseUrl 省略時は url の origin を既定にする。\n const effectiveBaseUrl =\n options?.baseUrl ?? (options?.url ? new URL(options.url).origin : undefined);\n restoreBaseUrl = effectiveBaseUrl\n ? installBaseUrl(effectiveBaseUrl)\n : null;\n\n // bootstrap の解決。非同期ローダー(BootstrapFunction 参照)を許容するため\n // await する。try 内で行うのは、throw 時にもグローバル復元を保証するため。\n const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();\n\n for (const bootstrap of bootstraps) {\n await bootstrap();\n }\n\n // SSR モードを html 要素に設定。snapshot builder(bootstraps の実行が\n // 登録し得る — ssr-snapshot プロトコル)が居れば orchestrated を宣言し、\n // <wcs-ssr> 生成をサーバー主導の最終パスへ回す(docs/ssr-router-design.md §5)。\n // 値の宣言はパースより前 — 各要素は connectedCallback で値を読むため\n const snapshotBuilder = getSsrSnapshotBuilder();\n document.documentElement.setAttribute(\n 'data-wcs-server',\n snapshotBuilder !== null ? SSR_ORCHESTRATED_VALUE : ''\n );\n\n // HTML をパース\n // connectedCallback が自動発火 → state ロード → $connectedCallback 実行\n document.body.innerHTML = html;\n\n // connectedCallbackPromise / getBindingsReady プロトコルを自動検出\n // $connectedCallback が動的にカスタム要素を追加する場合があるため、\n // 新しい要素が見つからなくなるまで走査を繰り返す(安定化ループ)\n const MAX_ITERATIONS = 10;\n const awaitedElements = new WeakSet();\n const readyCtors = new Set<any>();\n\n for (let i = 0; i < MAX_ITERATIONS; i++) {\n const connectedPromises: Promise<void>[] = [];\n\n for (const el of document.querySelectorAll('*-*')) {\n if (awaitedElements.has(el)) continue;\n const ctor = el.constructor as any;\n if (ctor.hasConnectedCallbackPromise) {\n awaitedElements.add(el);\n connectedPromises.push((el as any).connectedCallbackPromise);\n }\n if (typeof ctor.getBindingsReady === 'function') {\n readyCtors.add(ctor);\n }\n }\n\n if (connectedPromises.length === 0) break;\n await Promise.all(connectedPromises);\n }\n\n // 非同期初期化(バインディング構築)の完了を待機。Promise の**取得**は\n // 安定化ループの後に行う — getBindingsReady の実体(rootNode ごとの ready)は\n // 各要素の connectedCallback 内・最初の await より後に登録されるため、ループ\n // 初回の収集では「まだ登録前」の即時解決 Promise を掴むことがある。inline\n // スナップショット時代は state の connectedCallback 自身が bindings を待って\n // いたため隠れていた(orchestrated 化で顕在化)。取り逃すと構築の続きが\n // グローバル復元後に走り、document 消失でクラッシュする\n await Promise.all(\n Array.from(readyCtors, (ctor) => (ctor as { getBindingsReady(root: Node): Promise<void> }).getBindingsReady(document as unknown as Node))\n );\n\n // スナップショット最終パス(orchestrated): 全要素の完了とバインディング構築の\n // 後に <wcs-ssr> を生成する。inline 生成(connectedCallback 内)が取り逃がす\n // 「後から挿入されたルート内容の構造テンプレート」も、この時点なら確定している\n snapshotBuilder?.build(document as unknown as Document);\n\n return document.body.innerHTML;\n } finally {\n // エラー経路でも進行中のバインディング構築を待ってから globals を戻す。\n // 構築は要素の connectedCallback とは独立した microtask 連鎖で走るため、\n // 待たずに戻すと続きが document 消失で unhandled になりプロセスを落とす。\n // 後始末はベストエフォート(rejected も含めて待つだけ待つ)\n try {\n const readyPending: Promise<void>[] = [];\n for (const el of document.querySelectorAll('*-*')) {\n const ctor = el.constructor as { getBindingsReady?(root: Node): Promise<void> };\n if (typeof ctor.getBindingsReady === 'function') {\n readyPending.push(ctor.getBindingsReady(document as unknown as Node));\n }\n }\n await Promise.allSettled(readyPending);\n } catch { /* best effort */ }\n // binder プロトコルの保留キュー(Symbol.for なので installGlobals の restore\n // 対象外=プロセス寿命)を空にする。state を読み込まないページで挿入側\n // (router 等)が差し出したノードは引き取り手が現れないまま蓄積するため、\n // レンダリングごとに後始末する(docs/ssr-router-design.md §3.1)。\n // これはプロトコルの公開シンボル面であり、パッケージ内部への依存ではない。\n const pendingBinds = (globalThis as Record<symbol, unknown>)[\n Symbol.for('wcstack.binder.pending')\n ];\n if (Array.isArray(pendingBinds)) {\n pendingBinds.length = 0;\n }\n restoreBaseUrl?.();\n restoreGlobals();\n await window.close();\n releaseMutex();\n }\n}\n","import pkg from \"../package.json\" with { type: \"json\" };\nexport const VERSION = pkg.version;\n","import { renderToString } from \"./render.js\";\nimport { IWcBindable } from \"./types.js\";\n\nexport class RenderCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"html\", event: \"wcs-render:html-changed\", semantics: \"state\" },\n { name: \"loading\", event: \"wcs-render:loading-changed\", semantics: \"state\" },\n { name: \"error\", event: \"wcs-render:error\", semantics: \"state\" },\n ],\n };\n\n private _html: string | null = null;\n private _loading: boolean = false;\n private _error: Error | null = null;\n\n get html(): string | null {\n return this._html;\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n private _setLoading(loading: boolean): void {\n this._loading = loading;\n this.dispatchEvent(new CustomEvent(\"wcs-render:loading-changed\", {\n detail: loading,\n }));\n }\n\n private _setHtml(html: string): void {\n this._html = html;\n this.dispatchEvent(new CustomEvent(\"wcs-render:html-changed\", {\n detail: html,\n }));\n }\n\n private _setError(error: Error | null): void {\n this._error = error;\n this.dispatchEvent(new CustomEvent(\"wcs-render:error\", {\n detail: error,\n }));\n }\n\n async render(html: string): Promise<string | null> {\n this._setLoading(true);\n this._setError(null);\n\n try {\n const result = await renderToString(html);\n this._setHtml(result);\n this._setLoading(false);\n return this._html;\n } catch (e: any) {\n this._setError(e instanceof Error ? e : new Error(String(e)));\n this._setLoading(false);\n return null;\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../../src/protocol/ssrSnapshot.ts","../../src/render.ts","../../src/version.ts","../../src/RenderCore.ts"],"sourcesContent":["// ===========================================================================\n// AUTO-GENERATED FILE - DO NOT EDIT.\n// Generated from /protocol/ssr-snapshot.ts by scripts/sync-protocol-types.mjs.\n// Run `node scripts/sync-protocol-types.mjs` after editing the source.\n// ===========================================================================\n\n// ssr-snapshot protocol — how the SSR renderer asks whoever owns reactive\n// state to build hydration snapshots (<wcs-ssr>) as a final pass, after every\n// DOM inserter (router route content, late custom elements) has settled.\n//\n// Without this, the snapshot is built inside <wcs-state>'s connectedCallback\n// and races DOM inserted by other packages: whether a route's structural\n// templates make it into the snapshot depends on document order and state's\n// load mechanism (docs/ssr-router-design.md §5).\n//\n// The provider (@wcstack/state) installs itself on a well-known global symbol\n// at bootstrap. The renderer (@wcstack/server) looks the builder up after\n// running bootstraps: if present it announces orchestration by setting\n// `data-wcs-server=\"orchestrated\"` on the document element BEFORE parsing, and\n// calls build() right before serialization. The provider keeps its inline\n// per-element fallback whenever the attribute value is anything else, so:\n// - old renderer + new provider -> inline build, yesterday's behavior\n// - new renderer + old provider -> no builder found, attribute stays \"\",\n// the old provider builds inline as before\n// - new renderer + new provider -> orchestrated: snapshots are built last\n// and therefore always see settled DOM\n//\n// The symbol (rather than a package import) also pins the builder to the state\n// copy that actually runs on the page — its module-scoped fragment registries\n// are the ones the snapshot must read.\n//\n// SINGLE SOURCE OF TRUTH: edit only this file (/protocol/ssr-snapshot.ts), then\n// run `node scripts/sync-protocol-types.mjs` to regenerate the per-package\n// copies (packages/<pkg>/src/protocol/ssrSnapshot.ts). Those copies are\n// generated — do not edit them.\n\n/**\n * Global key the snapshot builder installs itself under. `Symbol.for` so\n * independently loaded copies of this file (state's and server's) still agree.\n */\nexport const SSR_SNAPSHOT_BUILDER_KEY = Symbol.for(\"wcstack.ssr.snapshotBuilder\");\n\n/**\n * `data-wcs-server` attribute value announcing that the renderer will call the\n * builder as a final pass. Providers must skip their inline per-element build\n * when they see this value, and keep it for any other value (including \"\").\n */\nexport const SSR_ORCHESTRATED_VALUE = \"orchestrated\";\n\nexport interface IWcsSsrSnapshotBuilder {\n readonly protocol: \"wcs-ssr-snapshot\";\n /** Integer protocol version. All versions >= 1 are participant-compatible. */\n readonly version: number;\n /**\n * Build a hydration snapshot for every enable-ssr state element in `root`\n * that does not already have one. Called once per render, after all\n * waiting-protocol elements have settled and bindings are ready. Must be\n * idempotent with respect to already-snapshotted elements.\n */\n build(root: Document): void;\n}\n\n/**\n * The installed builder, or null when there is none or it speaks a shape this\n * reader does not.\n */\nexport function getSsrSnapshotBuilder(): IWcsSsrSnapshotBuilder | null {\n const candidate = (globalThis as Record<symbol, unknown>)[\n SSR_SNAPSHOT_BUILDER_KEY\n ] as IWcsSsrSnapshotBuilder | undefined;\n if (candidate === undefined || candidate === null) return null;\n if (candidate.protocol !== \"wcs-ssr-snapshot\") return null;\n if (typeof candidate.version !== \"number\" || candidate.version < 1) return null;\n if (typeof candidate.build !== \"function\") return null;\n return candidate;\n}\n","import { Window } from 'happy-dom';\nimport { getSsrSnapshotBuilder, SSR_ORCHESTRATED_VALUE } from './protocol/ssrSnapshot';\n\n/**\n * globalThis を差し替える renderToString の並列実行を防止する Mutex。\n * 同一 Node プロセス内で複数リクエストが同時に renderToString を呼んでも\n * シリアライズされ、グローバル状態の衝突を防ぐ。\n */\nclass Mutex {\n private _queue: (() => void)[] = [];\n private _locked = false;\n\n async acquire(): Promise<() => void> {\n if (this._locked) {\n await new Promise<void>(resolve => this._queue.push(resolve));\n }\n this._locked = true;\n return () => {\n this._locked = false;\n this._queue.shift()?.();\n };\n }\n}\n\nconst renderMutex = new Mutex();\n\nexport const GLOBALS_KEYS = [\n 'document', 'customElements', 'HTMLElement',\n 'DocumentFragment', 'Node', 'NodeFilter', 'Comment', 'Text',\n 'MutationObserver', 'ShadowRoot', 'Element', 'HTMLTemplateElement',\n // URL を持つコンポーネント(@wcstack/router 等)が window.location /\n // history を読めるようにする(docs/ssr-router-design.md §3.1)。\n 'window', 'location', 'history',\n // コンポーネントが発火するイベントをレンダリングウィンドウの realm に揃える。\n // Node ネイティブの CustomEvent は happy-dom の EventTarget に拒否される\n // (\"parameter 1 is not of type 'Event'\")。vitest の happy-dom 環境では\n // グローバルが happy-dom 側なので隠れ、素の Node サーバーでだけ顕在化する\n 'Event', 'CustomEvent',\n];\n\nexport function installGlobals(window: Window): () => void {\n const saved: Record<string, any> = {};\n for (const key of GLOBALS_KEYS) {\n saved[key] = (globalThis as any)[key];\n (globalThis as any)[key] = (window as any)[key];\n }\n // URL.createObjectURL を無効化して、\n // loadFromInnerScript が base64 data: URL フォールバックを使うようにする\n const origCreateObjectURL = URL.createObjectURL;\n (URL as any).createObjectURL = undefined;\n return () => {\n URL.createObjectURL = origCreateObjectURL;\n for (const key of GLOBALS_KEYS) {\n (globalThis as any)[key] = saved[key];\n }\n };\n}\n\nexport function installBaseUrl(baseUrl: string): () => void {\n const OrigURL = globalThis.URL;\n const base = baseUrl;\n globalThis.URL = class extends OrigURL {\n constructor(input: string | URL, inputBase?: string | URL) {\n if (typeof input === 'string' && input.startsWith('/') && inputBase === undefined) {\n super(input, base);\n } else {\n super(input as string, inputBase);\n }\n }\n } as typeof URL;\n // 静的メソッドを引き継ぐ\n globalThis.URL.createObjectURL = OrigURL.createObjectURL;\n globalThis.URL.revokeObjectURL = OrigURL.revokeObjectURL;\n return () => { globalThis.URL = OrigURL; };\n}\n\n/**\n * 同期の bootstrap 関数、または非同期ローダー。\n * `HTMLElement` を継承するクラスはモジュール評価時にグローバルの `HTMLElement` を\n * 参照するため、純 Node 環境ではトップレベル import できないパッケージがある。\n * その場合は `async () => (await import('@wcstack/router')).bootstrapRouter()` の\n * ように非同期ローダーを渡す — 呼び出しは installGlobals の後なので、モジュール\n * 評価時にはグローバルが揃っている(docs/ssr-router-design.md §3.1)。\n */\nexport type BootstrapFunction = () => void | Promise<void>;\n\nexport interface RenderOptions {\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\")。省略時は `url` の origin */\n baseUrl?: string;\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\n bootstraps?: BootstrapFunction[];\n /**\n * このリクエストの完全 URL (例: \"http://localhost:3000/products/1\")。\n * `window.location` / `document.baseURI` に反映される。ルーティングする\n * コンポーネント(@wcstack/router 等)のサーバーレンダリングに必要\n * (docs/ssr-router-design.md §3.1)。\n */\n url?: string;\n /**\n * `<head>` へ注入する `<base href>` の値。`url` 指定時の既定は \"/\"。\n * ブラウザで `<base>` を置く SPA と同じ条件をサーバー内に再現する\n * (深い URL での basename 誤認を防ぐ)。サブパス配備では明示する。\n */\n baseHref?: string;\n}\n\nasync function loadDefaultBootstraps(): Promise<BootstrapFunction[]> {\n const { bootstrapState } = await import('@wcstack/state');\n return [bootstrapState];\n}\n\nexport interface WaitForReadyOptions {\n /**\n * 安定化ループの上限。`$connectedCallback` が動的に追加した要素を拾うため、\n * 新しい要素が見つからなくなるまで走査を繰り返す(既定 10)。\n */\n maxIterations?: number;\n}\n\n/**\n * `root`(document / ShadowRoot)配下のカスタム要素が readiness プロトコルに従って\n * 初期化を終えるまで待つ。renderToString がシリアライズ前に行う待機と同じ手順で、\n * `@wcstack/testing` の `mount()` もこれを呼ぶ(docs/app-testing-and-typescript-impl-plan.md D11)。\n *\n * 1. `static hasConnectedCallbackPromise = true` を持つ全要素の `connectedCallbackPromise`\n * を待つ。待っている間に追加された要素も拾う(安定化ループ)。\n * `<wcs-router>` の初期ルート適用・`<wcs-state>` の状態ロードはここで完了する。\n * 2. `static getBindingsReady(root)` を持つクラス(`<wcs-state>`)の、この root に対する\n * バインディング構築完了を待つ。Promise の取得はループの後 — 実体は各要素の\n * connectedCallback 内・最初の await より後に登録されるため、先に掴むと「まだ\n * 登録前」の即時解決 Promise を取り逃す。\n *\n * バインディング初期化の失敗は reject として伝わる(state v1.26+)。\n */\nexport async function waitForReady(root: ParentNode & Node, options?: WaitForReadyOptions): Promise<void> {\n const maxIterations = options?.maxIterations ?? 10;\n const awaitedElements = new WeakSet<Element>();\n const readyCtors = new Set<{ getBindingsReady(root: Node): Promise<void> }>();\n\n for (let i = 0; i < maxIterations; i++) {\n const connectedPromises: Promise<void>[] = [];\n\n for (const el of root.querySelectorAll('*-*')) {\n if (awaitedElements.has(el)) continue;\n const ctor = el.constructor as any;\n if (ctor.hasConnectedCallbackPromise) {\n awaitedElements.add(el);\n connectedPromises.push((el as any).connectedCallbackPromise);\n }\n if (typeof ctor.getBindingsReady === 'function') {\n readyCtors.add(ctor);\n }\n }\n\n if (connectedPromises.length === 0) break;\n await Promise.all(connectedPromises);\n }\n\n await Promise.all(Array.from(readyCtors, (ctor) => ctor.getBindingsReady(root)));\n}\n\n\n/**\n * HTML 文字列を SSR レンダリングして返す。\n *\n * ## 入力 HTML のルール\n * - `<body>` の中身だけを渡す(`<html>`, `<head>`, `<body>` タグは含めない)\n * - `<script>` / `<link>` による外部リソース読み込みは実行されない\n * → 必要なパッケージは `options.bootstraps` で明示的に渡す\n *\n * ## SSR でできること\n *\n * ### 状態の初期化とデータ取得\n * - `<wcs-state>` の状態ロード(json 属性, src 属性, inline `<script type=\"module\">`)\n * - `$connectedCallback` でのサーバーサイド fetch(API 呼び出し、DB 問い合わせ等)\n *\n * ```html\n * <!-- JSON 直接指定 -->\n * <wcs-state enable-ssr json='{\"title\":\"Hello\"}'></wcs-state>\n *\n * <!-- $connectedCallback で API からデータ取得 -->\n * <!-- $connectedCallback は状態オブジェクトのメソッドとして定義し、this が state proxy -->\n * <wcs-state enable-ssr>\n * <script type=\"module\">\n * export default {\n * async $connectedCallback() {\n * const res = await fetch('/api/users');\n * this.users = await res.json();\n * }\n * };\n * </script>\n * </wcs-state>\n * ```\n *\n * ### wcs-fetch を使ったサーバー通信\n * - `<wcs-fetch>` の auto-fetch(`manual` なし)はサーバーでも実行される\n * - `manual` + `$connectedCallback` で明示的に制御する場合:\n *\n * ```html\n * <wcs-fetch id=\"api\" url=\"/api/users\" manual></wcs-fetch>\n * <wcs-state enable-ssr>\n * <script type=\"module\">\n * export default {\n * async $connectedCallback() {\n * const el = document.getElementById('api');\n * this.users = await el.fetch();\n * }\n * };\n * </script>\n * </wcs-state>\n * ```\n * ※ `bootstraps` に `bootstrapFetch` を含める必要あり\n *\n * ### バインディングと構造レンダリング\n * - `data-wcs` バインディングの適用(text, attribute, class, style, property)\n * - `<template data-wcs=\"for:\">` / `if:` / `elseif:` / `else:` の構造レンダリング\n *\n * ```html\n * <ul>\n * <template data-wcs=\"for: users\">\n * <li data-wcs=\"textContent: .name\"></li>\n * </template>\n * </ul>\n * <template data-wcs=\"if: isAdmin\">\n * <div class=\"admin-panel\">...</div>\n * </template>\n * ```\n *\n * ### ハイドレーション\n * - `enable-ssr` 付き `<wcs-state>` の `<wcs-ssr>` メタデータ自動生成\n * - クライアント側でのハイドレーション(再レンダリングなしでバインディング復元)\n * - `enable-ssr` を外した `<wcs-state>` はクライアントのみで動作(部分 CSR)\n *\n * ### カスタム要素の待機\n * - `static hasConnectedCallbackPromise = true` プロトコル準拠の全カスタム要素を自動待機\n * - `$connectedCallback` 中に動的追加されたカスタム要素も安定化ループで検出・待機(最大 10 回)\n *\n * ### router SSR\n * - `<wcs-router enable-ssr>` + `url` オプションで初期ルートをサーバー描画。\n * クライアント側 router は描画済み DOM を採用(adopt)する。\n * 詳細は README「Router SSR」/ docs/ssr-router-design.md\n *\n * ## SSR でできないこと\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\n * - guard 付きルートのサーバー描画(設計上・クライアントで guard 実行)、\n * `<wcs-layout>` ルートの採用(クライアント描画へフォールバック)、\n * `<wcs-head>` のサーバー反映(body のみの出力に head は載らない)\n *\n * ## HTML の分割パターン\n * ```\n * // server.js\n * const ssrBody = await renderToString(template, { ... });\n * const page = `<!DOCTYPE html>\n * <html lang=\"ja\">\n * <head>\n * <script type=\"module\" src=\"/packages/state/dist/auto.min.js\"></script>\n * </head>\n * <body>${ssrBody}</body>\n * </html>`;\n * ```\n * `renderToString` には `<body>` の中身だけを渡し、\n * `<head>` や `<script>` タグは外側のテンプレートで囲む。\n */\nexport async function renderToString(html: string, options?: RenderOptions): Promise<string> {\n // globalThis を差し替えるため、同時に1つしか実行できない\n const releaseMutex = await renderMutex.acquire();\n\n const window = options?.url ? new Window({ url: options.url }) : new Window();\n const restoreGlobals = installGlobals(window);\n const document = window.document;\n\n let restoreBaseUrl: (() => void) | null = null;\n\n try {\n // url 指定時は <base href> を注入する(既定 \"/\")。ブラウザで <base> を置く\n // SPA と同じ条件を再現し、深い URL での basename 誤認を防ぐ\n // (docs/ssr-router-design.md §3.1)。\n if (options?.url !== undefined || options?.baseHref !== undefined) {\n const base = document.createElement('base');\n base.setAttribute('href', options.baseHref ?? '/');\n document.head.appendChild(base);\n }\n\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール。\n // baseUrl 省略時は url の origin を既定にする。\n const effectiveBaseUrl =\n options?.baseUrl ?? (options?.url ? new URL(options.url).origin : undefined);\n restoreBaseUrl = effectiveBaseUrl\n ? installBaseUrl(effectiveBaseUrl)\n : null;\n\n // bootstrap の解決。非同期ローダー(BootstrapFunction 参照)を許容するため\n // await する。try 内で行うのは、throw 時にもグローバル復元を保証するため。\n const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();\n\n for (const bootstrap of bootstraps) {\n await bootstrap();\n }\n\n // SSR モードを html 要素に設定。snapshot builder(bootstraps の実行が\n // 登録し得る — ssr-snapshot プロトコル)が居れば orchestrated を宣言し、\n // <wcs-ssr> 生成をサーバー主導の最終パスへ回す(docs/ssr-router-design.md §5)。\n // 値の宣言はパースより前 — 各要素は connectedCallback で値を読むため\n const snapshotBuilder = getSsrSnapshotBuilder();\n document.documentElement.setAttribute(\n 'data-wcs-server',\n snapshotBuilder !== null ? SSR_ORCHESTRATED_VALUE : ''\n );\n\n // HTML をパース\n // connectedCallback が自動発火 → state ロード → $connectedCallback 実行\n document.body.innerHTML = html;\n\n // connectedCallbackPromise / getBindingsReady プロトコルを自動検出して待つ\n // (安定化ループ + バインディング構築。取り逃すと構築の続きがグローバル復元後に\n // 走り、document 消失でクラッシュする — 手順の詳細は waitForReady 参照)\n await waitForReady(document as unknown as ParentNode & Node);\n\n // スナップショット最終パス(orchestrated): 全要素の完了とバインディング構築の\n // 後に <wcs-ssr> を生成する。inline 生成(connectedCallback 内)が取り逃がす\n // 「後から挿入されたルート内容の構造テンプレート」も、この時点なら確定している\n snapshotBuilder?.build(document as unknown as Document);\n\n return document.body.innerHTML;\n } finally {\n // エラー経路でも進行中のバインディング構築を待ってから globals を戻す。\n // 構築は要素の connectedCallback とは独立した microtask 連鎖で走るため、\n // 待たずに戻すと続きが document 消失で unhandled になりプロセスを落とす。\n // 後始末はベストエフォート(rejected も含めて待つだけ待つ)\n try {\n const readyPending: Promise<void>[] = [];\n for (const el of document.querySelectorAll('*-*')) {\n const ctor = el.constructor as { getBindingsReady?(root: Node): Promise<void> };\n if (typeof ctor.getBindingsReady === 'function') {\n readyPending.push(ctor.getBindingsReady(document as unknown as Node));\n }\n }\n await Promise.allSettled(readyPending);\n } catch { /* best effort */ }\n // binder プロトコルの保留キュー(Symbol.for なので installGlobals の restore\n // 対象外=プロセス寿命)を空にする。state を読み込まないページで挿入側\n // (router 等)が差し出したノードは引き取り手が現れないまま蓄積するため、\n // レンダリングごとに後始末する(docs/ssr-router-design.md §3.1)。\n // これはプロトコルの公開シンボル面であり、パッケージ内部への依存ではない。\n const pendingBinds = (globalThis as Record<symbol, unknown>)[\n Symbol.for('wcstack.binder.pending')\n ];\n if (Array.isArray(pendingBinds)) {\n pendingBinds.length = 0;\n }\n restoreBaseUrl?.();\n restoreGlobals();\n await window.close();\n releaseMutex();\n }\n}\n","import pkg from \"../package.json\" with { type: \"json\" };\nexport const VERSION = pkg.version;\n","import { renderToString } from \"./render.js\";\nimport { IWcBindable } from \"./types.js\";\n\nexport class RenderCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"html\", event: \"wcs-render:html-changed\", semantics: \"state\" },\n { name: \"loading\", event: \"wcs-render:loading-changed\", semantics: \"state\" },\n { name: \"error\", event: \"wcs-render:error\", semantics: \"state\" },\n ],\n };\n\n private _html: string | null = null;\n private _loading: boolean = false;\n private _error: Error | null = null;\n\n get html(): string | null {\n return this._html;\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n private _setLoading(loading: boolean): void {\n this._loading = loading;\n this.dispatchEvent(new CustomEvent(\"wcs-render:loading-changed\", {\n detail: loading,\n }));\n }\n\n private _setHtml(html: string): void {\n this._html = html;\n this.dispatchEvent(new CustomEvent(\"wcs-render:html-changed\", {\n detail: html,\n }));\n }\n\n private _setError(error: Error | null): void {\n this._error = error;\n this.dispatchEvent(new CustomEvent(\"wcs-render:error\", {\n detail: error,\n }));\n }\n\n async render(html: string): Promise<string | null> {\n this._setLoading(true);\n this._setError(null);\n\n try {\n const result = await renderToString(html);\n this._setHtml(result);\n this._setLoading(false);\n return this._html;\n } catch (e: any) {\n this._setError(e instanceof Error ? e : new Error(String(e)));\n this._setLoading(false);\n return null;\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.esm.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Window as t}from"happy-dom";const e=Symbol.for("wcstack.ssr.snapshotBuilder");const n=new class{_queue=[];_locked=!1;async acquire(){return this._locked&&await new Promise(t=>this._queue.push(t)),this._locked=!0,()=>{this._locked=!1,this._queue.shift()?.()}}},
|
|
1
|
+
import{Window as t}from"happy-dom";const e=Symbol.for("wcstack.ssr.snapshotBuilder");const n=new class{_queue=[];_locked=!1;async acquire(){return this._locked&&await new Promise(t=>this._queue.push(t)),this._locked=!0,()=>{this._locked=!1,this._queue.shift()?.()}}},r=["document","customElements","HTMLElement","DocumentFragment","Node","NodeFilter","Comment","Text","MutationObserver","ShadowRoot","Element","HTMLTemplateElement","window","location","history","Event","CustomEvent"];function o(t){const e={};for(const n of r)e[n]=globalThis[n],globalThis[n]=t[n];const n=URL.createObjectURL;return URL.createObjectURL=void 0,()=>{URL.createObjectURL=n;for(const t of r)globalThis[t]=e[t]}}function s(t){const e=globalThis.URL,n=t;return globalThis.URL=class extends e{constructor(t,e){"string"==typeof t&&t.startsWith("/")&&void 0===e?super(t,n):super(t,e)}},globalThis.URL.createObjectURL=e.createObjectURL,globalThis.URL.revokeObjectURL=e.revokeObjectURL,()=>{globalThis.URL=e}}async function a(t,e){const n=e?.maxIterations??10,r=new WeakSet,o=new Set;for(let e=0;e<n;e++){const e=[];for(const n of t.querySelectorAll("*-*")){if(r.has(n))continue;const t=n.constructor;t.hasConnectedCallbackPromise&&(r.add(n),e.push(n.connectedCallbackPromise)),"function"==typeof t.getBindingsReady&&o.add(t)}if(0===e.length)break;await Promise.all(e)}await Promise.all(Array.from(o,e=>e.getBindingsReady(t)))}async function i(r,i){const c=await n.acquire(),l=i?.url?new t({url:i.url}):new t,d=o(l),u=l.document;let h=null;try{if(void 0!==i?.url||void 0!==i?.baseHref){const t=u.createElement("base");t.setAttribute("href",i.baseHref??"/"),u.head.appendChild(t)}const t=i?.baseUrl??(i?.url?new URL(i.url).origin:void 0);h=t?s(t):null;const n=i?.bootstraps??await async function(){const{bootstrapState:t}=await import("@wcstack/state");return[t]}();for(const t of n)await t();const o=function(){const t=globalThis[e];return null==t||"wcs-ssr-snapshot"!==t.protocol||"number"!=typeof t.version||t.version<1||"function"!=typeof t.build?null:t}();return u.documentElement.setAttribute("data-wcs-server",null!==o?"orchestrated":""),u.body.innerHTML=r,await a(u),o?.build(u),u.body.innerHTML}finally{try{const t=[];for(const e of u.querySelectorAll("*-*")){const n=e.constructor;"function"==typeof n.getBindingsReady&&t.push(n.getBindingsReady(u))}await Promise.allSettled(t)}catch{}const t=globalThis[Symbol.for("wcstack.binder.pending")];Array.isArray(t)&&(t.length=0),h?.(),d(),await l.close(),c()}}const c="2.0.0";class l extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"html",event:"wcs-render:html-changed",semantics:"state"},{name:"loading",event:"wcs-render:loading-changed",semantics:"state"},{name:"error",event:"wcs-render:error",semantics:"state"}]};_html=null;_loading=!1;_error=null;get html(){return this._html}get loading(){return this._loading}get error(){return this._error}_setLoading(t){this._loading=t,this.dispatchEvent(new CustomEvent("wcs-render:loading-changed",{detail:t}))}_setHtml(t){this._html=t,this.dispatchEvent(new CustomEvent("wcs-render:html-changed",{detail:t}))}_setError(t){this._error=t,this.dispatchEvent(new CustomEvent("wcs-render:error",{detail:t}))}async render(t){this._setLoading(!0),this._setError(null);try{const e=await i(t);return this._setHtml(e),this._setLoading(!1),this._html}catch(t){return this._setError(t instanceof Error?t:new Error(String(t))),this._setLoading(!1),null}}}export{r as GLOBALS_KEYS,l as RenderCore,c as VERSION,s as installBaseUrl,o as installGlobals,i as renderToString,a as waitForReady};
|
|
2
2
|
//# sourceMappingURL=index.esm.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.min.js","sources":["../../src/protocol/ssrSnapshot.ts","../../src/render.ts","../../src/version.ts","../../src/RenderCore.ts"],"sourcesContent":["// ===========================================================================\n// AUTO-GENERATED FILE - DO NOT EDIT.\n// Generated from /protocol/ssr-snapshot.ts by scripts/sync-protocol-types.mjs.\n// Run `node scripts/sync-protocol-types.mjs` after editing the source.\n// ===========================================================================\n\n// ssr-snapshot protocol — how the SSR renderer asks whoever owns reactive\n// state to build hydration snapshots (<wcs-ssr>) as a final pass, after every\n// DOM inserter (router route content, late custom elements) has settled.\n//\n// Without this, the snapshot is built inside <wcs-state>'s connectedCallback\n// and races DOM inserted by other packages: whether a route's structural\n// templates make it into the snapshot depends on document order and state's\n// load mechanism (docs/ssr-router-design.md §5).\n//\n// The provider (@wcstack/state) installs itself on a well-known global symbol\n// at bootstrap. The renderer (@wcstack/server) looks the builder up after\n// running bootstraps: if present it announces orchestration by setting\n// `data-wcs-server=\"orchestrated\"` on the document element BEFORE parsing, and\n// calls build() right before serialization. The provider keeps its inline\n// per-element fallback whenever the attribute value is anything else, so:\n// - old renderer + new provider -> inline build, yesterday's behavior\n// - new renderer + old provider -> no builder found, attribute stays \"\",\n// the old provider builds inline as before\n// - new renderer + new provider -> orchestrated: snapshots are built last\n// and therefore always see settled DOM\n//\n// The symbol (rather than a package import) also pins the builder to the state\n// copy that actually runs on the page — its module-scoped fragment registries\n// are the ones the snapshot must read.\n//\n// SINGLE SOURCE OF TRUTH: edit only this file (/protocol/ssr-snapshot.ts), then\n// run `node scripts/sync-protocol-types.mjs` to regenerate the per-package\n// copies (packages/<pkg>/src/protocol/ssrSnapshot.ts). Those copies are\n// generated — do not edit them.\n\n/**\n * Global key the snapshot builder installs itself under. `Symbol.for` so\n * independently loaded copies of this file (state's and server's) still agree.\n */\nexport const SSR_SNAPSHOT_BUILDER_KEY = Symbol.for(\"wcstack.ssr.snapshotBuilder\");\n\n/**\n * `data-wcs-server` attribute value announcing that the renderer will call the\n * builder as a final pass. Providers must skip their inline per-element build\n * when they see this value, and keep it for any other value (including \"\").\n */\nexport const SSR_ORCHESTRATED_VALUE = \"orchestrated\";\n\nexport interface IWcsSsrSnapshotBuilder {\n readonly protocol: \"wcs-ssr-snapshot\";\n /** Integer protocol version. All versions >= 1 are participant-compatible. */\n readonly version: number;\n /**\n * Build a hydration snapshot for every enable-ssr state element in `root`\n * that does not already have one. Called once per render, after all\n * waiting-protocol elements have settled and bindings are ready. Must be\n * idempotent with respect to already-snapshotted elements.\n */\n build(root: Document): void;\n}\n\n/**\n * The installed builder, or null when there is none or it speaks a shape this\n * reader does not.\n */\nexport function getSsrSnapshotBuilder(): IWcsSsrSnapshotBuilder | null {\n const candidate = (globalThis as Record<symbol, unknown>)[\n SSR_SNAPSHOT_BUILDER_KEY\n ] as IWcsSsrSnapshotBuilder | undefined;\n if (candidate === undefined || candidate === null) return null;\n if (candidate.protocol !== \"wcs-ssr-snapshot\") return null;\n if (typeof candidate.version !== \"number\" || candidate.version < 1) return null;\n if (typeof candidate.build !== \"function\") return null;\n return candidate;\n}\n","import { Window } from 'happy-dom';\nimport { getSsrSnapshotBuilder, SSR_ORCHESTRATED_VALUE } from './protocol/ssrSnapshot';\n\n/**\n * globalThis を差し替える renderToString の並列実行を防止する Mutex。\n * 同一 Node プロセス内で複数リクエストが同時に renderToString を呼んでも\n * シリアライズされ、グローバル状態の衝突を防ぐ。\n */\nclass Mutex {\n private _queue: (() => void)[] = [];\n private _locked = false;\n\n async acquire(): Promise<() => void> {\n if (this._locked) {\n await new Promise<void>(resolve => this._queue.push(resolve));\n }\n this._locked = true;\n return () => {\n this._locked = false;\n this._queue.shift()?.();\n };\n }\n}\n\nconst renderMutex = new Mutex();\n\nexport const GLOBALS_KEYS = [\n 'document', 'customElements', 'HTMLElement',\n 'DocumentFragment', 'Node', 'NodeFilter', 'Comment', 'Text',\n 'MutationObserver', 'ShadowRoot', 'Element', 'HTMLTemplateElement',\n // URL を持つコンポーネント(@wcstack/router 等)が window.location /\n // history を読めるようにする(docs/ssr-router-design.md §3.1)。\n 'window', 'location', 'history',\n // コンポーネントが発火するイベントをレンダリングウィンドウの realm に揃える。\n // Node ネイティブの CustomEvent は happy-dom の EventTarget に拒否される\n // (\"parameter 1 is not of type 'Event'\")。vitest の happy-dom 環境では\n // グローバルが happy-dom 側なので隠れ、素の Node サーバーでだけ顕在化する\n 'Event', 'CustomEvent',\n];\n\nexport function installGlobals(window: Window): () => void {\n const saved: Record<string, any> = {};\n for (const key of GLOBALS_KEYS) {\n saved[key] = (globalThis as any)[key];\n (globalThis as any)[key] = (window as any)[key];\n }\n // URL.createObjectURL を無効化して、\n // loadFromInnerScript が base64 data: URL フォールバックを使うようにする\n const origCreateObjectURL = URL.createObjectURL;\n (URL as any).createObjectURL = undefined;\n return () => {\n URL.createObjectURL = origCreateObjectURL;\n for (const key of GLOBALS_KEYS) {\n (globalThis as any)[key] = saved[key];\n }\n };\n}\n\nexport function installBaseUrl(baseUrl: string): () => void {\n const OrigURL = globalThis.URL;\n const base = baseUrl;\n globalThis.URL = class extends OrigURL {\n constructor(input: string | URL, inputBase?: string | URL) {\n if (typeof input === 'string' && input.startsWith('/') && inputBase === undefined) {\n super(input, base);\n } else {\n super(input as string, inputBase);\n }\n }\n } as typeof URL;\n // 静的メソッドを引き継ぐ\n globalThis.URL.createObjectURL = OrigURL.createObjectURL;\n globalThis.URL.revokeObjectURL = OrigURL.revokeObjectURL;\n return () => { globalThis.URL = OrigURL; };\n}\n\n/** @deprecated Use Ssr.extractStateData() from @wcstack/state instead */\nexport function extractStateData(stateEl: any): Record<string, any> {\n const raw = (stateEl as any).__state;\n if (!raw || typeof raw !== 'object') return {};\n const data: Record<string, any> = {};\n for (const [key, value] of Object.entries(raw)) {\n if (!key.startsWith('$') && typeof value !== 'function') {\n data[key] = value;\n }\n }\n return data;\n}\n\n/**\n * 同期の bootstrap 関数、または非同期ローダー。\n * `HTMLElement` を継承するクラスはモジュール評価時にグローバルの `HTMLElement` を\n * 参照するため、純 Node 環境ではトップレベル import できないパッケージがある。\n * その場合は `async () => (await import('@wcstack/router')).bootstrapRouter()` の\n * ように非同期ローダーを渡す — 呼び出しは installGlobals の後なので、モジュール\n * 評価時にはグローバルが揃っている(docs/ssr-router-design.md §3.1)。\n */\nexport type BootstrapFunction = () => void | Promise<void>;\n\nexport interface RenderOptions {\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\")。省略時は `url` の origin */\n baseUrl?: string;\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\n bootstraps?: BootstrapFunction[];\n /**\n * このリクエストの完全 URL (例: \"http://localhost:3000/products/1\")。\n * `window.location` / `document.baseURI` に反映される。ルーティングする\n * コンポーネント(@wcstack/router 等)のサーバーレンダリングに必要\n * (docs/ssr-router-design.md §3.1)。\n */\n url?: string;\n /**\n * `<head>` へ注入する `<base href>` の値。`url` 指定時の既定は \"/\"。\n * ブラウザで `<base>` を置く SPA と同じ条件をサーバー内に再現する\n * (深い URL での basename 誤認を防ぐ)。サブパス配備では明示する。\n */\n baseHref?: string;\n}\n\nasync function loadDefaultBootstraps(): Promise<BootstrapFunction[]> {\n const { bootstrapState } = await import('@wcstack/state');\n return [bootstrapState];\n}\n\n\n/**\n * HTML 文字列を SSR レンダリングして返す。\n *\n * ## 入力 HTML のルール\n * - `<body>` の中身だけを渡す(`<html>`, `<head>`, `<body>` タグは含めない)\n * - `<script>` / `<link>` による外部リソース読み込みは実行されない\n * → 必要なパッケージは `options.bootstraps` で明示的に渡す\n *\n * ## SSR でできること\n *\n * ### 状態の初期化とデータ取得\n * - `<wcs-state>` の状態ロード(json 属性, src 属性, inline `<script type=\"module\">`)\n * - `$connectedCallback` でのサーバーサイド fetch(API 呼び出し、DB 問い合わせ等)\n *\n * ```html\n * <!-- JSON 直接指定 -->\n * <wcs-state enable-ssr json='{\"title\":\"Hello\"}'></wcs-state>\n *\n * <!-- $connectedCallback で API からデータ取得 -->\n * <!-- $connectedCallback は状態オブジェクトのメソッドとして定義し、this が state proxy -->\n * <wcs-state enable-ssr>\n * <script type=\"module\">\n * export default {\n * async $connectedCallback() {\n * const res = await fetch('/api/users');\n * this.users = await res.json();\n * }\n * };\n * </script>\n * </wcs-state>\n * ```\n *\n * ### wcs-fetch を使ったサーバー通信\n * - `<wcs-fetch>` の auto-fetch(`manual` なし)はサーバーでも実行される\n * - `manual` + `$connectedCallback` で明示的に制御する場合:\n *\n * ```html\n * <wcs-fetch id=\"api\" url=\"/api/users\" manual></wcs-fetch>\n * <wcs-state enable-ssr>\n * <script type=\"module\">\n * export default {\n * async $connectedCallback() {\n * const el = document.getElementById('api');\n * this.users = await el.fetch();\n * }\n * };\n * </script>\n * </wcs-state>\n * ```\n * ※ `bootstraps` に `bootstrapFetch` を含める必要あり\n *\n * ### バインディングと構造レンダリング\n * - `data-wcs` バインディングの適用(text, attribute, class, style, property)\n * - `<template data-wcs=\"for:\">` / `if:` / `elseif:` / `else:` の構造レンダリング\n *\n * ```html\n * <ul>\n * <template data-wcs=\"for: users\">\n * <li data-wcs=\"textContent: .name\"></li>\n * </template>\n * </ul>\n * <template data-wcs=\"if: isAdmin\">\n * <div class=\"admin-panel\">...</div>\n * </template>\n * ```\n *\n * ### ハイドレーション\n * - `enable-ssr` 付き `<wcs-state>` の `<wcs-ssr>` メタデータ自動生成\n * - クライアント側でのハイドレーション(再レンダリングなしでバインディング復元)\n * - `enable-ssr` を外した `<wcs-state>` はクライアントのみで動作(部分 CSR)\n *\n * ### カスタム要素の待機\n * - `static hasConnectedCallbackPromise = true` プロトコル準拠の全カスタム要素を自動待機\n * - `$connectedCallback` 中に動的追加されたカスタム要素も安定化ループで検出・待機(最大 10 回)\n *\n * ### router SSR\n * - `<wcs-router enable-ssr>` + `url` オプションで初期ルートをサーバー描画。\n * クライアント側 router は描画済み DOM を採用(adopt)する。\n * 詳細は README「Router SSR」/ docs/ssr-router-design.md\n *\n * ## SSR でできないこと\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\n * - guard 付きルートのサーバー描画(設計上・クライアントで guard 実行)、\n * `<wcs-layout>` ルートの採用(クライアント描画へフォールバック)、\n * `<wcs-head>` のサーバー反映(body のみの出力に head は載らない)\n *\n * ## HTML の分割パターン\n * ```\n * // server.js\n * const ssrBody = await renderToString(template, { ... });\n * const page = `<!DOCTYPE html>\n * <html lang=\"ja\">\n * <head>\n * <script type=\"module\" src=\"/packages/state/dist/auto.min.js\"></script>\n * </head>\n * <body>${ssrBody}</body>\n * </html>`;\n * ```\n * `renderToString` には `<body>` の中身だけを渡し、\n * `<head>` や `<script>` タグは外側のテンプレートで囲む。\n */\nexport async function renderToString(html: string, options?: RenderOptions): Promise<string> {\n // globalThis を差し替えるため、同時に1つしか実行できない\n const releaseMutex = await renderMutex.acquire();\n\n const window = options?.url ? new Window({ url: options.url }) : new Window();\n const restoreGlobals = installGlobals(window);\n const document = window.document;\n\n let restoreBaseUrl: (() => void) | null = null;\n\n try {\n // url 指定時は <base href> を注入する(既定 \"/\")。ブラウザで <base> を置く\n // SPA と同じ条件を再現し、深い URL での basename 誤認を防ぐ\n // (docs/ssr-router-design.md §3.1)。\n if (options?.url !== undefined || options?.baseHref !== undefined) {\n const base = document.createElement('base');\n base.setAttribute('href', options.baseHref ?? '/');\n document.head.appendChild(base);\n }\n\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール。\n // baseUrl 省略時は url の origin を既定にする。\n const effectiveBaseUrl =\n options?.baseUrl ?? (options?.url ? new URL(options.url).origin : undefined);\n restoreBaseUrl = effectiveBaseUrl\n ? installBaseUrl(effectiveBaseUrl)\n : null;\n\n // bootstrap の解決。非同期ローダー(BootstrapFunction 参照)を許容するため\n // await する。try 内で行うのは、throw 時にもグローバル復元を保証するため。\n const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();\n\n for (const bootstrap of bootstraps) {\n await bootstrap();\n }\n\n // SSR モードを html 要素に設定。snapshot builder(bootstraps の実行が\n // 登録し得る — ssr-snapshot プロトコル)が居れば orchestrated を宣言し、\n // <wcs-ssr> 生成をサーバー主導の最終パスへ回す(docs/ssr-router-design.md §5)。\n // 値の宣言はパースより前 — 各要素は connectedCallback で値を読むため\n const snapshotBuilder = getSsrSnapshotBuilder();\n document.documentElement.setAttribute(\n 'data-wcs-server',\n snapshotBuilder !== null ? SSR_ORCHESTRATED_VALUE : ''\n );\n\n // HTML をパース\n // connectedCallback が自動発火 → state ロード → $connectedCallback 実行\n document.body.innerHTML = html;\n\n // connectedCallbackPromise / getBindingsReady プロトコルを自動検出\n // $connectedCallback が動的にカスタム要素を追加する場合があるため、\n // 新しい要素が見つからなくなるまで走査を繰り返す(安定化ループ)\n const MAX_ITERATIONS = 10;\n const awaitedElements = new WeakSet();\n const readyCtors = new Set<any>();\n\n for (let i = 0; i < MAX_ITERATIONS; i++) {\n const connectedPromises: Promise<void>[] = [];\n\n for (const el of document.querySelectorAll('*-*')) {\n if (awaitedElements.has(el)) continue;\n const ctor = el.constructor as any;\n if (ctor.hasConnectedCallbackPromise) {\n awaitedElements.add(el);\n connectedPromises.push((el as any).connectedCallbackPromise);\n }\n if (typeof ctor.getBindingsReady === 'function') {\n readyCtors.add(ctor);\n }\n }\n\n if (connectedPromises.length === 0) break;\n await Promise.all(connectedPromises);\n }\n\n // 非同期初期化(バインディング構築)の完了を待機。Promise の**取得**は\n // 安定化ループの後に行う — getBindingsReady の実体(rootNode ごとの ready)は\n // 各要素の connectedCallback 内・最初の await より後に登録されるため、ループ\n // 初回の収集では「まだ登録前」の即時解決 Promise を掴むことがある。inline\n // スナップショット時代は state の connectedCallback 自身が bindings を待って\n // いたため隠れていた(orchestrated 化で顕在化)。取り逃すと構築の続きが\n // グローバル復元後に走り、document 消失でクラッシュする\n await Promise.all(\n Array.from(readyCtors, (ctor) => (ctor as { getBindingsReady(root: Node): Promise<void> }).getBindingsReady(document as unknown as Node))\n );\n\n // スナップショット最終パス(orchestrated): 全要素の完了とバインディング構築の\n // 後に <wcs-ssr> を生成する。inline 生成(connectedCallback 内)が取り逃がす\n // 「後から挿入されたルート内容の構造テンプレート」も、この時点なら確定している\n snapshotBuilder?.build(document as unknown as Document);\n\n return document.body.innerHTML;\n } finally {\n // エラー経路でも進行中のバインディング構築を待ってから globals を戻す。\n // 構築は要素の connectedCallback とは独立した microtask 連鎖で走るため、\n // 待たずに戻すと続きが document 消失で unhandled になりプロセスを落とす。\n // 後始末はベストエフォート(rejected も含めて待つだけ待つ)\n try {\n const readyPending: Promise<void>[] = [];\n for (const el of document.querySelectorAll('*-*')) {\n const ctor = el.constructor as { getBindingsReady?(root: Node): Promise<void> };\n if (typeof ctor.getBindingsReady === 'function') {\n readyPending.push(ctor.getBindingsReady(document as unknown as Node));\n }\n }\n await Promise.allSettled(readyPending);\n } catch { /* best effort */ }\n // binder プロトコルの保留キュー(Symbol.for なので installGlobals の restore\n // 対象外=プロセス寿命)を空にする。state を読み込まないページで挿入側\n // (router 等)が差し出したノードは引き取り手が現れないまま蓄積するため、\n // レンダリングごとに後始末する(docs/ssr-router-design.md §3.1)。\n // これはプロトコルの公開シンボル面であり、パッケージ内部への依存ではない。\n const pendingBinds = (globalThis as Record<symbol, unknown>)[\n Symbol.for('wcstack.binder.pending')\n ];\n if (Array.isArray(pendingBinds)) {\n pendingBinds.length = 0;\n }\n restoreBaseUrl?.();\n restoreGlobals();\n await window.close();\n releaseMutex();\n }\n}\n","import pkg from \"../package.json\" with { type: \"json\" };\nexport const VERSION = pkg.version;\n","import { renderToString } from \"./render.js\";\nimport { IWcBindable } from \"./types.js\";\n\nexport class RenderCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"html\", event: \"wcs-render:html-changed\", semantics: \"state\" },\n { name: \"loading\", event: \"wcs-render:loading-changed\", semantics: \"state\" },\n { name: \"error\", event: \"wcs-render:error\", semantics: \"state\" },\n ],\n };\n\n private _html: string | null = null;\n private _loading: boolean = false;\n private _error: Error | null = null;\n\n get html(): string | null {\n return this._html;\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n private _setLoading(loading: boolean): void {\n this._loading = loading;\n this.dispatchEvent(new CustomEvent(\"wcs-render:loading-changed\", {\n detail: loading,\n }));\n }\n\n private _setHtml(html: string): void {\n this._html = html;\n this.dispatchEvent(new CustomEvent(\"wcs-render:html-changed\", {\n detail: html,\n }));\n }\n\n private _setError(error: Error | null): void {\n this._error = error;\n this.dispatchEvent(new CustomEvent(\"wcs-render:error\", {\n detail: error,\n }));\n }\n\n async render(html: string): Promise<string | null> {\n this._setLoading(true);\n this._setError(null);\n\n try {\n const result = await renderToString(html);\n this._setHtml(result);\n this._setLoading(false);\n return this._html;\n } catch (e: any) {\n this._setError(e instanceof Error ? e : new Error(String(e)));\n this._setLoading(false);\n return null;\n }\n }\n}\n"],"names":["SSR_SNAPSHOT_BUILDER_KEY","Symbol","for","renderMutex","_queue","_locked","acquire","this","Promise","resolve","push","shift","GLOBALS_KEYS","installGlobals","window","saved","key","globalThis","origCreateObjectURL","URL","createObjectURL","undefined","installBaseUrl","baseUrl","OrigURL","base","constructor","input","inputBase","startsWith","super","revokeObjectURL","extractStateData","stateEl","raw","__state","data","value","Object","entries","async","renderToString","html","options","releaseMutex","url","Window","restoreGlobals","document","restoreBaseUrl","baseHref","createElement","setAttribute","head","appendChild","effectiveBaseUrl","origin","bootstraps","bootstrapState","import","loadDefaultBootstraps","bootstrap","snapshotBuilder","candidate","protocol","version","build","getSsrSnapshotBuilder","documentElement","body","innerHTML","MAX_ITERATIONS","awaitedElements","WeakSet","readyCtors","Set","i","connectedPromises","el","querySelectorAll","has","ctor","hasConnectedCallbackPromise","add","connectedCallbackPromise","getBindingsReady","length","all","Array","from","readyPending","allSettled","pendingBinds","isArray","close","VERSION","RenderCore","EventTarget","static","properties","name","event","semantics","_html","_loading","_error","loading","error","_setLoading","dispatchEvent","CustomEvent","detail","_setHtml","_setError","render","result","e","Error","String"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.min.js","sources":["../../src/protocol/ssrSnapshot.ts","../../src/render.ts","../../src/version.ts","../../src/RenderCore.ts"],"sourcesContent":["// ===========================================================================\n// AUTO-GENERATED FILE - DO NOT EDIT.\n// Generated from /protocol/ssr-snapshot.ts by scripts/sync-protocol-types.mjs.\n// Run `node scripts/sync-protocol-types.mjs` after editing the source.\n// ===========================================================================\n\n// ssr-snapshot protocol — how the SSR renderer asks whoever owns reactive\n// state to build hydration snapshots (<wcs-ssr>) as a final pass, after every\n// DOM inserter (router route content, late custom elements) has settled.\n//\n// Without this, the snapshot is built inside <wcs-state>'s connectedCallback\n// and races DOM inserted by other packages: whether a route's structural\n// templates make it into the snapshot depends on document order and state's\n// load mechanism (docs/ssr-router-design.md §5).\n//\n// The provider (@wcstack/state) installs itself on a well-known global symbol\n// at bootstrap. The renderer (@wcstack/server) looks the builder up after\n// running bootstraps: if present it announces orchestration by setting\n// `data-wcs-server=\"orchestrated\"` on the document element BEFORE parsing, and\n// calls build() right before serialization. The provider keeps its inline\n// per-element fallback whenever the attribute value is anything else, so:\n// - old renderer + new provider -> inline build, yesterday's behavior\n// - new renderer + old provider -> no builder found, attribute stays \"\",\n// the old provider builds inline as before\n// - new renderer + new provider -> orchestrated: snapshots are built last\n// and therefore always see settled DOM\n//\n// The symbol (rather than a package import) also pins the builder to the state\n// copy that actually runs on the page — its module-scoped fragment registries\n// are the ones the snapshot must read.\n//\n// SINGLE SOURCE OF TRUTH: edit only this file (/protocol/ssr-snapshot.ts), then\n// run `node scripts/sync-protocol-types.mjs` to regenerate the per-package\n// copies (packages/<pkg>/src/protocol/ssrSnapshot.ts). Those copies are\n// generated — do not edit them.\n\n/**\n * Global key the snapshot builder installs itself under. `Symbol.for` so\n * independently loaded copies of this file (state's and server's) still agree.\n */\nexport const SSR_SNAPSHOT_BUILDER_KEY = Symbol.for(\"wcstack.ssr.snapshotBuilder\");\n\n/**\n * `data-wcs-server` attribute value announcing that the renderer will call the\n * builder as a final pass. Providers must skip their inline per-element build\n * when they see this value, and keep it for any other value (including \"\").\n */\nexport const SSR_ORCHESTRATED_VALUE = \"orchestrated\";\n\nexport interface IWcsSsrSnapshotBuilder {\n readonly protocol: \"wcs-ssr-snapshot\";\n /** Integer protocol version. All versions >= 1 are participant-compatible. */\n readonly version: number;\n /**\n * Build a hydration snapshot for every enable-ssr state element in `root`\n * that does not already have one. Called once per render, after all\n * waiting-protocol elements have settled and bindings are ready. Must be\n * idempotent with respect to already-snapshotted elements.\n */\n build(root: Document): void;\n}\n\n/**\n * The installed builder, or null when there is none or it speaks a shape this\n * reader does not.\n */\nexport function getSsrSnapshotBuilder(): IWcsSsrSnapshotBuilder | null {\n const candidate = (globalThis as Record<symbol, unknown>)[\n SSR_SNAPSHOT_BUILDER_KEY\n ] as IWcsSsrSnapshotBuilder | undefined;\n if (candidate === undefined || candidate === null) return null;\n if (candidate.protocol !== \"wcs-ssr-snapshot\") return null;\n if (typeof candidate.version !== \"number\" || candidate.version < 1) return null;\n if (typeof candidate.build !== \"function\") return null;\n return candidate;\n}\n","import { Window } from 'happy-dom';\nimport { getSsrSnapshotBuilder, SSR_ORCHESTRATED_VALUE } from './protocol/ssrSnapshot';\n\n/**\n * globalThis を差し替える renderToString の並列実行を防止する Mutex。\n * 同一 Node プロセス内で複数リクエストが同時に renderToString を呼んでも\n * シリアライズされ、グローバル状態の衝突を防ぐ。\n */\nclass Mutex {\n private _queue: (() => void)[] = [];\n private _locked = false;\n\n async acquire(): Promise<() => void> {\n if (this._locked) {\n await new Promise<void>(resolve => this._queue.push(resolve));\n }\n this._locked = true;\n return () => {\n this._locked = false;\n this._queue.shift()?.();\n };\n }\n}\n\nconst renderMutex = new Mutex();\n\nexport const GLOBALS_KEYS = [\n 'document', 'customElements', 'HTMLElement',\n 'DocumentFragment', 'Node', 'NodeFilter', 'Comment', 'Text',\n 'MutationObserver', 'ShadowRoot', 'Element', 'HTMLTemplateElement',\n // URL を持つコンポーネント(@wcstack/router 等)が window.location /\n // history を読めるようにする(docs/ssr-router-design.md §3.1)。\n 'window', 'location', 'history',\n // コンポーネントが発火するイベントをレンダリングウィンドウの realm に揃える。\n // Node ネイティブの CustomEvent は happy-dom の EventTarget に拒否される\n // (\"parameter 1 is not of type 'Event'\")。vitest の happy-dom 環境では\n // グローバルが happy-dom 側なので隠れ、素の Node サーバーでだけ顕在化する\n 'Event', 'CustomEvent',\n];\n\nexport function installGlobals(window: Window): () => void {\n const saved: Record<string, any> = {};\n for (const key of GLOBALS_KEYS) {\n saved[key] = (globalThis as any)[key];\n (globalThis as any)[key] = (window as any)[key];\n }\n // URL.createObjectURL を無効化して、\n // loadFromInnerScript が base64 data: URL フォールバックを使うようにする\n const origCreateObjectURL = URL.createObjectURL;\n (URL as any).createObjectURL = undefined;\n return () => {\n URL.createObjectURL = origCreateObjectURL;\n for (const key of GLOBALS_KEYS) {\n (globalThis as any)[key] = saved[key];\n }\n };\n}\n\nexport function installBaseUrl(baseUrl: string): () => void {\n const OrigURL = globalThis.URL;\n const base = baseUrl;\n globalThis.URL = class extends OrigURL {\n constructor(input: string | URL, inputBase?: string | URL) {\n if (typeof input === 'string' && input.startsWith('/') && inputBase === undefined) {\n super(input, base);\n } else {\n super(input as string, inputBase);\n }\n }\n } as typeof URL;\n // 静的メソッドを引き継ぐ\n globalThis.URL.createObjectURL = OrigURL.createObjectURL;\n globalThis.URL.revokeObjectURL = OrigURL.revokeObjectURL;\n return () => { globalThis.URL = OrigURL; };\n}\n\n/**\n * 同期の bootstrap 関数、または非同期ローダー。\n * `HTMLElement` を継承するクラスはモジュール評価時にグローバルの `HTMLElement` を\n * 参照するため、純 Node 環境ではトップレベル import できないパッケージがある。\n * その場合は `async () => (await import('@wcstack/router')).bootstrapRouter()` の\n * ように非同期ローダーを渡す — 呼び出しは installGlobals の後なので、モジュール\n * 評価時にはグローバルが揃っている(docs/ssr-router-design.md §3.1)。\n */\nexport type BootstrapFunction = () => void | Promise<void>;\n\nexport interface RenderOptions {\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\")。省略時は `url` の origin */\n baseUrl?: string;\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\n bootstraps?: BootstrapFunction[];\n /**\n * このリクエストの完全 URL (例: \"http://localhost:3000/products/1\")。\n * `window.location` / `document.baseURI` に反映される。ルーティングする\n * コンポーネント(@wcstack/router 等)のサーバーレンダリングに必要\n * (docs/ssr-router-design.md §3.1)。\n */\n url?: string;\n /**\n * `<head>` へ注入する `<base href>` の値。`url` 指定時の既定は \"/\"。\n * ブラウザで `<base>` を置く SPA と同じ条件をサーバー内に再現する\n * (深い URL での basename 誤認を防ぐ)。サブパス配備では明示する。\n */\n baseHref?: string;\n}\n\nasync function loadDefaultBootstraps(): Promise<BootstrapFunction[]> {\n const { bootstrapState } = await import('@wcstack/state');\n return [bootstrapState];\n}\n\nexport interface WaitForReadyOptions {\n /**\n * 安定化ループの上限。`$connectedCallback` が動的に追加した要素を拾うため、\n * 新しい要素が見つからなくなるまで走査を繰り返す(既定 10)。\n */\n maxIterations?: number;\n}\n\n/**\n * `root`(document / ShadowRoot)配下のカスタム要素が readiness プロトコルに従って\n * 初期化を終えるまで待つ。renderToString がシリアライズ前に行う待機と同じ手順で、\n * `@wcstack/testing` の `mount()` もこれを呼ぶ(docs/app-testing-and-typescript-impl-plan.md D11)。\n *\n * 1. `static hasConnectedCallbackPromise = true` を持つ全要素の `connectedCallbackPromise`\n * を待つ。待っている間に追加された要素も拾う(安定化ループ)。\n * `<wcs-router>` の初期ルート適用・`<wcs-state>` の状態ロードはここで完了する。\n * 2. `static getBindingsReady(root)` を持つクラス(`<wcs-state>`)の、この root に対する\n * バインディング構築完了を待つ。Promise の取得はループの後 — 実体は各要素の\n * connectedCallback 内・最初の await より後に登録されるため、先に掴むと「まだ\n * 登録前」の即時解決 Promise を取り逃す。\n *\n * バインディング初期化の失敗は reject として伝わる(state v1.26+)。\n */\nexport async function waitForReady(root: ParentNode & Node, options?: WaitForReadyOptions): Promise<void> {\n const maxIterations = options?.maxIterations ?? 10;\n const awaitedElements = new WeakSet<Element>();\n const readyCtors = new Set<{ getBindingsReady(root: Node): Promise<void> }>();\n\n for (let i = 0; i < maxIterations; i++) {\n const connectedPromises: Promise<void>[] = [];\n\n for (const el of root.querySelectorAll('*-*')) {\n if (awaitedElements.has(el)) continue;\n const ctor = el.constructor as any;\n if (ctor.hasConnectedCallbackPromise) {\n awaitedElements.add(el);\n connectedPromises.push((el as any).connectedCallbackPromise);\n }\n if (typeof ctor.getBindingsReady === 'function') {\n readyCtors.add(ctor);\n }\n }\n\n if (connectedPromises.length === 0) break;\n await Promise.all(connectedPromises);\n }\n\n await Promise.all(Array.from(readyCtors, (ctor) => ctor.getBindingsReady(root)));\n}\n\n\n/**\n * HTML 文字列を SSR レンダリングして返す。\n *\n * ## 入力 HTML のルール\n * - `<body>` の中身だけを渡す(`<html>`, `<head>`, `<body>` タグは含めない)\n * - `<script>` / `<link>` による外部リソース読み込みは実行されない\n * → 必要なパッケージは `options.bootstraps` で明示的に渡す\n *\n * ## SSR でできること\n *\n * ### 状態の初期化とデータ取得\n * - `<wcs-state>` の状態ロード(json 属性, src 属性, inline `<script type=\"module\">`)\n * - `$connectedCallback` でのサーバーサイド fetch(API 呼び出し、DB 問い合わせ等)\n *\n * ```html\n * <!-- JSON 直接指定 -->\n * <wcs-state enable-ssr json='{\"title\":\"Hello\"}'></wcs-state>\n *\n * <!-- $connectedCallback で API からデータ取得 -->\n * <!-- $connectedCallback は状態オブジェクトのメソッドとして定義し、this が state proxy -->\n * <wcs-state enable-ssr>\n * <script type=\"module\">\n * export default {\n * async $connectedCallback() {\n * const res = await fetch('/api/users');\n * this.users = await res.json();\n * }\n * };\n * </script>\n * </wcs-state>\n * ```\n *\n * ### wcs-fetch を使ったサーバー通信\n * - `<wcs-fetch>` の auto-fetch(`manual` なし)はサーバーでも実行される\n * - `manual` + `$connectedCallback` で明示的に制御する場合:\n *\n * ```html\n * <wcs-fetch id=\"api\" url=\"/api/users\" manual></wcs-fetch>\n * <wcs-state enable-ssr>\n * <script type=\"module\">\n * export default {\n * async $connectedCallback() {\n * const el = document.getElementById('api');\n * this.users = await el.fetch();\n * }\n * };\n * </script>\n * </wcs-state>\n * ```\n * ※ `bootstraps` に `bootstrapFetch` を含める必要あり\n *\n * ### バインディングと構造レンダリング\n * - `data-wcs` バインディングの適用(text, attribute, class, style, property)\n * - `<template data-wcs=\"for:\">` / `if:` / `elseif:` / `else:` の構造レンダリング\n *\n * ```html\n * <ul>\n * <template data-wcs=\"for: users\">\n * <li data-wcs=\"textContent: .name\"></li>\n * </template>\n * </ul>\n * <template data-wcs=\"if: isAdmin\">\n * <div class=\"admin-panel\">...</div>\n * </template>\n * ```\n *\n * ### ハイドレーション\n * - `enable-ssr` 付き `<wcs-state>` の `<wcs-ssr>` メタデータ自動生成\n * - クライアント側でのハイドレーション(再レンダリングなしでバインディング復元)\n * - `enable-ssr` を外した `<wcs-state>` はクライアントのみで動作(部分 CSR)\n *\n * ### カスタム要素の待機\n * - `static hasConnectedCallbackPromise = true` プロトコル準拠の全カスタム要素を自動待機\n * - `$connectedCallback` 中に動的追加されたカスタム要素も安定化ループで検出・待機(最大 10 回)\n *\n * ### router SSR\n * - `<wcs-router enable-ssr>` + `url` オプションで初期ルートをサーバー描画。\n * クライアント側 router は描画済み DOM を採用(adopt)する。\n * 詳細は README「Router SSR」/ docs/ssr-router-design.md\n *\n * ## SSR でできないこと\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\n * - guard 付きルートのサーバー描画(設計上・クライアントで guard 実行)、\n * `<wcs-layout>` ルートの採用(クライアント描画へフォールバック)、\n * `<wcs-head>` のサーバー反映(body のみの出力に head は載らない)\n *\n * ## HTML の分割パターン\n * ```\n * // server.js\n * const ssrBody = await renderToString(template, { ... });\n * const page = `<!DOCTYPE html>\n * <html lang=\"ja\">\n * <head>\n * <script type=\"module\" src=\"/packages/state/dist/auto.min.js\"></script>\n * </head>\n * <body>${ssrBody}</body>\n * </html>`;\n * ```\n * `renderToString` には `<body>` の中身だけを渡し、\n * `<head>` や `<script>` タグは外側のテンプレートで囲む。\n */\nexport async function renderToString(html: string, options?: RenderOptions): Promise<string> {\n // globalThis を差し替えるため、同時に1つしか実行できない\n const releaseMutex = await renderMutex.acquire();\n\n const window = options?.url ? new Window({ url: options.url }) : new Window();\n const restoreGlobals = installGlobals(window);\n const document = window.document;\n\n let restoreBaseUrl: (() => void) | null = null;\n\n try {\n // url 指定時は <base href> を注入する(既定 \"/\")。ブラウザで <base> を置く\n // SPA と同じ条件を再現し、深い URL での basename 誤認を防ぐ\n // (docs/ssr-router-design.md §3.1)。\n if (options?.url !== undefined || options?.baseHref !== undefined) {\n const base = document.createElement('base');\n base.setAttribute('href', options.baseHref ?? '/');\n document.head.appendChild(base);\n }\n\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール。\n // baseUrl 省略時は url の origin を既定にする。\n const effectiveBaseUrl =\n options?.baseUrl ?? (options?.url ? new URL(options.url).origin : undefined);\n restoreBaseUrl = effectiveBaseUrl\n ? installBaseUrl(effectiveBaseUrl)\n : null;\n\n // bootstrap の解決。非同期ローダー(BootstrapFunction 参照)を許容するため\n // await する。try 内で行うのは、throw 時にもグローバル復元を保証するため。\n const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();\n\n for (const bootstrap of bootstraps) {\n await bootstrap();\n }\n\n // SSR モードを html 要素に設定。snapshot builder(bootstraps の実行が\n // 登録し得る — ssr-snapshot プロトコル)が居れば orchestrated を宣言し、\n // <wcs-ssr> 生成をサーバー主導の最終パスへ回す(docs/ssr-router-design.md §5)。\n // 値の宣言はパースより前 — 各要素は connectedCallback で値を読むため\n const snapshotBuilder = getSsrSnapshotBuilder();\n document.documentElement.setAttribute(\n 'data-wcs-server',\n snapshotBuilder !== null ? SSR_ORCHESTRATED_VALUE : ''\n );\n\n // HTML をパース\n // connectedCallback が自動発火 → state ロード → $connectedCallback 実行\n document.body.innerHTML = html;\n\n // connectedCallbackPromise / getBindingsReady プロトコルを自動検出して待つ\n // (安定化ループ + バインディング構築。取り逃すと構築の続きがグローバル復元後に\n // 走り、document 消失でクラッシュする — 手順の詳細は waitForReady 参照)\n await waitForReady(document as unknown as ParentNode & Node);\n\n // スナップショット最終パス(orchestrated): 全要素の完了とバインディング構築の\n // 後に <wcs-ssr> を生成する。inline 生成(connectedCallback 内)が取り逃がす\n // 「後から挿入されたルート内容の構造テンプレート」も、この時点なら確定している\n snapshotBuilder?.build(document as unknown as Document);\n\n return document.body.innerHTML;\n } finally {\n // エラー経路でも進行中のバインディング構築を待ってから globals を戻す。\n // 構築は要素の connectedCallback とは独立した microtask 連鎖で走るため、\n // 待たずに戻すと続きが document 消失で unhandled になりプロセスを落とす。\n // 後始末はベストエフォート(rejected も含めて待つだけ待つ)\n try {\n const readyPending: Promise<void>[] = [];\n for (const el of document.querySelectorAll('*-*')) {\n const ctor = el.constructor as { getBindingsReady?(root: Node): Promise<void> };\n if (typeof ctor.getBindingsReady === 'function') {\n readyPending.push(ctor.getBindingsReady(document as unknown as Node));\n }\n }\n await Promise.allSettled(readyPending);\n } catch { /* best effort */ }\n // binder プロトコルの保留キュー(Symbol.for なので installGlobals の restore\n // 対象外=プロセス寿命)を空にする。state を読み込まないページで挿入側\n // (router 等)が差し出したノードは引き取り手が現れないまま蓄積するため、\n // レンダリングごとに後始末する(docs/ssr-router-design.md §3.1)。\n // これはプロトコルの公開シンボル面であり、パッケージ内部への依存ではない。\n const pendingBinds = (globalThis as Record<symbol, unknown>)[\n Symbol.for('wcstack.binder.pending')\n ];\n if (Array.isArray(pendingBinds)) {\n pendingBinds.length = 0;\n }\n restoreBaseUrl?.();\n restoreGlobals();\n await window.close();\n releaseMutex();\n }\n}\n","import pkg from \"../package.json\" with { type: \"json\" };\nexport const VERSION = pkg.version;\n","import { renderToString } from \"./render.js\";\nimport { IWcBindable } from \"./types.js\";\n\nexport class RenderCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"html\", event: \"wcs-render:html-changed\", semantics: \"state\" },\n { name: \"loading\", event: \"wcs-render:loading-changed\", semantics: \"state\" },\n { name: \"error\", event: \"wcs-render:error\", semantics: \"state\" },\n ],\n };\n\n private _html: string | null = null;\n private _loading: boolean = false;\n private _error: Error | null = null;\n\n get html(): string | null {\n return this._html;\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n private _setLoading(loading: boolean): void {\n this._loading = loading;\n this.dispatchEvent(new CustomEvent(\"wcs-render:loading-changed\", {\n detail: loading,\n }));\n }\n\n private _setHtml(html: string): void {\n this._html = html;\n this.dispatchEvent(new CustomEvent(\"wcs-render:html-changed\", {\n detail: html,\n }));\n }\n\n private _setError(error: Error | null): void {\n this._error = error;\n this.dispatchEvent(new CustomEvent(\"wcs-render:error\", {\n detail: error,\n }));\n }\n\n async render(html: string): Promise<string | null> {\n this._setLoading(true);\n this._setError(null);\n\n try {\n const result = await renderToString(html);\n this._setHtml(result);\n this._setLoading(false);\n return this._html;\n } catch (e: any) {\n this._setError(e instanceof Error ? e : new Error(String(e)));\n this._setLoading(false);\n return null;\n }\n }\n}\n"],"names":["SSR_SNAPSHOT_BUILDER_KEY","Symbol","for","renderMutex","_queue","_locked","acquire","this","Promise","resolve","push","shift","GLOBALS_KEYS","installGlobals","window","saved","key","globalThis","origCreateObjectURL","URL","createObjectURL","undefined","installBaseUrl","baseUrl","OrigURL","base","constructor","input","inputBase","startsWith","super","revokeObjectURL","async","waitForReady","root","options","maxIterations","awaitedElements","WeakSet","readyCtors","Set","i","connectedPromises","el","querySelectorAll","has","ctor","hasConnectedCallbackPromise","add","connectedCallbackPromise","getBindingsReady","length","all","Array","from","renderToString","html","releaseMutex","url","Window","restoreGlobals","document","restoreBaseUrl","baseHref","createElement","setAttribute","head","appendChild","effectiveBaseUrl","origin","bootstraps","bootstrapState","import","loadDefaultBootstraps","bootstrap","snapshotBuilder","candidate","protocol","version","build","getSsrSnapshotBuilder","documentElement","body","innerHTML","readyPending","allSettled","pendingBinds","isArray","close","VERSION","RenderCore","EventTarget","static","properties","name","event","semantics","_html","_loading","_error","loading","error","_setLoading","dispatchEvent","CustomEvent","detail","_setHtml","_setError","render","result","e","Error","String"],"mappings":"mCAwCO,MAAMA,EAA2BC,OAAOC,IAAI,+BChBnD,MAAMC,EAAc,IAhBpB,MACUC,OAAyB,GACzBC,SAAU,EAElB,aAAMC,GAKJ,OAJIC,KAAKF,eACD,IAAIG,QAAcC,GAAWF,KAAKH,OAAOM,KAAKD,IAEtDF,KAAKF,SAAU,EACR,KACLE,KAAKF,SAAU,EACfE,KAAKH,OAAOO,OAAZJ,KAEJ,GAKWK,EAAe,CAC1B,WAAY,iBAAkB,cAC9B,mBAAoB,OAAQ,aAAc,UAAW,OACrD,mBAAoB,aAAc,UAAW,sBAG7C,SAAU,WAAY,UAKtB,QAAS,eAGL,SAAUC,EAAeC,GAC7B,MAAMC,EAA6B,CAAA,EACnC,IAAK,MAAMC,KAAOJ,EAChBG,EAAMC,GAAQC,WAAmBD,GAChCC,WAAmBD,GAAQF,EAAeE,GAI7C,MAAME,EAAsBC,IAAIC,gBAEhC,OADCD,IAAYC,qBAAkBC,EACxB,KACLF,IAAIC,gBAAkBF,EACtB,IAAK,MAAMF,KAAOJ,EACfK,WAAmBD,GAAOD,EAAMC,GAGvC,CAEM,SAAUM,EAAeC,GAC7B,MAAMC,EAAUP,WAAWE,IACrBM,EAAOF,EAab,OAZAN,WAAWE,IAAM,cAAcK,EAC7B,WAAAE,CAAYC,EAAqBC,GACV,iBAAVD,GAAsBA,EAAME,WAAW,WAAsBR,IAAdO,EACxDE,MAAMH,EAAOF,GAEbK,MAAMH,EAAiBC,EAE3B,GAGFX,WAAWE,IAAIC,gBAAkBI,EAAQJ,gBACzCH,WAAWE,IAAIY,gBAAkBP,EAAQO,gBAClC,KAAQd,WAAWE,IAAMK,EAClC,CA4DOQ,eAAeC,EAAaC,EAAyBC,GAC1D,MAAMC,EAAgBD,GAASC,eAAiB,GAC1CC,EAAkB,IAAIC,QACtBC,EAAa,IAAIC,IAEvB,IAAK,IAAIC,EAAI,EAAGA,EAAIL,EAAeK,IAAK,CACtC,MAAMC,EAAqC,GAE3C,IAAK,MAAMC,KAAMT,EAAKU,iBAAiB,OAAQ,CAC7C,GAAIP,EAAgBQ,IAAIF,GAAK,SAC7B,MAAMG,EAAOH,EAAGjB,YACZoB,EAAKC,8BACPV,EAAgBW,IAAIL,GACpBD,EAAkBhC,KAAMiC,EAAWM,2BAEA,mBAA1BH,EAAKI,kBACdX,EAAWS,IAAIF,EAEnB,CAEA,GAAiC,IAA7BJ,EAAkBS,OAAc,YAC9B3C,QAAQ4C,IAAIV,EACpB,OAEMlC,QAAQ4C,IAAIC,MAAMC,KAAKf,EAAaO,GAASA,EAAKI,iBAAiBhB,IAC3E,CA4GOF,eAAeuB,EAAeC,EAAcrB,GAEjD,MAAMsB,QAAqBtD,EAAYG,UAEjCQ,EAASqB,GAASuB,IAAM,IAAIC,EAAO,CAAED,IAAKvB,EAAQuB,MAAS,IAAIC,EAC/DC,EAAiB/C,EAAeC,GAChC+C,EAAW/C,EAAO+C,SAExB,IAAIC,EAAsC,KAE1C,IAIE,QAAqBzC,IAAjBc,GAASuB,UAA2CrC,IAAtBc,GAAS4B,SAAwB,CACjE,MAAMtC,EAAOoC,EAASG,cAAc,QACpCvC,EAAKwC,aAAa,OAAQ9B,EAAQ4B,UAAY,KAC9CF,EAASK,KAAKC,YAAY1C,EAC5B,CAIA,MAAM2C,EACJjC,GAASZ,UAAYY,GAASuB,IAAM,IAAIvC,IAAIgB,EAAQuB,KAAKW,YAAShD,GACpEyC,EAAiBM,EACb9C,EAAe8C,GACf,KAIJ,MAAME,EAAanC,GAASmC,kBA/LhCtC,iBACE,MAAMuC,eAAEA,SAAyBC,OAAO,kBACxC,MAAO,CAACD,EACV,CA4LoDE,GAEhD,IAAK,MAAMC,KAAaJ,QAChBI,IAOR,MAAMC,aDhPR,MAAMC,EAAa3D,WACjBjB,GAEF,OAAI4E,SACuB,qBAAvBA,EAAUC,UACmB,iBAAtBD,EAAUE,SAAwBF,EAAUE,QAAU,GAClC,mBAApBF,EAAUG,MAHqC,KAInDH,CACT,CCwO4BI,GAoBxB,OAnBAnB,EAASoB,gBAAgBhB,aACvB,kBACoB,OAApBU,EDvQgC,eCuQoB,IAKtDd,EAASqB,KAAKC,UAAY3B,QAKpBvB,EAAa4B,GAKnBc,GAAiBI,MAAMlB,GAEhBA,EAASqB,KAAKC,SACvB,SAKE,IACE,MAAMC,EAAgC,GACtC,IAAK,MAAMzC,KAAMkB,EAASjB,iBAAiB,OAAQ,CACjD,MAAME,EAAOH,EAAGjB,YACqB,mBAA1BoB,EAAKI,kBACdkC,EAAa1E,KAAKoC,EAAKI,iBAAiBW,GAE5C,OACMrD,QAAQ6E,WAAWD,EAC3B,CAAE,MAA0B,CAM5B,MAAME,EAAgBrE,WACpBhB,OAAOC,IAAI,2BAETmD,MAAMkC,QAAQD,KAChBA,EAAanC,OAAS,GAExBW,MACAF,UACM9C,EAAO0E,QACb/B,GACF,CACF,CCtWO,MAAMgC,UCEP,MAAOC,UAAmBC,YAC9BC,kBAAiC,CAC/Bf,SAAU,cACVC,QAAS,EACTe,WAAY,CACV,CAAEC,KAAM,OAAQC,MAAO,0BAA2BC,UAAW,SAC7D,CAAEF,KAAM,UAAWC,MAAO,6BAA8BC,UAAW,SACnE,CAAEF,KAAM,QAASC,MAAO,mBAAoBC,UAAW,WAInDC,MAAuB,KACvBC,UAAoB,EACpBC,OAAuB,KAE/B,QAAI3C,GACF,OAAOjD,KAAK0F,KACd,CAEA,WAAIG,GACF,OAAO7F,KAAK2F,QACd,CAEA,SAAIG,GACF,OAAO9F,KAAK4F,MACd,CAEQ,WAAAG,CAAYF,GAClB7F,KAAK2F,SAAWE,EAChB7F,KAAKgG,cAAc,IAAIC,YAAY,6BAA8B,CAC/DC,OAAQL,IAEZ,CAEQ,QAAAM,CAASlD,GACfjD,KAAK0F,MAAQzC,EACbjD,KAAKgG,cAAc,IAAIC,YAAY,0BAA2B,CAC5DC,OAAQjD,IAEZ,CAEQ,SAAAmD,CAAUN,GAChB9F,KAAK4F,OAASE,EACd9F,KAAKgG,cAAc,IAAIC,YAAY,mBAAoB,CACrDC,OAAQJ,IAEZ,CAEA,YAAMO,CAAOpD,GACXjD,KAAK+F,aAAY,GACjB/F,KAAKoG,UAAU,MAEf,IACE,MAAME,QAAetD,EAAeC,GAGpC,OAFAjD,KAAKmG,SAASG,GACdtG,KAAK+F,aAAY,GACV/F,KAAK0F,KACd,CAAE,MAAOa,GAGP,OAFAvG,KAAKoG,UAAUG,aAAaC,MAAQD,EAAI,IAAIC,MAAMC,OAAOF,KACzDvG,KAAK+F,aAAY,GACV,IACT,CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wcstack/server",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Server-side rendering for wcstack Web Components.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.esm.js",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"license": "MIT",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@wcstack/state": "^
|
|
45
|
+
"@wcstack/state": "^2.0.0",
|
|
46
46
|
"happy-dom": "^20.0.11"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|