@wcstack/server 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -6,14 +6,11 @@ declare function installBaseUrl(baseUrl: string): () => void;
6
6
  /** @deprecated Use Ssr.extractStateData() from @wcstack/state instead */
7
7
  declare function extractStateData(stateEl: any): Record<string, any>;
8
8
  type BootstrapFunction = () => void;
9
- type ReadyFunction = (doc: Document) => Promise<void>;
10
9
  interface RenderOptions {
11
10
  /** 相対 URL を解決するベース URL (例: "http://localhost:3001") */
12
11
  baseUrl?: string;
13
12
  /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */
14
13
  bootstraps?: BootstrapFunction[];
15
- /** バインディング等の非同期初期化完了を待機する関数の配列 */
16
- ready?: ReadyFunction[];
17
14
  }
18
15
  /**
19
16
  * HTML 文字列を SSR レンダリングして返す。
@@ -156,4 +153,4 @@ declare class RenderCore extends EventTarget {
156
153
  }
157
154
 
158
155
  export { GLOBALS_KEYS, RenderCore, VERSION, extractStateData, installBaseUrl, installGlobals, renderToString };
159
- export type { BootstrapFunction, IWcBindable, IWcBindableProperty, ReadyFunction, RenderOptions, WcsRenderValues };
156
+ export type { BootstrapFunction, IWcBindable, IWcBindableProperty, RenderOptions, WcsRenderValues };
package/dist/index.esm.js CHANGED
@@ -54,11 +54,8 @@ function extractStateData(stateEl) {
54
54
  return data;
55
55
  }
56
56
  async function loadDefaultBootstraps() {
57
- const { bootstrapState, getBindingsReady } = await import('@wcstack/state');
58
- return {
59
- bootstraps: [bootstrapState],
60
- ready: [(doc) => getBindingsReady(doc)],
61
- };
57
+ const { bootstrapState } = await import('@wcstack/state');
58
+ return [bootstrapState];
62
59
  }
63
60
  /**
64
61
  * HTML 文字列を SSR レンダリングして返す。
@@ -164,11 +161,8 @@ async function renderToString(html, options) {
164
161
  const restoreBaseUrl = options?.baseUrl
165
162
  ? installBaseUrl(options.baseUrl)
166
163
  : null;
167
- // bootstrap / ready の解決
168
- const hasCustomBootstraps = options?.bootstraps !== undefined;
169
- const defaults = hasCustomBootstraps ? null : await loadDefaultBootstraps();
170
- const bootstraps = options?.bootstraps ?? defaults.bootstraps;
171
- const readyFns = options?.ready ?? defaults?.ready ?? [];
164
+ // bootstrap の解決
165
+ const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();
172
166
  for (const bootstrap of bootstraps) {
173
167
  bootstrap();
174
168
  }
@@ -178,17 +172,23 @@ async function renderToString(html, options) {
178
172
  // HTML をパース
179
173
  // connectedCallback が自動発火 → state ロード → $connectedCallback 実行
180
174
  document.body.innerHTML = html;
181
- // connectedCallbackPromise プロトコル準拠の全カスタム要素の完了を待機
182
- const promises = [];
175
+ // connectedCallbackPromise / getBindingsReady プロトコルを自動検出
176
+ const connectedPromises = [];
177
+ const readyPromises = [];
178
+ const readyCtors = new Set();
183
179
  for (const el of document.querySelectorAll('*-*')) {
184
180
  const ctor = el.constructor;
185
181
  if (ctor.hasConnectedCallbackPromise) {
186
- promises.push(el.connectedCallbackPromise);
182
+ connectedPromises.push(el.connectedCallbackPromise);
183
+ }
184
+ if (!readyCtors.has(ctor) && typeof ctor.getBindingsReady === 'function') {
185
+ readyCtors.add(ctor);
186
+ readyPromises.push(ctor.getBindingsReady(document));
187
187
  }
188
188
  }
189
- await Promise.all(promises);
189
+ await Promise.all(connectedPromises);
190
190
  // 非同期初期化の完了を待機
191
- await Promise.all(readyFns.map(fn => fn(document)));
191
+ await Promise.all(readyPromises);
192
192
  return document.body.innerHTML;
193
193
  }
194
194
  finally {
@@ -198,7 +198,7 @@ async function renderToString(html, options) {
198
198
  }
199
199
  }
200
200
 
201
- var version = "0.2.3";
201
+ var version = "0.2.5";
202
202
  var pkg = {
203
203
  version: version};
204
204
 
@@ -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';\r\n\r\nexport const GLOBALS_KEYS = [\r\n 'document', 'customElements', 'HTMLElement',\r\n 'DocumentFragment', 'Node', 'NodeFilter', 'Comment', 'Text',\r\n 'MutationObserver', 'ShadowRoot', 'Element', 'HTMLTemplateElement',\r\n];\r\n\r\nexport function installGlobals(window: Window): () => void {\r\n const saved: Record<string, any> = {};\r\n for (const key of GLOBALS_KEYS) {\r\n saved[key] = (globalThis as any)[key];\r\n (globalThis as any)[key] = (window as any)[key];\r\n }\r\n // URL.createObjectURL を無効化して、\r\n // loadFromInnerScript が base64 data: URL フォールバックを使うようにする\r\n const origCreateObjectURL = URL.createObjectURL;\r\n (URL as any).createObjectURL = undefined;\r\n return () => {\r\n URL.createObjectURL = origCreateObjectURL;\r\n for (const key of GLOBALS_KEYS) {\r\n (globalThis as any)[key] = saved[key];\r\n }\r\n };\r\n}\r\n\r\nexport function installBaseUrl(baseUrl: string): () => void {\r\n const OrigURL = globalThis.URL;\r\n const base = baseUrl;\r\n globalThis.URL = class extends OrigURL {\r\n constructor(input: string | URL, inputBase?: string | URL) {\r\n if (typeof input === 'string' && input.startsWith('/') && inputBase === undefined) {\r\n super(input, base);\r\n } else {\r\n super(input as string, inputBase);\r\n }\r\n }\r\n } as typeof URL;\r\n // 静的メソッドを引き継ぐ\r\n globalThis.URL.createObjectURL = OrigURL.createObjectURL;\r\n globalThis.URL.revokeObjectURL = OrigURL.revokeObjectURL;\r\n return () => { globalThis.URL = OrigURL; };\r\n}\r\n\r\n/** @deprecated Use Ssr.extractStateData() from @wcstack/state instead */\r\nexport function extractStateData(stateEl: any): Record<string, any> {\r\n const raw = (stateEl as any).__state;\r\n if (!raw || typeof raw !== 'object') return {};\r\n const data: Record<string, any> = {};\r\n for (const [key, value] of Object.entries(raw)) {\r\n if (!key.startsWith('$') && typeof value !== 'function') {\r\n data[key] = value;\r\n }\r\n }\r\n return data;\r\n}\r\n\r\nexport type BootstrapFunction = () => void;\r\nexport type ReadyFunction = (doc: Document) => Promise<void>;\r\n\r\nexport interface RenderOptions {\r\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\") */\r\n baseUrl?: string;\r\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\r\n bootstraps?: BootstrapFunction[];\r\n /** バインディング等の非同期初期化完了を待機する関数の配列 */\r\n ready?: ReadyFunction[];\r\n}\r\n\r\nasync function loadDefaultBootstraps(): Promise<{\r\n bootstraps: BootstrapFunction[];\r\n ready: ReadyFunction[];\r\n}> {\r\n const { bootstrapState, getBindingsReady } = await import('@wcstack/state');\r\n return {\r\n bootstraps: [bootstrapState],\r\n ready: [(doc: Document) => getBindingsReady(doc as any)],\r\n };\r\n}\r\n\r\n/**\r\n * HTML 文字列を SSR レンダリングして返す。\r\n *\r\n * ## 入力 HTML のルール\r\n * - `<body>` の中身だけを渡す(`<html>`, `<head>`, `<body>` タグは含めない)\r\n * - `<script>` / `<link>` による外部リソース読み込みは実行されない\r\n * → 必要なパッケージは `options.bootstraps` で明示的に渡す\r\n *\r\n * ## SSR でできること\r\n *\r\n * ### 状態の初期化とデータ取得\r\n * - `<wcs-state>` の状態ロード(json 属性, src 属性, inline `<script type=\"module\">`)\r\n * - `$connectedCallback` でのサーバーサイド fetch(API 呼び出し、DB 問い合わせ等)\r\n *\r\n * ```html\r\n * <!-- JSON 直接指定 -->\r\n * <wcs-state enable-ssr json='{\"title\":\"Hello\"}'></wcs-state>\r\n *\r\n * <!-- $connectedCallback で API からデータ取得 -->\r\n * <!-- $connectedCallback は状態オブジェクトのメソッドとして定義し、this が state proxy -->\r\n * <wcs-state enable-ssr>\r\n * <script type=\"module\">\r\n * export default {\r\n * async $connectedCallback() {\r\n * const res = await fetch('/api/users');\r\n * this.users = await res.json();\r\n * }\r\n * };\r\n * </script>\r\n * </wcs-state>\r\n * ```\r\n *\r\n * ### wcs-fetch を使ったサーバー通信\r\n * - `<wcs-fetch>` の auto-fetch(`manual` なし)はサーバーでも実行される\r\n * - `manual` + `$connectedCallback` で明示的に制御する場合:\r\n *\r\n * ```html\r\n * <wcs-fetch id=\"api\" url=\"/api/users\" manual></wcs-fetch>\r\n * <wcs-state enable-ssr>\r\n * <script type=\"module\">\r\n * export default {\r\n * async $connectedCallback() {\r\n * const el = document.getElementById('api');\r\n * this.users = await el.fetch();\r\n * }\r\n * };\r\n * </script>\r\n * </wcs-state>\r\n * ```\r\n * ※ `bootstraps` に `bootstrapFetch` を含める必要あり\r\n *\r\n * ### バインディングと構造レンダリング\r\n * - `data-wcs` バインディングの適用(text, attribute, class, style, property)\r\n * - `<template data-wcs=\"for:\">` / `if:` / `elseif:` / `else:` の構造レンダリング\r\n *\r\n * ```html\r\n * <ul>\r\n * <template data-wcs=\"for: users\">\r\n * <li data-wcs=\"textContent: .name\"></li>\r\n * </template>\r\n * </ul>\r\n * <template data-wcs=\"if: isAdmin\">\r\n * <div class=\"admin-panel\">...</div>\r\n * </template>\r\n * ```\r\n *\r\n * ### ハイドレーション\r\n * - `enable-ssr` 付き `<wcs-state>` の `<wcs-ssr>` メタデータ自動生成\r\n * - クライアント側でのハイドレーション(再レンダリングなしでバインディング復元)\r\n * - `enable-ssr` を外した `<wcs-state>` はクライアントのみで動作(部分 CSR)\r\n *\r\n * ### カスタム要素の待機\r\n * - `static hasConnectedCallbackPromise = true` プロトコル準拠の全カスタム要素を自動待機\r\n *\r\n * ## SSR でできないこと\r\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\r\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\r\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\r\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\r\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\r\n *\r\n * ## HTML の分割パターン\r\n * ```\r\n * // server.js\r\n * const ssrBody = await renderToString(template, { ... });\r\n * const page = `<!DOCTYPE html>\r\n * <html lang=\"ja\">\r\n * <head>\r\n * <script type=\"module\" src=\"/packages/state/dist/auto.js\"></script>\r\n * </head>\r\n * <body>${ssrBody}</body>\r\n * </html>`;\r\n * ```\r\n * `renderToString` には `<body>` の中身だけを渡し、\r\n * `<head>` や `<script>` タグは外側のテンプレートで囲む。\r\n */\r\nexport async function renderToString(html: string, options?: RenderOptions): Promise<string> {\r\n const window = new Window();\r\n const restoreGlobals = installGlobals(window);\r\n const document = window.document;\r\n\r\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール\r\n const restoreBaseUrl = options?.baseUrl\r\n ? installBaseUrl(options.baseUrl)\r\n : null;\r\n\r\n // bootstrap / ready の解決\r\n const hasCustomBootstraps = options?.bootstraps !== undefined;\r\n const defaults = hasCustomBootstraps ? null : await loadDefaultBootstraps();\r\n const bootstraps = options?.bootstraps ?? defaults!.bootstraps;\r\n const readyFns = options?.ready ?? defaults?.ready ?? [];\r\n\r\n for (const bootstrap of bootstraps) {\r\n bootstrap();\r\n }\r\n\r\n try {\r\n\r\n // SSR モードを html 要素に設定\r\n document.documentElement.setAttribute('data-wcs-server', '');\r\n\r\n // HTML をパース\r\n // connectedCallback が自動発火 → state ロード → $connectedCallback 実行\r\n document.body.innerHTML = html;\r\n\r\n // connectedCallbackPromise プロトコル準拠の全カスタム要素の完了を待機\r\n const promises: Promise<void>[] = [];\r\n for (const el of document.querySelectorAll('*-*')) {\r\n const ctor = el.constructor as any;\r\n if (ctor.hasConnectedCallbackPromise) {\r\n promises.push((el as any).connectedCallbackPromise);\r\n }\r\n }\r\n await Promise.all(promises);\r\n\r\n // 非同期初期化の完了を待機\r\n await Promise.all(readyFns.map(fn => fn(document as any)));\r\n\r\n return document.body.innerHTML;\r\n } finally {\r\n restoreBaseUrl?.();\r\n restoreGlobals();\r\n await window.close();\r\n }\r\n}\r\n","import pkg from \"../package.json\" with { type: \"json\" };\r\nexport const VERSION = pkg.version;\r\n","import { renderToString } from \"./render.js\";\r\nimport { IWcBindable } from \"./types.js\";\r\n\r\nexport class RenderCore extends EventTarget {\r\n static wcBindable: IWcBindable = {\r\n protocol: \"wc-bindable\",\r\n version: 1,\r\n properties: [\r\n { name: \"html\", event: \"wcs-render:html-changed\" },\r\n { name: \"loading\", event: \"wcs-render:loading-changed\" },\r\n { name: \"error\", event: \"wcs-render:error\" },\r\n ],\r\n };\r\n\r\n private _html: string | null = null;\r\n private _loading: boolean = false;\r\n private _error: Error | null = null;\r\n\r\n get html(): string | null {\r\n return this._html;\r\n }\r\n\r\n get loading(): boolean {\r\n return this._loading;\r\n }\r\n\r\n get error(): Error | null {\r\n return this._error;\r\n }\r\n\r\n private _setLoading(loading: boolean): void {\r\n this._loading = loading;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:loading-changed\", {\r\n detail: loading,\r\n }));\r\n }\r\n\r\n private _setHtml(html: string): void {\r\n this._html = html;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:html-changed\", {\r\n detail: html,\r\n }));\r\n }\r\n\r\n private _setError(error: Error): void {\r\n this._error = error;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:error\", {\r\n detail: error,\r\n }));\r\n }\r\n\r\n async render(html: string): Promise<string | null> {\r\n this._setLoading(true);\r\n this._error = null;\r\n\r\n try {\r\n const result = await renderToString(html);\r\n this._setHtml(result);\r\n this._setLoading(false);\r\n return this._html;\r\n } catch (e: any) {\r\n this._setError(e instanceof Error ? e : new Error(String(e)));\r\n this._setLoading(false);\r\n return null;\r\n }\r\n }\r\n}\r\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';\r\n\r\nexport const GLOBALS_KEYS = [\r\n 'document', 'customElements', 'HTMLElement',\r\n 'DocumentFragment', 'Node', 'NodeFilter', 'Comment', 'Text',\r\n 'MutationObserver', 'ShadowRoot', 'Element', 'HTMLTemplateElement',\r\n];\r\n\r\nexport function installGlobals(window: Window): () => void {\r\n const saved: Record<string, any> = {};\r\n for (const key of GLOBALS_KEYS) {\r\n saved[key] = (globalThis as any)[key];\r\n (globalThis as any)[key] = (window as any)[key];\r\n }\r\n // URL.createObjectURL を無効化して、\r\n // loadFromInnerScript が base64 data: URL フォールバックを使うようにする\r\n const origCreateObjectURL = URL.createObjectURL;\r\n (URL as any).createObjectURL = undefined;\r\n return () => {\r\n URL.createObjectURL = origCreateObjectURL;\r\n for (const key of GLOBALS_KEYS) {\r\n (globalThis as any)[key] = saved[key];\r\n }\r\n };\r\n}\r\n\r\nexport function installBaseUrl(baseUrl: string): () => void {\r\n const OrigURL = globalThis.URL;\r\n const base = baseUrl;\r\n globalThis.URL = class extends OrigURL {\r\n constructor(input: string | URL, inputBase?: string | URL) {\r\n if (typeof input === 'string' && input.startsWith('/') && inputBase === undefined) {\r\n super(input, base);\r\n } else {\r\n super(input as string, inputBase);\r\n }\r\n }\r\n } as typeof URL;\r\n // 静的メソッドを引き継ぐ\r\n globalThis.URL.createObjectURL = OrigURL.createObjectURL;\r\n globalThis.URL.revokeObjectURL = OrigURL.revokeObjectURL;\r\n return () => { globalThis.URL = OrigURL; };\r\n}\r\n\r\n/** @deprecated Use Ssr.extractStateData() from @wcstack/state instead */\r\nexport function extractStateData(stateEl: any): Record<string, any> {\r\n const raw = (stateEl as any).__state;\r\n if (!raw || typeof raw !== 'object') return {};\r\n const data: Record<string, any> = {};\r\n for (const [key, value] of Object.entries(raw)) {\r\n if (!key.startsWith('$') && typeof value !== 'function') {\r\n data[key] = value;\r\n }\r\n }\r\n return data;\r\n}\r\n\r\nexport type BootstrapFunction = () => void;\r\n\r\nexport interface RenderOptions {\r\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\") */\r\n baseUrl?: string;\r\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\r\n bootstraps?: BootstrapFunction[];\r\n}\r\n\r\nasync function loadDefaultBootstraps(): Promise<BootstrapFunction[]> {\r\n const { bootstrapState } = await import('@wcstack/state');\r\n return [bootstrapState];\r\n}\r\n\r\n\r\n/**\r\n * HTML 文字列を SSR レンダリングして返す。\r\n *\r\n * ## 入力 HTML のルール\r\n * - `<body>` の中身だけを渡す(`<html>`, `<head>`, `<body>` タグは含めない)\r\n * - `<script>` / `<link>` による外部リソース読み込みは実行されない\r\n * → 必要なパッケージは `options.bootstraps` で明示的に渡す\r\n *\r\n * ## SSR でできること\r\n *\r\n * ### 状態の初期化とデータ取得\r\n * - `<wcs-state>` の状態ロード(json 属性, src 属性, inline `<script type=\"module\">`)\r\n * - `$connectedCallback` でのサーバーサイド fetch(API 呼び出し、DB 問い合わせ等)\r\n *\r\n * ```html\r\n * <!-- JSON 直接指定 -->\r\n * <wcs-state enable-ssr json='{\"title\":\"Hello\"}'></wcs-state>\r\n *\r\n * <!-- $connectedCallback で API からデータ取得 -->\r\n * <!-- $connectedCallback は状態オブジェクトのメソッドとして定義し、this が state proxy -->\r\n * <wcs-state enable-ssr>\r\n * <script type=\"module\">\r\n * export default {\r\n * async $connectedCallback() {\r\n * const res = await fetch('/api/users');\r\n * this.users = await res.json();\r\n * }\r\n * };\r\n * </script>\r\n * </wcs-state>\r\n * ```\r\n *\r\n * ### wcs-fetch を使ったサーバー通信\r\n * - `<wcs-fetch>` の auto-fetch(`manual` なし)はサーバーでも実行される\r\n * - `manual` + `$connectedCallback` で明示的に制御する場合:\r\n *\r\n * ```html\r\n * <wcs-fetch id=\"api\" url=\"/api/users\" manual></wcs-fetch>\r\n * <wcs-state enable-ssr>\r\n * <script type=\"module\">\r\n * export default {\r\n * async $connectedCallback() {\r\n * const el = document.getElementById('api');\r\n * this.users = await el.fetch();\r\n * }\r\n * };\r\n * </script>\r\n * </wcs-state>\r\n * ```\r\n * ※ `bootstraps` に `bootstrapFetch` を含める必要あり\r\n *\r\n * ### バインディングと構造レンダリング\r\n * - `data-wcs` バインディングの適用(text, attribute, class, style, property)\r\n * - `<template data-wcs=\"for:\">` / `if:` / `elseif:` / `else:` の構造レンダリング\r\n *\r\n * ```html\r\n * <ul>\r\n * <template data-wcs=\"for: users\">\r\n * <li data-wcs=\"textContent: .name\"></li>\r\n * </template>\r\n * </ul>\r\n * <template data-wcs=\"if: isAdmin\">\r\n * <div class=\"admin-panel\">...</div>\r\n * </template>\r\n * ```\r\n *\r\n * ### ハイドレーション\r\n * - `enable-ssr` 付き `<wcs-state>` の `<wcs-ssr>` メタデータ自動生成\r\n * - クライアント側でのハイドレーション(再レンダリングなしでバインディング復元)\r\n * - `enable-ssr` を外した `<wcs-state>` はクライアントのみで動作(部分 CSR)\r\n *\r\n * ### カスタム要素の待機\r\n * - `static hasConnectedCallbackPromise = true` プロトコル準拠の全カスタム要素を自動待機\r\n *\r\n * ## SSR でできないこと\r\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\r\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\r\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\r\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\r\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\r\n *\r\n * ## HTML の分割パターン\r\n * ```\r\n * // server.js\r\n * const ssrBody = await renderToString(template, { ... });\r\n * const page = `<!DOCTYPE html>\r\n * <html lang=\"ja\">\r\n * <head>\r\n * <script type=\"module\" src=\"/packages/state/dist/auto.js\"></script>\r\n * </head>\r\n * <body>${ssrBody}</body>\r\n * </html>`;\r\n * ```\r\n * `renderToString` には `<body>` の中身だけを渡し、\r\n * `<head>` や `<script>` タグは外側のテンプレートで囲む。\r\n */\r\nexport async function renderToString(html: string, options?: RenderOptions): Promise<string> {\r\n const window = new Window();\r\n const restoreGlobals = installGlobals(window);\r\n const document = window.document;\r\n\r\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール\r\n const restoreBaseUrl = options?.baseUrl\r\n ? installBaseUrl(options.baseUrl)\r\n : null;\r\n\r\n // bootstrap の解決\r\n const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();\r\n\r\n for (const bootstrap of bootstraps) {\r\n bootstrap();\r\n }\r\n\r\n try {\r\n\r\n // SSR モードを html 要素に設定\r\n document.documentElement.setAttribute('data-wcs-server', '');\r\n\r\n // HTML をパース\r\n // connectedCallback が自動発火 → state ロード → $connectedCallback 実行\r\n document.body.innerHTML = html;\r\n\r\n // connectedCallbackPromise / getBindingsReady プロトコルを自動検出\r\n const connectedPromises: Promise<void>[] = [];\r\n const readyPromises: Promise<void>[] = [];\r\n const readyCtors = new Set<any>();\r\n for (const el of document.querySelectorAll('*-*')) {\r\n const ctor = el.constructor as any;\r\n if (ctor.hasConnectedCallbackPromise) {\r\n connectedPromises.push((el as any).connectedCallbackPromise);\r\n }\r\n if (!readyCtors.has(ctor) && typeof ctor.getBindingsReady === 'function') {\r\n readyCtors.add(ctor);\r\n readyPromises.push(ctor.getBindingsReady(document));\r\n }\r\n }\r\n await Promise.all(connectedPromises);\r\n\r\n // 非同期初期化の完了を待機\r\n await Promise.all(readyPromises);\r\n\r\n return document.body.innerHTML;\r\n } finally {\r\n restoreBaseUrl?.();\r\n restoreGlobals();\r\n await window.close();\r\n }\r\n}\r\n","import pkg from \"../package.json\" with { type: \"json\" };\r\nexport const VERSION = pkg.version;\r\n","import { renderToString } from \"./render.js\";\r\nimport { IWcBindable } from \"./types.js\";\r\n\r\nexport class RenderCore extends EventTarget {\r\n static wcBindable: IWcBindable = {\r\n protocol: \"wc-bindable\",\r\n version: 1,\r\n properties: [\r\n { name: \"html\", event: \"wcs-render:html-changed\" },\r\n { name: \"loading\", event: \"wcs-render:loading-changed\" },\r\n { name: \"error\", event: \"wcs-render:error\" },\r\n ],\r\n };\r\n\r\n private _html: string | null = null;\r\n private _loading: boolean = false;\r\n private _error: Error | null = null;\r\n\r\n get html(): string | null {\r\n return this._html;\r\n }\r\n\r\n get loading(): boolean {\r\n return this._loading;\r\n }\r\n\r\n get error(): Error | null {\r\n return this._error;\r\n }\r\n\r\n private _setLoading(loading: boolean): void {\r\n this._loading = loading;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:loading-changed\", {\r\n detail: loading,\r\n }));\r\n }\r\n\r\n private _setHtml(html: string): void {\r\n this._html = html;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:html-changed\", {\r\n detail: html,\r\n }));\r\n }\r\n\r\n private _setError(error: Error): void {\r\n this._error = error;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:error\", {\r\n detail: error,\r\n }));\r\n }\r\n\r\n async render(html: string): Promise<string | null> {\r\n this._setLoading(true);\r\n this._error = null;\r\n\r\n try {\r\n const result = await renderToString(html);\r\n this._setHtml(result);\r\n this._setLoading(false);\r\n return this._html;\r\n } catch (e: any) {\r\n this._setError(e instanceof Error ? e : new Error(String(e)));\r\n this._setLoading(false);\r\n return null;\r\n }\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{Window as t}from"happy-dom";const e=["document","customElements","HTMLElement","DocumentFragment","Node","NodeFilter","Comment","Text","MutationObserver","ShadowRoot","Element","HTMLTemplateElement"];function r(t){const r={};for(const n of e)r[n]=globalThis[n],globalThis[n]=t[n];const n=URL.createObjectURL;return URL.createObjectURL=void 0,()=>{URL.createObjectURL=n;for(const t of e)globalThis[t]=r[t]}}function n(t){const e=globalThis.URL,r=t;return globalThis.URL=class extends e{constructor(t,e){"string"==typeof t&&t.startsWith("/")&&void 0===e?super(t,r):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 r={};for(const[t,n]of Object.entries(e))t.startsWith("$")||"function"==typeof n||(r[t]=n);return r}async function s(e,o){const s=new t,a=r(s),i=s.document,c=o?.baseUrl?n(o.baseUrl):null,l=void 0!==o?.bootstraps?null:await async function(){const{bootstrapState:t,getBindingsReady:e}=await import("@wcstack/state");return{bootstraps:[t],ready:[t=>e(t)]}}(),d=o?.bootstraps??l.bootstraps,h=o?.ready??l?.ready??[];for(const t of d)t();try{i.documentElement.setAttribute("data-wcs-server",""),i.body.innerHTML=e;const t=[];for(const e of i.querySelectorAll("*-*")){e.constructor.hasConnectedCallbackPromise&&t.push(e.connectedCallbackPromise)}return await Promise.all(t),await Promise.all(h.map(t=>t(i))),i.body.innerHTML}finally{c?.(),a(),await s.close()}}const a="0.2.3";class i extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"html",event:"wcs-render:html-changed"},{name:"loading",event:"wcs-render:loading-changed"},{name:"error",event:"wcs-render:error"}]};_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._error=null;try{const e=await s(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{e as GLOBALS_KEYS,i as RenderCore,a as VERSION,o as extractStateData,n as installBaseUrl,r as installGlobals,s as renderToString};
1
+ import{Window as t}from"happy-dom";const e=["document","customElements","HTMLElement","DocumentFragment","Node","NodeFilter","Comment","Text","MutationObserver","ShadowRoot","Element","HTMLTemplateElement"];function n(t){const n={};for(const r of e)n[r]=globalThis[r],globalThis[r]=t[r];const r=URL.createObjectURL;return URL.createObjectURL=void 0,()=>{URL.createObjectURL=r;for(const t of e)globalThis[t]=n[t]}}function r(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 s(e,o){const s=new t,a=n(s),i=s.document,c=o?.baseUrl?r(o.baseUrl):null,l=o?.bootstraps??await async function(){const{bootstrapState:t}=await import("@wcstack/state");return[t]}();for(const t of l)t();try{i.documentElement.setAttribute("data-wcs-server",""),i.body.innerHTML=e;const t=[],n=[],r=new Set;for(const e of i.querySelectorAll("*-*")){const o=e.constructor;o.hasConnectedCallbackPromise&&t.push(e.connectedCallbackPromise),r.has(o)||"function"!=typeof o.getBindingsReady||(r.add(o),n.push(o.getBindingsReady(i)))}return await Promise.all(t),await Promise.all(n),i.body.innerHTML}finally{c?.(),a(),await s.close()}}const a="0.2.5";class i extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"html",event:"wcs-render:html-changed"},{name:"loading",event:"wcs-render:loading-changed"},{name:"error",event:"wcs-render:error"}]};_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._error=null;try{const e=await s(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{e as GLOBALS_KEYS,i as RenderCore,a as VERSION,o as extractStateData,r as installBaseUrl,n as installGlobals,s 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';\r\n\r\nexport const GLOBALS_KEYS = [\r\n 'document', 'customElements', 'HTMLElement',\r\n 'DocumentFragment', 'Node', 'NodeFilter', 'Comment', 'Text',\r\n 'MutationObserver', 'ShadowRoot', 'Element', 'HTMLTemplateElement',\r\n];\r\n\r\nexport function installGlobals(window: Window): () => void {\r\n const saved: Record<string, any> = {};\r\n for (const key of GLOBALS_KEYS) {\r\n saved[key] = (globalThis as any)[key];\r\n (globalThis as any)[key] = (window as any)[key];\r\n }\r\n // URL.createObjectURL を無効化して、\r\n // loadFromInnerScript が base64 data: URL フォールバックを使うようにする\r\n const origCreateObjectURL = URL.createObjectURL;\r\n (URL as any).createObjectURL = undefined;\r\n return () => {\r\n URL.createObjectURL = origCreateObjectURL;\r\n for (const key of GLOBALS_KEYS) {\r\n (globalThis as any)[key] = saved[key];\r\n }\r\n };\r\n}\r\n\r\nexport function installBaseUrl(baseUrl: string): () => void {\r\n const OrigURL = globalThis.URL;\r\n const base = baseUrl;\r\n globalThis.URL = class extends OrigURL {\r\n constructor(input: string | URL, inputBase?: string | URL) {\r\n if (typeof input === 'string' && input.startsWith('/') && inputBase === undefined) {\r\n super(input, base);\r\n } else {\r\n super(input as string, inputBase);\r\n }\r\n }\r\n } as typeof URL;\r\n // 静的メソッドを引き継ぐ\r\n globalThis.URL.createObjectURL = OrigURL.createObjectURL;\r\n globalThis.URL.revokeObjectURL = OrigURL.revokeObjectURL;\r\n return () => { globalThis.URL = OrigURL; };\r\n}\r\n\r\n/** @deprecated Use Ssr.extractStateData() from @wcstack/state instead */\r\nexport function extractStateData(stateEl: any): Record<string, any> {\r\n const raw = (stateEl as any).__state;\r\n if (!raw || typeof raw !== 'object') return {};\r\n const data: Record<string, any> = {};\r\n for (const [key, value] of Object.entries(raw)) {\r\n if (!key.startsWith('$') && typeof value !== 'function') {\r\n data[key] = value;\r\n }\r\n }\r\n return data;\r\n}\r\n\r\nexport type BootstrapFunction = () => void;\r\nexport type ReadyFunction = (doc: Document) => Promise<void>;\r\n\r\nexport interface RenderOptions {\r\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\") */\r\n baseUrl?: string;\r\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\r\n bootstraps?: BootstrapFunction[];\r\n /** バインディング等の非同期初期化完了を待機する関数の配列 */\r\n ready?: ReadyFunction[];\r\n}\r\n\r\nasync function loadDefaultBootstraps(): Promise<{\r\n bootstraps: BootstrapFunction[];\r\n ready: ReadyFunction[];\r\n}> {\r\n const { bootstrapState, getBindingsReady } = await import('@wcstack/state');\r\n return {\r\n bootstraps: [bootstrapState],\r\n ready: [(doc: Document) => getBindingsReady(doc as any)],\r\n };\r\n}\r\n\r\n/**\r\n * HTML 文字列を SSR レンダリングして返す。\r\n *\r\n * ## 入力 HTML のルール\r\n * - `<body>` の中身だけを渡す(`<html>`, `<head>`, `<body>` タグは含めない)\r\n * - `<script>` / `<link>` による外部リソース読み込みは実行されない\r\n * → 必要なパッケージは `options.bootstraps` で明示的に渡す\r\n *\r\n * ## SSR でできること\r\n *\r\n * ### 状態の初期化とデータ取得\r\n * - `<wcs-state>` の状態ロード(json 属性, src 属性, inline `<script type=\"module\">`)\r\n * - `$connectedCallback` でのサーバーサイド fetch(API 呼び出し、DB 問い合わせ等)\r\n *\r\n * ```html\r\n * <!-- JSON 直接指定 -->\r\n * <wcs-state enable-ssr json='{\"title\":\"Hello\"}'></wcs-state>\r\n *\r\n * <!-- $connectedCallback で API からデータ取得 -->\r\n * <!-- $connectedCallback は状態オブジェクトのメソッドとして定義し、this が state proxy -->\r\n * <wcs-state enable-ssr>\r\n * <script type=\"module\">\r\n * export default {\r\n * async $connectedCallback() {\r\n * const res = await fetch('/api/users');\r\n * this.users = await res.json();\r\n * }\r\n * };\r\n * </script>\r\n * </wcs-state>\r\n * ```\r\n *\r\n * ### wcs-fetch を使ったサーバー通信\r\n * - `<wcs-fetch>` の auto-fetch(`manual` なし)はサーバーでも実行される\r\n * - `manual` + `$connectedCallback` で明示的に制御する場合:\r\n *\r\n * ```html\r\n * <wcs-fetch id=\"api\" url=\"/api/users\" manual></wcs-fetch>\r\n * <wcs-state enable-ssr>\r\n * <script type=\"module\">\r\n * export default {\r\n * async $connectedCallback() {\r\n * const el = document.getElementById('api');\r\n * this.users = await el.fetch();\r\n * }\r\n * };\r\n * </script>\r\n * </wcs-state>\r\n * ```\r\n * ※ `bootstraps` に `bootstrapFetch` を含める必要あり\r\n *\r\n * ### バインディングと構造レンダリング\r\n * - `data-wcs` バインディングの適用(text, attribute, class, style, property)\r\n * - `<template data-wcs=\"for:\">` / `if:` / `elseif:` / `else:` の構造レンダリング\r\n *\r\n * ```html\r\n * <ul>\r\n * <template data-wcs=\"for: users\">\r\n * <li data-wcs=\"textContent: .name\"></li>\r\n * </template>\r\n * </ul>\r\n * <template data-wcs=\"if: isAdmin\">\r\n * <div class=\"admin-panel\">...</div>\r\n * </template>\r\n * ```\r\n *\r\n * ### ハイドレーション\r\n * - `enable-ssr` 付き `<wcs-state>` の `<wcs-ssr>` メタデータ自動生成\r\n * - クライアント側でのハイドレーション(再レンダリングなしでバインディング復元)\r\n * - `enable-ssr` を外した `<wcs-state>` はクライアントのみで動作(部分 CSR)\r\n *\r\n * ### カスタム要素の待機\r\n * - `static hasConnectedCallbackPromise = true` プロトコル準拠の全カスタム要素を自動待機\r\n *\r\n * ## SSR でできないこと\r\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\r\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\r\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\r\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\r\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\r\n *\r\n * ## HTML の分割パターン\r\n * ```\r\n * // server.js\r\n * const ssrBody = await renderToString(template, { ... });\r\n * const page = `<!DOCTYPE html>\r\n * <html lang=\"ja\">\r\n * <head>\r\n * <script type=\"module\" src=\"/packages/state/dist/auto.js\"></script>\r\n * </head>\r\n * <body>${ssrBody}</body>\r\n * </html>`;\r\n * ```\r\n * `renderToString` には `<body>` の中身だけを渡し、\r\n * `<head>` や `<script>` タグは外側のテンプレートで囲む。\r\n */\r\nexport async function renderToString(html: string, options?: RenderOptions): Promise<string> {\r\n const window = new Window();\r\n const restoreGlobals = installGlobals(window);\r\n const document = window.document;\r\n\r\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール\r\n const restoreBaseUrl = options?.baseUrl\r\n ? installBaseUrl(options.baseUrl)\r\n : null;\r\n\r\n // bootstrap / ready の解決\r\n const hasCustomBootstraps = options?.bootstraps !== undefined;\r\n const defaults = hasCustomBootstraps ? null : await loadDefaultBootstraps();\r\n const bootstraps = options?.bootstraps ?? defaults!.bootstraps;\r\n const readyFns = options?.ready ?? defaults?.ready ?? [];\r\n\r\n for (const bootstrap of bootstraps) {\r\n bootstrap();\r\n }\r\n\r\n try {\r\n\r\n // SSR モードを html 要素に設定\r\n document.documentElement.setAttribute('data-wcs-server', '');\r\n\r\n // HTML をパース\r\n // connectedCallback が自動発火 → state ロード → $connectedCallback 実行\r\n document.body.innerHTML = html;\r\n\r\n // connectedCallbackPromise プロトコル準拠の全カスタム要素の完了を待機\r\n const promises: Promise<void>[] = [];\r\n for (const el of document.querySelectorAll('*-*')) {\r\n const ctor = el.constructor as any;\r\n if (ctor.hasConnectedCallbackPromise) {\r\n promises.push((el as any).connectedCallbackPromise);\r\n }\r\n }\r\n await Promise.all(promises);\r\n\r\n // 非同期初期化の完了を待機\r\n await Promise.all(readyFns.map(fn => fn(document as any)));\r\n\r\n return document.body.innerHTML;\r\n } finally {\r\n restoreBaseUrl?.();\r\n restoreGlobals();\r\n await window.close();\r\n }\r\n}\r\n","import pkg from \"../package.json\" with { type: \"json\" };\r\nexport const VERSION = pkg.version;\r\n","import { renderToString } from \"./render.js\";\r\nimport { IWcBindable } from \"./types.js\";\r\n\r\nexport class RenderCore extends EventTarget {\r\n static wcBindable: IWcBindable = {\r\n protocol: \"wc-bindable\",\r\n version: 1,\r\n properties: [\r\n { name: \"html\", event: \"wcs-render:html-changed\" },\r\n { name: \"loading\", event: \"wcs-render:loading-changed\" },\r\n { name: \"error\", event: \"wcs-render:error\" },\r\n ],\r\n };\r\n\r\n private _html: string | null = null;\r\n private _loading: boolean = false;\r\n private _error: Error | null = null;\r\n\r\n get html(): string | null {\r\n return this._html;\r\n }\r\n\r\n get loading(): boolean {\r\n return this._loading;\r\n }\r\n\r\n get error(): Error | null {\r\n return this._error;\r\n }\r\n\r\n private _setLoading(loading: boolean): void {\r\n this._loading = loading;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:loading-changed\", {\r\n detail: loading,\r\n }));\r\n }\r\n\r\n private _setHtml(html: string): void {\r\n this._html = html;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:html-changed\", {\r\n detail: html,\r\n }));\r\n }\r\n\r\n private _setError(error: Error): void {\r\n this._error = error;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:error\", {\r\n detail: error,\r\n }));\r\n }\r\n\r\n async render(html: string): Promise<string | null> {\r\n this._setLoading(true);\r\n this._error = null;\r\n\r\n try {\r\n const result = await renderToString(html);\r\n this._setHtml(result);\r\n this._setLoading(false);\r\n return this._html;\r\n } catch (e: any) {\r\n this._setError(e instanceof Error ? e : new Error(String(e)));\r\n this._setLoading(false);\r\n return null;\r\n }\r\n }\r\n}\r\n"],"names":["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","Window","restoreGlobals","document","restoreBaseUrl","defaults","bootstraps","bootstrapState","getBindingsReady","import","ready","doc","loadDefaultBootstraps","readyFns","bootstrap","documentElement","setAttribute","body","innerHTML","promises","el","querySelectorAll","hasConnectedCallbackPromise","push","connectedCallbackPromise","Promise","all","map","fn","close","VERSION","RenderCore","EventTarget","static","protocol","version","properties","name","event","_html","_loading","_error","this","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';\r\n\r\nexport const GLOBALS_KEYS = [\r\n 'document', 'customElements', 'HTMLElement',\r\n 'DocumentFragment', 'Node', 'NodeFilter', 'Comment', 'Text',\r\n 'MutationObserver', 'ShadowRoot', 'Element', 'HTMLTemplateElement',\r\n];\r\n\r\nexport function installGlobals(window: Window): () => void {\r\n const saved: Record<string, any> = {};\r\n for (const key of GLOBALS_KEYS) {\r\n saved[key] = (globalThis as any)[key];\r\n (globalThis as any)[key] = (window as any)[key];\r\n }\r\n // URL.createObjectURL を無効化して、\r\n // loadFromInnerScript が base64 data: URL フォールバックを使うようにする\r\n const origCreateObjectURL = URL.createObjectURL;\r\n (URL as any).createObjectURL = undefined;\r\n return () => {\r\n URL.createObjectURL = origCreateObjectURL;\r\n for (const key of GLOBALS_KEYS) {\r\n (globalThis as any)[key] = saved[key];\r\n }\r\n };\r\n}\r\n\r\nexport function installBaseUrl(baseUrl: string): () => void {\r\n const OrigURL = globalThis.URL;\r\n const base = baseUrl;\r\n globalThis.URL = class extends OrigURL {\r\n constructor(input: string | URL, inputBase?: string | URL) {\r\n if (typeof input === 'string' && input.startsWith('/') && inputBase === undefined) {\r\n super(input, base);\r\n } else {\r\n super(input as string, inputBase);\r\n }\r\n }\r\n } as typeof URL;\r\n // 静的メソッドを引き継ぐ\r\n globalThis.URL.createObjectURL = OrigURL.createObjectURL;\r\n globalThis.URL.revokeObjectURL = OrigURL.revokeObjectURL;\r\n return () => { globalThis.URL = OrigURL; };\r\n}\r\n\r\n/** @deprecated Use Ssr.extractStateData() from @wcstack/state instead */\r\nexport function extractStateData(stateEl: any): Record<string, any> {\r\n const raw = (stateEl as any).__state;\r\n if (!raw || typeof raw !== 'object') return {};\r\n const data: Record<string, any> = {};\r\n for (const [key, value] of Object.entries(raw)) {\r\n if (!key.startsWith('$') && typeof value !== 'function') {\r\n data[key] = value;\r\n }\r\n }\r\n return data;\r\n}\r\n\r\nexport type BootstrapFunction = () => void;\r\n\r\nexport interface RenderOptions {\r\n /** 相対 URL を解決するベース URL (例: \"http://localhost:3001\") */\r\n baseUrl?: string;\r\n /** bootstrap 関数の配列。省略時は @wcstack/state を自動ロード */\r\n bootstraps?: BootstrapFunction[];\r\n}\r\n\r\nasync function loadDefaultBootstraps(): Promise<BootstrapFunction[]> {\r\n const { bootstrapState } = await import('@wcstack/state');\r\n return [bootstrapState];\r\n}\r\n\r\n\r\n/**\r\n * HTML 文字列を SSR レンダリングして返す。\r\n *\r\n * ## 入力 HTML のルール\r\n * - `<body>` の中身だけを渡す(`<html>`, `<head>`, `<body>` タグは含めない)\r\n * - `<script>` / `<link>` による外部リソース読み込みは実行されない\r\n * → 必要なパッケージは `options.bootstraps` で明示的に渡す\r\n *\r\n * ## SSR でできること\r\n *\r\n * ### 状態の初期化とデータ取得\r\n * - `<wcs-state>` の状態ロード(json 属性, src 属性, inline `<script type=\"module\">`)\r\n * - `$connectedCallback` でのサーバーサイド fetch(API 呼び出し、DB 問い合わせ等)\r\n *\r\n * ```html\r\n * <!-- JSON 直接指定 -->\r\n * <wcs-state enable-ssr json='{\"title\":\"Hello\"}'></wcs-state>\r\n *\r\n * <!-- $connectedCallback で API からデータ取得 -->\r\n * <!-- $connectedCallback は状態オブジェクトのメソッドとして定義し、this が state proxy -->\r\n * <wcs-state enable-ssr>\r\n * <script type=\"module\">\r\n * export default {\r\n * async $connectedCallback() {\r\n * const res = await fetch('/api/users');\r\n * this.users = await res.json();\r\n * }\r\n * };\r\n * </script>\r\n * </wcs-state>\r\n * ```\r\n *\r\n * ### wcs-fetch を使ったサーバー通信\r\n * - `<wcs-fetch>` の auto-fetch(`manual` なし)はサーバーでも実行される\r\n * - `manual` + `$connectedCallback` で明示的に制御する場合:\r\n *\r\n * ```html\r\n * <wcs-fetch id=\"api\" url=\"/api/users\" manual></wcs-fetch>\r\n * <wcs-state enable-ssr>\r\n * <script type=\"module\">\r\n * export default {\r\n * async $connectedCallback() {\r\n * const el = document.getElementById('api');\r\n * this.users = await el.fetch();\r\n * }\r\n * };\r\n * </script>\r\n * </wcs-state>\r\n * ```\r\n * ※ `bootstraps` に `bootstrapFetch` を含める必要あり\r\n *\r\n * ### バインディングと構造レンダリング\r\n * - `data-wcs` バインディングの適用(text, attribute, class, style, property)\r\n * - `<template data-wcs=\"for:\">` / `if:` / `elseif:` / `else:` の構造レンダリング\r\n *\r\n * ```html\r\n * <ul>\r\n * <template data-wcs=\"for: users\">\r\n * <li data-wcs=\"textContent: .name\"></li>\r\n * </template>\r\n * </ul>\r\n * <template data-wcs=\"if: isAdmin\">\r\n * <div class=\"admin-panel\">...</div>\r\n * </template>\r\n * ```\r\n *\r\n * ### ハイドレーション\r\n * - `enable-ssr` 付き `<wcs-state>` の `<wcs-ssr>` メタデータ自動生成\r\n * - クライアント側でのハイドレーション(再レンダリングなしでバインディング復元)\r\n * - `enable-ssr` を外した `<wcs-state>` はクライアントのみで動作(部分 CSR)\r\n *\r\n * ### カスタム要素の待機\r\n * - `static hasConnectedCallbackPromise = true` プロトコル準拠の全カスタム要素を自動待機\r\n *\r\n * ## SSR でできないこと\r\n * - `<head>` 内の `<script src=\"...\">` や `<link>` の自動実行\r\n * - ブラウザ固有 API(localStorage, sessionStorage, navigator 等)\r\n * - Shadow DOM のレンダリング(Declarative Shadow DOM 非対応)\r\n * - イベントハンドラの登録(クライアント側のハイドレーションで復元)\r\n * - `<wcs-autoloader>` による動的コンポーネント読み込み\r\n *\r\n * ## HTML の分割パターン\r\n * ```\r\n * // server.js\r\n * const ssrBody = await renderToString(template, { ... });\r\n * const page = `<!DOCTYPE html>\r\n * <html lang=\"ja\">\r\n * <head>\r\n * <script type=\"module\" src=\"/packages/state/dist/auto.js\"></script>\r\n * </head>\r\n * <body>${ssrBody}</body>\r\n * </html>`;\r\n * ```\r\n * `renderToString` には `<body>` の中身だけを渡し、\r\n * `<head>` や `<script>` タグは外側のテンプレートで囲む。\r\n */\r\nexport async function renderToString(html: string, options?: RenderOptions): Promise<string> {\r\n const window = new Window();\r\n const restoreGlobals = installGlobals(window);\r\n const document = window.document;\r\n\r\n // 相対 URL を baseUrl で解決する URL コンストラクタパッチをインストール\r\n const restoreBaseUrl = options?.baseUrl\r\n ? installBaseUrl(options.baseUrl)\r\n : null;\r\n\r\n // bootstrap の解決\r\n const bootstraps = options?.bootstraps ?? await loadDefaultBootstraps();\r\n\r\n for (const bootstrap of bootstraps) {\r\n bootstrap();\r\n }\r\n\r\n try {\r\n\r\n // SSR モードを html 要素に設定\r\n document.documentElement.setAttribute('data-wcs-server', '');\r\n\r\n // HTML をパース\r\n // connectedCallback が自動発火 → state ロード → $connectedCallback 実行\r\n document.body.innerHTML = html;\r\n\r\n // connectedCallbackPromise / getBindingsReady プロトコルを自動検出\r\n const connectedPromises: Promise<void>[] = [];\r\n const readyPromises: Promise<void>[] = [];\r\n const readyCtors = new Set<any>();\r\n for (const el of document.querySelectorAll('*-*')) {\r\n const ctor = el.constructor as any;\r\n if (ctor.hasConnectedCallbackPromise) {\r\n connectedPromises.push((el as any).connectedCallbackPromise);\r\n }\r\n if (!readyCtors.has(ctor) && typeof ctor.getBindingsReady === 'function') {\r\n readyCtors.add(ctor);\r\n readyPromises.push(ctor.getBindingsReady(document));\r\n }\r\n }\r\n await Promise.all(connectedPromises);\r\n\r\n // 非同期初期化の完了を待機\r\n await Promise.all(readyPromises);\r\n\r\n return document.body.innerHTML;\r\n } finally {\r\n restoreBaseUrl?.();\r\n restoreGlobals();\r\n await window.close();\r\n }\r\n}\r\n","import pkg from \"../package.json\" with { type: \"json\" };\r\nexport const VERSION = pkg.version;\r\n","import { renderToString } from \"./render.js\";\r\nimport { IWcBindable } from \"./types.js\";\r\n\r\nexport class RenderCore extends EventTarget {\r\n static wcBindable: IWcBindable = {\r\n protocol: \"wc-bindable\",\r\n version: 1,\r\n properties: [\r\n { name: \"html\", event: \"wcs-render:html-changed\" },\r\n { name: \"loading\", event: \"wcs-render:loading-changed\" },\r\n { name: \"error\", event: \"wcs-render:error\" },\r\n ],\r\n };\r\n\r\n private _html: string | null = null;\r\n private _loading: boolean = false;\r\n private _error: Error | null = null;\r\n\r\n get html(): string | null {\r\n return this._html;\r\n }\r\n\r\n get loading(): boolean {\r\n return this._loading;\r\n }\r\n\r\n get error(): Error | null {\r\n return this._error;\r\n }\r\n\r\n private _setLoading(loading: boolean): void {\r\n this._loading = loading;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:loading-changed\", {\r\n detail: loading,\r\n }));\r\n }\r\n\r\n private _setHtml(html: string): void {\r\n this._html = html;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:html-changed\", {\r\n detail: html,\r\n }));\r\n }\r\n\r\n private _setError(error: Error): void {\r\n this._error = error;\r\n this.dispatchEvent(new CustomEvent(\"wcs-render:error\", {\r\n detail: error,\r\n }));\r\n }\r\n\r\n async render(html: string): Promise<string | null> {\r\n this._setLoading(true);\r\n this._error = null;\r\n\r\n try {\r\n const result = await renderToString(html);\r\n this._setHtml(result);\r\n this._setLoading(false);\r\n return this._html;\r\n } catch (e: any) {\r\n this._setError(e instanceof Error ? e : new Error(String(e)));\r\n this._setLoading(false);\r\n return null;\r\n }\r\n }\r\n}\r\n"],"names":["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","Window","restoreGlobals","document","restoreBaseUrl","bootstraps","bootstrapState","import","loadDefaultBootstraps","bootstrap","documentElement","setAttribute","body","innerHTML","connectedPromises","readyPromises","readyCtors","Set","el","querySelectorAll","ctor","hasConnectedCallbackPromise","push","connectedCallbackPromise","has","getBindingsReady","add","Promise","all","close","VERSION","RenderCore","EventTarget","static","protocol","version","properties","name","event","_html","_loading","_error","this","loading","error","_setLoading","dispatchEvent","CustomEvent","detail","_setHtml","_setError","render","result","e","Error","String"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/server",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
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": "file:../state",
45
+ "@wcstack/state": "^1.6.0",
46
46
  "happy-dom": "^20.0.11"
47
47
  },
48
48
  "devDependencies": {