@wcstack/server 1.23.0 → 1.24.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/dist/index.d.ts +17 -0
- package/dist/index.esm.js +4 -4
- 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 +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -113,10 +113,27 @@ declare function renderToString(html: string, options?: RenderOptions): Promise<
|
|
|
113
113
|
|
|
114
114
|
declare const VERSION: string;
|
|
115
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Observation semantics of a `properties` entry.
|
|
118
|
+
*
|
|
119
|
+
* "state" — current value. A snapshot may cache it, and equality-based dedupe is safe.
|
|
120
|
+
* "event" — occurrence. Repeated identical payloads are distinct occurrences; never dedupe.
|
|
121
|
+
* "handle" — live / opaque resource with its own lifecycle (e.g. MediaStream). Not
|
|
122
|
+
* snapshot-safe and not necessarily serializable; consumers need an explicit
|
|
123
|
+
* ref / callback surface rather than a value slot.
|
|
124
|
+
*/
|
|
125
|
+
type WcBindableSemantics = "state" | "event" | "handle";
|
|
116
126
|
interface IWcBindableProperty {
|
|
117
127
|
readonly name: string;
|
|
118
128
|
readonly event: string;
|
|
119
129
|
readonly getter?: (event: Event) => any;
|
|
130
|
+
/**
|
|
131
|
+
* Optional, additive, forward-compatible. An absent value means **unspecified**, NOT
|
|
132
|
+
* "state": a reader that finds no `semantics` MUST keep the behavior it had before this
|
|
133
|
+
* field existed (deliver the update as-is; do not start deduping, caching or serializing
|
|
134
|
+
* on assumption). Only an explicit value licenses a reader to change its handling.
|
|
135
|
+
*/
|
|
136
|
+
readonly semantics?: WcBindableSemantics;
|
|
120
137
|
}
|
|
121
138
|
interface IWcBindableInput {
|
|
122
139
|
readonly name: string;
|
package/dist/index.esm.js
CHANGED
|
@@ -233,7 +233,7 @@ async function renderToString(html, options) {
|
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
-
var version = "1.
|
|
236
|
+
var version = "1.24.0";
|
|
237
237
|
var pkg = {
|
|
238
238
|
version: version};
|
|
239
239
|
|
|
@@ -244,9 +244,9 @@ class RenderCore extends EventTarget {
|
|
|
244
244
|
protocol: "wc-bindable",
|
|
245
245
|
version: 1,
|
|
246
246
|
properties: [
|
|
247
|
-
{ name: "html", event: "wcs-render:html-changed" },
|
|
248
|
-
{ name: "loading", event: "wcs-render:loading-changed" },
|
|
249
|
-
{ name: "error", event: "wcs-render:error" },
|
|
247
|
+
{ name: "html", event: "wcs-render:html-changed", semantics: "state" },
|
|
248
|
+
{ name: "loading", event: "wcs-render:loading-changed", semantics: "state" },
|
|
249
|
+
{ name: "error", event: "wcs-render:error", semantics: "state" },
|
|
250
250
|
],
|
|
251
251
|
};
|
|
252
252
|
_html = null;
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../../src/render.ts","../../src/version.ts","../../src/RenderCore.ts"],"sourcesContent":["import { Window } from 'happy-dom';\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];\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\nexport type BootstrapFunction = () => void;\n\nexport interface RenderOptions {\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\") */\n baseUrl?: string;\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\n bootstraps?: BootstrapFunction[];\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 * ## SSR でできないこと\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\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.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 = new Window();\n const restoreGlobals = installGlobals(window);\n const document = window.document;\n\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール\n const restoreBaseUrl = options?.baseUrl\n ? installBaseUrl(options.baseUrl)\n : null;\n\n // bootstrap の解決\n const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();\n\n for (const bootstrap of bootstraps) {\n bootstrap();\n }\n\n try {\n\n // SSR モードを html 要素に設定\n document.documentElement.setAttribute('data-wcs-server', '');\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 const readyPromises: Promise<void>[] = [];\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 (!readyCtors.has(ctor) && typeof ctor.getBindingsReady === 'function') {\n readyCtors.add(ctor);\n readyPromises.push(ctor.getBindingsReady(document));\n }\n }\n\n if (connectedPromises.length === 0) break;\n await Promise.all(connectedPromises);\n }\n\n // 非同期初期化の完了を待機\n await Promise.all(readyPromises);\n\n return document.body.innerHTML;\n } finally {\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\" },\n { name: \"loading\", event: \"wcs-render:loading-changed\" },\n { name: \"error\", event: \"wcs-render:error\" },\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/render.ts","../../src/version.ts","../../src/RenderCore.ts"],"sourcesContent":["import { Window } from 'happy-dom';\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];\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\nexport type BootstrapFunction = () => void;\n\nexport interface RenderOptions {\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\") */\n baseUrl?: string;\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\n bootstraps?: BootstrapFunction[];\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 * ## SSR でできないこと\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\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.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 = new Window();\n const restoreGlobals = installGlobals(window);\n const document = window.document;\n\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール\n const restoreBaseUrl = options?.baseUrl\n ? installBaseUrl(options.baseUrl)\n : null;\n\n // bootstrap の解決\n const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();\n\n for (const bootstrap of bootstraps) {\n bootstrap();\n }\n\n try {\n\n // SSR モードを html 要素に設定\n document.documentElement.setAttribute('data-wcs-server', '');\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 const readyPromises: Promise<void>[] = [];\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 (!readyCtors.has(ctor) && typeof ctor.getBindingsReady === 'function') {\n readyCtors.add(ctor);\n readyPromises.push(ctor.getBindingsReady(document));\n }\n }\n\n if (connectedPromises.length === 0) break;\n await Promise.all(connectedPromises);\n }\n\n // 非同期初期化の完了を待機\n await Promise.all(readyPromises);\n\n return document.body.innerHTML;\n } finally {\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=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()?.()}}},n=["document","customElements","HTMLElement","DocumentFragment","Node","NodeFilter","Comment","Text","MutationObserver","ShadowRoot","Element","HTMLTemplateElement"];function r(t){const e={};for(const r of n)e[r]=globalThis[r],globalThis[r]=t[r];const r=URL.createObjectURL;return URL.createObjectURL=void 0,()=>{URL.createObjectURL=r;for(const t of n)globalThis[t]=e[t]}}function
|
|
1
|
+
import{Window as t}from"happy-dom";const e=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()?.()}}},n=["document","customElements","HTMLElement","DocumentFragment","Node","NodeFilter","Comment","Text","MutationObserver","ShadowRoot","Element","HTMLTemplateElement"];function r(t){const e={};for(const r of n)e[r]=globalThis[r],globalThis[r]=t[r];const r=URL.createObjectURL;return URL.createObjectURL=void 0,()=>{URL.createObjectURL=r;for(const t of n)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}}function o(t){const e=t.__state;if(!e||"object"!=typeof e)return{};const n={};for(const[t,r]of Object.entries(e))t.startsWith("$")||"function"==typeof r||(n[t]=r);return n}async function a(n,o){const a=await e.acquire(),i=new t,c=r(i),l=i.document,d=o?.baseUrl?s(o.baseUrl):null,h=o?.bootstraps??await async function(){const{bootstrapState:t}=await import("@wcstack/state");return[t]}();for(const t of h)t();try{l.documentElement.setAttribute("data-wcs-server",""),l.body.innerHTML=n;const t=10,e=new WeakSet,r=new Set,s=[];for(let n=0;n<t;n++){const t=[];for(const n of l.querySelectorAll("*-*")){if(e.has(n))continue;const o=n.constructor;o.hasConnectedCallbackPromise&&(e.add(n),t.push(n.connectedCallbackPromise)),r.has(o)||"function"!=typeof o.getBindingsReady||(r.add(o),s.push(o.getBindingsReady(l)))}if(0===t.length)break;await Promise.all(t)}return await Promise.all(s),l.body.innerHTML}finally{d?.(),c(),await i.close(),a()}}const i="1.24.0";class c 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 a(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{n as GLOBALS_KEYS,c as RenderCore,i as VERSION,o as extractStateData,s as installBaseUrl,r as installGlobals,a as renderToString};
|
|
2
2
|
//# sourceMappingURL=index.esm.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.min.js","sources":["../../src/render.ts","../../src/version.ts","../../src/RenderCore.ts"],"sourcesContent":["import { Window } from 'happy-dom';\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];\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\nexport type BootstrapFunction = () => void;\n\nexport interface RenderOptions {\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\") */\n baseUrl?: string;\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\n bootstraps?: BootstrapFunction[];\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 * ## SSR でできないこと\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\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.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 = new Window();\n const restoreGlobals = installGlobals(window);\n const document = window.document;\n\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール\n const restoreBaseUrl = options?.baseUrl\n ? installBaseUrl(options.baseUrl)\n : null;\n\n // bootstrap の解決\n const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();\n\n for (const bootstrap of bootstraps) {\n bootstrap();\n }\n\n try {\n\n // SSR モードを html 要素に設定\n document.documentElement.setAttribute('data-wcs-server', '');\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 const readyPromises: Promise<void>[] = [];\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 (!readyCtors.has(ctor) && typeof ctor.getBindingsReady === 'function') {\n readyCtors.add(ctor);\n readyPromises.push(ctor.getBindingsReady(document));\n }\n }\n\n if (connectedPromises.length === 0) break;\n await Promise.all(connectedPromises);\n }\n\n // 非同期初期化の完了を待機\n await Promise.all(readyPromises);\n\n return document.body.innerHTML;\n } finally {\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\" },\n { name: \"loading\", event: \"wcs-render:loading-changed\" },\n { name: \"error\", event: \"wcs-render:error\" },\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":["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","Window","restoreGlobals","document","restoreBaseUrl","bootstraps","bootstrapState","import","loadDefaultBootstraps","bootstrap","documentElement","setAttribute","body","innerHTML","MAX_ITERATIONS","awaitedElements","WeakSet","readyCtors","Set","readyPromises","i","connectedPromises","el","querySelectorAll","has","ctor","hasConnectedCallbackPromise","add","connectedCallbackPromise","getBindingsReady","length","all","close","VERSION","RenderCore","EventTarget","static","protocol","version","properties","name","event","_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/render.ts","../../src/version.ts","../../src/RenderCore.ts"],"sourcesContent":["import { Window } from 'happy-dom';\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];\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\nexport type BootstrapFunction = () => void;\n\nexport interface RenderOptions {\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\") */\n baseUrl?: string;\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\n bootstraps?: BootstrapFunction[];\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 * ## SSR でできないこと\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\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.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 = new Window();\n const restoreGlobals = installGlobals(window);\n const document = window.document;\n\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール\n const restoreBaseUrl = options?.baseUrl\n ? installBaseUrl(options.baseUrl)\n : null;\n\n // bootstrap の解決\n const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();\n\n for (const bootstrap of bootstraps) {\n bootstrap();\n }\n\n try {\n\n // SSR モードを html 要素に設定\n document.documentElement.setAttribute('data-wcs-server', '');\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 const readyPromises: Promise<void>[] = [];\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 (!readyCtors.has(ctor) && typeof ctor.getBindingsReady === 'function') {\n readyCtors.add(ctor);\n readyPromises.push(ctor.getBindingsReady(document));\n }\n }\n\n if (connectedPromises.length === 0) break;\n await Promise.all(connectedPromises);\n }\n\n // 非同期初期化の完了を待機\n await Promise.all(readyPromises);\n\n return document.body.innerHTML;\n } finally {\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":["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","Window","restoreGlobals","document","restoreBaseUrl","bootstraps","bootstrapState","import","loadDefaultBootstraps","bootstrap","documentElement","setAttribute","body","innerHTML","MAX_ITERATIONS","awaitedElements","WeakSet","readyCtors","Set","readyPromises","i","connectedPromises","el","querySelectorAll","has","ctor","hasConnectedCallbackPromise","add","connectedCallbackPromise","getBindingsReady","length","all","close","VERSION","RenderCore","EventTarget","static","protocol","version","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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"}
|