@sigx/server-renderer 0.1.5 → 0.1.7
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/builtin-ssr-directives.d.ts +8 -0
- package/dist/builtin-ssr-directives.d.ts.map +1 -0
- package/dist/client/hydrate-component.d.ts +32 -0
- package/dist/client/hydrate-component.d.ts.map +1 -0
- package/dist/client/hydrate-context.d.ts +54 -0
- package/dist/client/hydrate-context.d.ts.map +1 -0
- package/dist/client/hydrate-core.d.ts +33 -0
- package/dist/client/hydrate-core.d.ts.map +1 -0
- package/dist/client/index.d.ts +9 -4
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -661
- package/dist/client/plugin.d.ts +52 -0
- package/dist/client/plugin.d.ts.map +1 -0
- package/dist/client-directives.d.ts +4 -37
- package/dist/client-directives.d.ts.map +1 -1
- package/dist/client-ggDL-Wx2.js +309 -0
- package/dist/client-ggDL-Wx2.js.map +1 -0
- package/dist/directive-ssr-types.d.ts +23 -0
- package/dist/directive-ssr-types.d.ts.map +1 -0
- package/dist/head.d.ts +97 -0
- package/dist/head.d.ts.map +1 -0
- package/dist/index.d.ts +26 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1170
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +124 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/server/context.d.ts +52 -59
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/index.d.ts +9 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -574
- package/dist/server/render-api.d.ts +64 -0
- package/dist/server/render-api.d.ts.map +1 -0
- package/dist/server/render-core.d.ts +46 -0
- package/dist/server/render-core.d.ts.map +1 -0
- package/dist/server/streaming.d.ts +24 -0
- package/dist/server/streaming.d.ts.map +1 -0
- package/dist/server/types.d.ts +40 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server-UBcHtkm-.js +829 -0
- package/dist/server-UBcHtkm-.js.map +1 -0
- package/dist/ssr.d.ts +38 -0
- package/dist/ssr.d.ts.map +1 -0
- package/dist/types-B4Rf1Xot.js +6 -0
- package/dist/types-B4Rf1Xot.js.map +1 -0
- package/package.json +9 -15
- package/dist/client/hydrate.d.ts +0 -19
- package/dist/client/hydrate.d.ts.map +0 -1
- package/dist/client/index.js.map +0 -1
- package/dist/client/registry.d.ts +0 -46
- package/dist/client/registry.d.ts.map +0 -1
- package/dist/client/types.d.ts +0 -43
- package/dist/client/types.d.ts.map +0 -1
- package/dist/server/index.js.map +0 -1
- package/dist/server/stream.d.ts +0 -34
- package/dist/server/stream.d.ts.map +0 -1
- package/src/jsx.d.ts +0 -62
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-UBcHtkm-.js","names":[],"sources":["../src/builtin-ssr-directives.ts","../src/server/context.ts","../src/server/render-core.ts","../src/server/streaming.ts","../src/head.ts","../src/ssr.ts","../src/server/render-api.ts","../src/server/index.ts"],"sourcesContent":["/**\r\n * Built-in directive SSR support — lazy patching.\r\n *\r\n * This module patches `getSSRProps` onto built-in directives (like `show`)\r\n * at runtime, keeping `@sigx/runtime-dom` free of SSR knowledge.\r\n *\r\n * Mirrors Vue 3's `initVShowForSSR()` / `initDirectivesForSSR()` pattern.\r\n *\r\n * @internal\r\n */\r\nimport { show } from '@sigx/runtime-dom';\r\n\r\nlet _initialized = false;\r\n\r\n/**\r\n * Patch `getSSRProps` onto the `show` directive for SSR support.\r\n *\r\n * Called lazily from `initDirectivesForSSR()` — not at import time,\r\n * so tree-shaking can eliminate this in client-only builds.\r\n */\r\nfunction initShowForSSR(): void {\r\n (show as any).getSSRProps = ({ value }: { value: boolean }) => {\r\n if (!value) {\r\n return { style: { display: 'none' } };\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Initialize SSR support for all built-in directives.\r\n *\r\n * Must be called before any SSR rendering occurs.\r\n * Safe to call multiple times — only patches once.\r\n */\r\nexport function initDirectivesForSSR(): void {\r\n if (_initialized) return;\r\n _initialized = true;\r\n initShowForSSR();\r\n}\r\n","/**\r\n * SSR Context — tracks component boundaries and rendering state.\r\n *\r\n * This is the core SSR context, free of any strategy-specific logic (islands, etc.).\r\n * Plugins extend it via the generic `_pluginData` map.\r\n */\r\n\r\nimport type { SSRPlugin } from '../plugin';\r\n\r\n/**\r\n * Core-managed pending async component.\r\n * Created by render-core when streaming mode is active and no plugin overrides.\r\n */\r\nexport interface CorePendingAsync {\r\n /** Component ID */\r\n id: number;\r\n /** Resolves to rendered HTML when ssr.load() completes */\r\n promise: Promise<string>;\r\n}\r\n\r\nexport interface SSRContextOptions {\r\n /**\r\n * Enable streaming mode (default: true)\r\n */\r\n streaming?: boolean;\r\n\r\n /**\r\n * Called when a component's setup() throws during SSR.\r\n *\r\n * Return a fallback HTML string to render in place of the failed component,\r\n * or `null` to use the default error placeholder.\r\n *\r\n * @param error - The error thrown during rendering\r\n * @param componentName - The component's `__name` (or 'Anonymous')\r\n * @param componentId - The numeric component ID assigned by the SSR context\r\n */\r\n onComponentError?: (error: Error, componentName: string, componentId: number) => string | null;\r\n}\r\n\r\nexport interface RenderOptions {\r\n /**\r\n * Custom SSR context (created automatically if not provided)\r\n */\r\n context?: SSRContext;\r\n}\r\n\r\nexport interface SSRContext {\r\n /**\r\n * Unique ID counter for component markers\r\n */\r\n _componentId: number;\r\n\r\n /**\r\n * Stack of component IDs for nested tracking\r\n */\r\n _componentStack: number[];\r\n\r\n /**\r\n * Collected head elements (scripts, styles, etc.)\r\n */\r\n _head: string[];\r\n\r\n /**\r\n * Error callback for component rendering failures\r\n */\r\n _onComponentError?: (error: Error, componentName: string, componentId: number) => string | null;\r\n\r\n /**\r\n * Registered SSR plugins\r\n */\r\n _plugins?: SSRPlugin[];\r\n\r\n /**\r\n * Plugin-specific data storage, keyed by plugin name.\r\n * Plugins store their own state here via `getPluginData` / `setPluginData`.\r\n */\r\n _pluginData: Map<string, any>;\r\n\r\n /**\r\n * Whether streaming mode is active.\r\n * When true, async components default to streaming (placeholder + deferred render)\r\n * instead of blocking. Set by renderStream / renderStreamWithCallbacks.\r\n */\r\n _streaming: boolean;\r\n\r\n /**\r\n * Core-managed pending async components.\r\n * Populated by render-core when async components are streamed without a plugin override.\r\n */\r\n _pendingAsync: CorePendingAsync[];\r\n\r\n /**\r\n * Generate next component ID\r\n */\r\n nextId(): number;\r\n\r\n /**\r\n * Push a component onto the stack\r\n */\r\n pushComponent(id: number): void;\r\n\r\n /**\r\n * Pop the current component from stack\r\n */\r\n popComponent(): number | undefined;\r\n\r\n /**\r\n * Add a head element\r\n */\r\n addHead(html: string): void;\r\n\r\n /**\r\n * Get collected head HTML\r\n */\r\n getHead(): string;\r\n\r\n /**\r\n * Get plugin-specific data by plugin name.\r\n */\r\n getPluginData<T>(pluginName: string): T | undefined;\r\n\r\n /**\r\n * Set plugin-specific data by plugin name.\r\n */\r\n setPluginData<T>(pluginName: string, data: T): void;\r\n}\r\n\r\n/**\r\n * Create a new SSR context for rendering\r\n */\r\nexport function createSSRContext(options: SSRContextOptions = {}): SSRContext {\r\n let componentId = 0;\r\n const componentStack: number[] = [];\r\n const head: string[] = [];\r\n const pluginData = new Map<string, any>();\r\n\r\n return {\r\n _componentId: componentId,\r\n _componentStack: componentStack,\r\n _head: head,\r\n _pluginData: pluginData,\r\n _onComponentError: options.onComponentError,\r\n _streaming: false,\r\n _pendingAsync: [],\r\n\r\n nextId() {\r\n return ++componentId;\r\n },\r\n\r\n pushComponent(id: number) {\r\n componentStack.push(id);\r\n },\r\n\r\n popComponent() {\r\n return componentStack.pop();\r\n },\r\n\r\n addHead(html: string) {\r\n head.push(html);\r\n },\r\n\r\n getHead() {\r\n return head.join('\\n');\r\n },\r\n\r\n getPluginData<T>(pluginName: string): T | undefined {\r\n return pluginData.get(pluginName);\r\n },\r\n\r\n setPluginData<T>(pluginName: string, data: T): void {\r\n pluginData.set(pluginName, data);\r\n }\r\n };\r\n}\r\n","/**\r\n * Core rendering logic for SSR\r\n *\r\n * The async generator `renderToChunks` walks a VNode tree and yields HTML strings.\r\n * Handles text, fragments, host elements, and delegates components to the\r\n * component renderer.\r\n *\r\n * This module is strategy-agnostic. Island-specific logic (signal tracking,\r\n * hydration directives, async streaming) lives in @sigx/ssr-islands and is\r\n * injected through the SSRPlugin hooks.\r\n */\r\n\r\nimport {\r\n VNode,\r\n Fragment,\r\n JSXElement,\r\n ComponentSetupContext,\r\n setCurrentInstance,\r\n signal,\r\n Text,\r\n SlotsObject,\r\n isComponent,\r\n createPropsAccessor,\r\n provideAppContext,\r\n isDirective\r\n} from 'sigx';\r\nimport type { DirectiveDefinition } from 'sigx';\r\nimport { resolveBuiltInDirective } from '@sigx/runtime-dom';\r\nimport type { AppContext } from 'sigx';\r\nimport type { SSRContext } from './context';\r\n\r\n// ============= HTML Utilities =============\r\n\r\nconst ESCAPE: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": '''\r\n};\r\n\r\nexport function escapeHtml(s: string): string {\r\n return s.replace(/[&<>\"']/g, c => ESCAPE[c]);\r\n}\r\n\r\n/** Cache for camelCase → kebab-case conversions (same properties repeat across elements) */\r\nconst kebabCache: Record<string, string> = {};\r\n\r\n/** Void elements that cannot have children — hoisted to module scope as a Set for O(1) lookup */\r\nconst VOID_ELEMENTS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);\r\n\r\nexport function camelToKebab(str: string): string {\r\n // CSS custom properties (--foo) are already kebab-case\r\n if (str.startsWith('--')) return str;\r\n return kebabCache[str] ||= str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\r\n}\r\n\r\n// ============= Style Parsing =============\r\n\r\n/**\r\n * Parse a CSS string into a style object.\r\n *\r\n * Handles edge cases: parens in values (e.g., `linear-gradient(...)`),\r\n * CSS comments, and colons in values.\r\n *\r\n * Adapted from Vue 3's `parseStringStyle` — battle-tested, split-based,\r\n * fast in V8.\r\n */\r\nconst listDelimiterRE = /;(?![^(]*\\))/g;\r\nconst propertyDelimiterRE = /:([^]+)/;\r\nconst styleCommentRE = /\\/\\*[^]*?\\*\\//g;\r\n\r\nexport function parseStringStyle(cssText: string): Record<string, string> {\r\n const ret: Record<string, string> = {};\r\n cssText\r\n .replace(styleCommentRE, '')\r\n .split(listDelimiterRE)\r\n .forEach(item => {\r\n if (item) {\r\n const tmp = item.split(propertyDelimiterRE);\r\n if (tmp.length > 1) {\r\n ret[tmp[0].trim()] = tmp[1].trim();\r\n }\r\n }\r\n });\r\n return ret;\r\n}\r\n\r\n/**\r\n * Serialize a style object to a CSS string.\r\n *\r\n * Uses for...in + string concat (avoids Object.entries/map/join allocations)\r\n * and cached kebab-case conversion.\r\n */\r\nexport function stringifyStyle(style: Record<string, any>): string {\r\n let ret = '';\r\n for (const key in style) {\r\n const value = style[key];\r\n if (value != null && value !== '') {\r\n ret += `${camelToKebab(key)}:${value};`;\r\n }\r\n }\r\n return ret;\r\n}\r\n\r\n/**\r\n * Check if element will render as text content\r\n */\r\nfunction isTextContent(element: JSXElement): boolean {\r\n if (element == null || element === false || element === true) return false;\r\n if (typeof element === 'string' || typeof element === 'number') return true;\r\n const vnode = element as VNode;\r\n return vnode.type === Text;\r\n}\r\n\r\n/**\r\n * Merge style values for SSR (element style + directive SSR style).\r\n * Either value can be an object, string, or undefined.\r\n * String styles are parsed into objects before merging.\r\n */\r\nfunction mergeSSRStyles(elementStyle: any, directiveStyle: any): Record<string, any> {\r\n if (!elementStyle) return directiveStyle;\r\n if (!directiveStyle) return elementStyle;\r\n // Normalize both to objects — parse CSS strings if needed\r\n const a = typeof elementStyle === 'string' ? parseStringStyle(elementStyle)\r\n : (typeof elementStyle === 'object' ? elementStyle : {});\r\n const b = typeof directiveStyle === 'string' ? parseStringStyle(directiveStyle)\r\n : (typeof directiveStyle === 'object' ? directiveStyle : {});\r\n return { ...a, ...b };\r\n}\r\n\r\n/**\r\n * Render element to string chunks (generator for streaming)\r\n * @param element - The JSX element to render\r\n * @param ctx - The SSR context for tracking state\r\n * @param parentCtx - The parent component context for provide/inject\r\n * @param appContext - The app context for app-level provides (from defineApp)\r\n */\r\nexport async function* renderToChunks(\r\n element: JSXElement,\r\n ctx: SSRContext,\r\n parentCtx: ComponentSetupContext | null = null,\r\n appContext: AppContext | null = null\r\n): AsyncGenerator<string> {\r\n if (element == null || element === false || element === true) {\r\n return;\r\n }\r\n\r\n if (typeof element === 'string' || typeof element === 'number') {\r\n yield escapeHtml(String(element));\r\n return;\r\n }\r\n\r\n const vnode = element as VNode;\r\n\r\n if (vnode.type === Text) {\r\n yield escapeHtml(String(vnode.text));\r\n return;\r\n }\r\n\r\n if (vnode.type === Fragment) {\r\n for (const child of vnode.children) {\r\n yield* renderToChunks(child, ctx, parentCtx, appContext);\r\n }\r\n return;\r\n }\r\n\r\n // Handle Components\r\n if (isComponent(vnode.type)) {\r\n const setup = vnode.type.__setup;\r\n const componentName = vnode.type.__name || 'Anonymous';\r\n const allProps = vnode.props || {};\r\n\r\n // Destructure props (filter out framework-internal keys)\r\n const { children, slots: slotsFromProps, $models: modelsData, ...propsData } = allProps;\r\n\r\n const id = ctx.nextId();\r\n ctx.pushComponent(id);\r\n\r\n // Create slots from children\r\n const slots: SlotsObject<any> = {\r\n default: () => children ? (Array.isArray(children) ? children : [children]) : [],\r\n ...slotsFromProps\r\n };\r\n\r\n // Track SSR loads for this component\r\n const ssrLoads: Promise<void>[] = [];\r\n\r\n // Create SSR helper for async data loading\r\n const ssrHelper = {\r\n load(fn: () => Promise<void>): void {\r\n ssrLoads.push(fn());\r\n },\r\n isServer: true,\r\n isHydrating: false\r\n };\r\n\r\n let componentCtx: ComponentSetupContext = {\r\n el: null as any,\r\n signal: signal,\r\n props: createPropsAccessor(propsData),\r\n slots: slots,\r\n emit: () => { },\r\n parent: parentCtx,\r\n onMounted: () => { },\r\n onUnmounted: () => { },\r\n onCreated: () => { },\r\n onUpdated: () => { },\r\n expose: () => { },\r\n renderFn: null,\r\n update: () => { },\r\n ssr: ssrHelper,\r\n _ssrLoads: ssrLoads\r\n };\r\n\r\n // Plugin hook: transformComponentContext\r\n // Allows plugins (e.g., islands) to swap signal fn, filter props, set up tracking, etc.\r\n if (ctx._plugins) {\r\n for (const plugin of ctx._plugins) {\r\n const transformed = plugin.server?.transformComponentContext?.(ctx, vnode, componentCtx);\r\n if (transformed) {\r\n componentCtx = transformed;\r\n }\r\n }\r\n }\r\n\r\n // For ROOT component only (no parent), provide the AppContext\r\n if (!parentCtx && appContext) {\r\n provideAppContext(componentCtx, appContext);\r\n }\r\n\r\n const prev = setCurrentInstance(componentCtx);\r\n try {\r\n // Run setup synchronously — it registers ssr.load() callbacks\r\n let renderFn = setup(componentCtx);\r\n\r\n // Support legacy async setup — await if it returns a promise\r\n if (renderFn && typeof (renderFn as any).then === 'function') {\r\n renderFn = await (renderFn as Promise<any>);\r\n }\r\n\r\n // Check if we have pending ssr.load() calls\r\n if (ssrLoads.length > 0) {\r\n // Plugin hook: handleAsyncSetup\r\n // Plugins can override the async mode.\r\n // Default: 'stream' in streaming mode, 'block' in string mode.\r\n let asyncMode: 'block' | 'stream' | 'skip' = ctx._streaming ? 'stream' : 'block';\r\n let asyncPlaceholder: string | undefined;\r\n let pluginHandled = false;\r\n\r\n if (ctx._plugins) {\r\n for (const plugin of ctx._plugins) {\r\n const result = plugin.server?.handleAsyncSetup?.(id, ssrLoads, renderFn as () => any, ctx);\r\n if (result) {\r\n asyncMode = result.mode;\r\n asyncPlaceholder = result.placeholder;\r\n pluginHandled = true;\r\n break; // First plugin to handle wins\r\n }\r\n }\r\n }\r\n\r\n if (asyncMode === 'stream') {\r\n // Use default placeholder if none provided by plugin\r\n const placeholder = asyncPlaceholder || `<div data-async-placeholder=\"${id}\" style=\"display:contents;\">`;\r\n\r\n // Render placeholder immediately\r\n yield placeholder;\r\n\r\n // Render with initial state (before data loads)\r\n if (renderFn) {\r\n const result = (renderFn as () => any)();\r\n if (result) {\r\n if (Array.isArray(result)) {\r\n for (const item of result) {\r\n yield* renderToChunks(item, ctx, componentCtx, appContext);\r\n }\r\n } else {\r\n yield* renderToChunks(result, ctx, componentCtx, appContext);\r\n }\r\n }\r\n }\r\n\r\n yield `</div>`;\r\n\r\n // If no plugin handled this, core manages the deferred render\r\n if (!pluginHandled) {\r\n const capturedRenderFn = renderFn;\r\n const capturedCtx = ctx;\r\n const capturedAppContext = appContext;\r\n\r\n const deferredRender = (async () => {\r\n await Promise.all(ssrLoads);\r\n\r\n let html = '';\r\n if (capturedRenderFn) {\r\n const result = (capturedRenderFn as () => any)();\r\n if (result) {\r\n html = await renderVNodeToString(result, capturedCtx, capturedAppContext);\r\n }\r\n }\r\n\r\n return html;\r\n })();\r\n\r\n ctx._pendingAsync.push({ id, promise: deferredRender });\r\n }\r\n } else if (asyncMode === 'skip') {\r\n // Plugin says skip — don't render content\r\n } else {\r\n // Default: block — wait for all async loads\r\n await Promise.all(ssrLoads);\r\n\r\n if (renderFn) {\r\n const result = (renderFn as () => any)();\r\n if (result) {\r\n if (Array.isArray(result)) {\r\n for (const item of result) {\r\n yield* renderToChunks(item, ctx, componentCtx, appContext);\r\n }\r\n } else {\r\n yield* renderToChunks(result, ctx, componentCtx, appContext);\r\n }\r\n }\r\n }\r\n }\r\n } else {\r\n // No async loads — render synchronously\r\n if (renderFn) {\r\n const result = (renderFn as () => any)();\r\n if (result) {\r\n if (Array.isArray(result)) {\r\n for (const item of result) {\r\n yield* renderToChunks(item, ctx, componentCtx, appContext);\r\n }\r\n } else {\r\n yield* renderToChunks(result, ctx, componentCtx, appContext);\r\n }\r\n }\r\n }\r\n }\r\n } catch (e) {\r\n const error = e instanceof Error ? e : new Error(String(e));\r\n let fallbackHtml: string | null = null;\r\n\r\n if (ctx._onComponentError) {\r\n fallbackHtml = ctx._onComponentError(error, componentName, id);\r\n }\r\n\r\n if (fallbackHtml === null || fallbackHtml === undefined) {\r\n fallbackHtml = `<!--ssr-error:${id}-->`;\r\n }\r\n\r\n if (fallbackHtml) {\r\n yield fallbackHtml;\r\n }\r\n\r\n if (process.env.NODE_ENV !== 'production') {\r\n console.error(`Error rendering component ${componentName}:`, e);\r\n }\r\n } finally {\r\n setCurrentInstance(prev || null);\r\n }\r\n\r\n // Collect rendered HTML for plugin post-processing\r\n // Note: For streaming, afterRenderComponent receives empty string\r\n // since chunks were already yielded. Plugins that need to wrap\r\n // content should use transformComponentContext to set up wrapping.\r\n\r\n // Plugin hook: afterRenderComponent\r\n if (ctx._plugins) {\r\n for (const plugin of ctx._plugins) {\r\n const transformed = plugin.server?.afterRenderComponent?.(id, vnode, '', ctx);\r\n if (transformed) {\r\n yield transformed;\r\n }\r\n }\r\n }\r\n\r\n // Emit trailing component marker\r\n yield `<!--$c:${id}-->`;\r\n ctx.popComponent();\r\n return;\r\n }\r\n\r\n // Handle host elements\r\n if (typeof vnode.type === 'string') {\r\n const tagName = vnode.type;\r\n let props = '';\r\n\r\n // Collect SSR props from use:* directive props (getSSRProps hook)\r\n let directiveSSRProps: Record<string, any> | null = null;\r\n if (vnode.props) {\r\n for (const key in vnode.props) {\r\n if (key.startsWith('use:')) {\r\n const propValue = vnode.props[key];\r\n let def: DirectiveDefinition | undefined;\r\n let value: any;\r\n\r\n if (isDirective(propValue)) {\r\n def = propValue;\r\n value = undefined;\r\n } else if (\r\n Array.isArray(propValue) &&\r\n propValue.length >= 1 &&\r\n isDirective(propValue[0])\r\n ) {\r\n def = propValue[0];\r\n value = propValue[1];\r\n } else {\r\n // Try to resolve by name:\r\n // 1. Built-in directives (always available, e.g., 'show')\r\n // 2. App-registered custom directives (via app.directive())\r\n const builtIn = resolveBuiltInDirective(key.slice(4));\r\n if (builtIn) {\r\n def = builtIn;\r\n value = propValue;\r\n } else {\r\n const custom = appContext?.directives.get(key.slice(4));\r\n if (custom) {\r\n def = custom;\r\n value = propValue;\r\n }\r\n }\r\n }\r\n\r\n if (def?.getSSRProps) {\r\n const ssrProps = def.getSSRProps({ value });\r\n if (ssrProps) {\r\n if (!directiveSSRProps) directiveSSRProps = {};\r\n for (const k in ssrProps) {\r\n if (k === 'style' && directiveSSRProps.style) {\r\n directiveSSRProps.style = { ...directiveSSRProps.style, ...ssrProps.style };\r\n } else if (k === 'class' && directiveSSRProps.class) {\r\n directiveSSRProps.class = directiveSSRProps.class + ' ' + ssrProps.class;\r\n } else {\r\n directiveSSRProps[k] = ssrProps[k];\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Merge directive SSR props with element props\r\n const allProps = directiveSSRProps\r\n ? { ...vnode.props, ...directiveSSRProps, style: mergeSSRStyles(vnode.props?.style, directiveSSRProps?.style) }\r\n : vnode.props;\r\n\r\n // Serialize props\r\n for (const key in allProps) {\r\n const value = allProps[key];\r\n if (key === 'children' || key === 'key' || key === 'ref') continue;\r\n if (key.startsWith('client:')) continue; // Skip client directives\r\n if (key.startsWith('use:')) continue; // Skip element directives\r\n\r\n if (key === 'style') {\r\n const styleString = typeof value === 'object'\r\n ? stringifyStyle(value)\r\n : String(value);\r\n props += ` style=\"${escapeHtml(styleString)}\"`;\r\n } else if (key === 'className') {\r\n props += ` class=\"${escapeHtml(String(value))}\"`;\r\n } else if (key.startsWith('on')) {\r\n // Skip event listeners on server\r\n } else if (value === true) {\r\n props += ` ${key}`;\r\n } else if (value !== false && value != null) {\r\n props += ` ${key}=\"${escapeHtml(String(value))}\"`;\r\n }\r\n }\r\n\r\n // Void elements\r\n if (VOID_ELEMENTS.has(tagName)) {\r\n yield `<${tagName}${props}>`;\r\n return;\r\n }\r\n\r\n yield `<${tagName}${props}>`;\r\n\r\n // Render children with text boundary markers\r\n // Adjacent text nodes get merged by the browser, so we insert <!--t--> markers\r\n let prevWasText = false;\r\n for (const child of vnode.children) {\r\n const isText = isTextContent(child);\r\n if (isText && prevWasText) {\r\n // Insert marker between adjacent text nodes\r\n yield '<!--t-->';\r\n }\r\n yield* renderToChunks(child, ctx, parentCtx, appContext);\r\n prevWasText = isText;\r\n }\r\n\r\n yield `</${tagName}>`;\r\n }\r\n}\r\n\r\n/**\r\n * Helper to render a VNode to string (for deferred async content)\r\n */\r\nexport async function renderVNodeToString(element: JSXElement, ctx: SSRContext, appContext: AppContext | null = null): Promise<string> {\r\n let result = '';\r\n for await (const chunk of renderToChunks(element, ctx, null, appContext)) {\r\n result += chunk;\r\n }\r\n return result;\r\n}\r\n\r\n// ============= Synchronous String Renderer =============\r\n\r\n/**\r\n * Synchronous render-to-string that avoids async generator overhead.\r\n * Returns null if any async operation is encountered (caller should fall back\r\n * to the async generator path).\r\n *\r\n * For purely synchronous component trees this eliminates thousands of\r\n * microtask/Promise allocations from the AsyncGenerator protocol.\r\n */\r\nexport function renderToStringSync(\r\n element: JSXElement,\r\n ctx: SSRContext,\r\n parentCtx: ComponentSetupContext | null,\r\n appContext: AppContext | null,\r\n buf: string[]\r\n): boolean {\r\n if (element == null || element === false || element === true) {\r\n return true;\r\n }\r\n\r\n if (typeof element === 'string' || typeof element === 'number') {\r\n buf.push(escapeHtml(String(element)));\r\n return true;\r\n }\r\n\r\n const vnode = element as VNode;\r\n\r\n if (vnode.type === Text) {\r\n buf.push(escapeHtml(String(vnode.text)));\r\n return true;\r\n }\r\n\r\n if (vnode.type === Fragment) {\r\n for (const child of vnode.children) {\r\n if (!renderToStringSync(child, ctx, parentCtx, appContext, buf)) return false;\r\n }\r\n return true;\r\n }\r\n\r\n // Handle Components\r\n if (isComponent(vnode.type)) {\r\n const setup = vnode.type.__setup;\r\n const componentName = vnode.type.__name || 'Anonymous';\r\n const allProps = vnode.props || {};\r\n\r\n const { children, slots: slotsFromProps, $models: modelsData, ...propsData } = allProps;\r\n\r\n const id = ctx.nextId();\r\n ctx.pushComponent(id);\r\n\r\n const slots: SlotsObject<any> = {\r\n default: () => children ? (Array.isArray(children) ? children : [children]) : [],\r\n ...slotsFromProps\r\n };\r\n\r\n const ssrLoads: Promise<void>[] = [];\r\n\r\n const ssrHelper = {\r\n load(fn: () => Promise<void>): void {\r\n ssrLoads.push(fn());\r\n },\r\n isServer: true,\r\n isHydrating: false\r\n };\r\n\r\n let componentCtx: ComponentSetupContext = {\r\n el: null as any,\r\n signal: signal,\r\n props: createPropsAccessor(propsData),\r\n slots: slots,\r\n emit: () => { },\r\n parent: parentCtx,\r\n onMounted: () => { },\r\n onUnmounted: () => { },\r\n onCreated: () => { },\r\n onUpdated: () => { },\r\n expose: () => { },\r\n renderFn: null,\r\n update: () => { },\r\n ssr: ssrHelper,\r\n _ssrLoads: ssrLoads\r\n };\r\n\r\n if (ctx._plugins) {\r\n for (const plugin of ctx._plugins) {\r\n const transformed = plugin.server?.transformComponentContext?.(ctx, vnode, componentCtx);\r\n if (transformed) {\r\n componentCtx = transformed;\r\n }\r\n }\r\n }\r\n\r\n if (!parentCtx && appContext) {\r\n provideAppContext(componentCtx, appContext);\r\n }\r\n\r\n const prev = setCurrentInstance(componentCtx);\r\n try {\r\n let renderFn = setup(componentCtx);\r\n\r\n // Bail out if setup is async\r\n if (renderFn && typeof (renderFn as any).then === 'function') {\r\n for (const p of ssrLoads) p.catch(() => {});\r\n setCurrentInstance(prev || null);\r\n ctx.popComponent();\r\n return false;\r\n }\r\n\r\n // Bail out if there are ssr.load() calls\r\n if (ssrLoads.length > 0) {\r\n // Suppress unhandled rejections — the async path will re-run these\r\n for (const p of ssrLoads) p.catch(() => {});\r\n setCurrentInstance(prev || null);\r\n ctx.popComponent();\r\n return false;\r\n }\r\n\r\n if (renderFn) {\r\n const result = (renderFn as () => any)();\r\n if (result) {\r\n if (Array.isArray(result)) {\r\n for (const item of result) {\r\n if (!renderToStringSync(item, ctx, componentCtx, appContext, buf)) {\r\n setCurrentInstance(prev || null);\r\n ctx.popComponent();\r\n return false;\r\n }\r\n }\r\n } else {\r\n if (!renderToStringSync(result, ctx, componentCtx, appContext, buf)) {\r\n setCurrentInstance(prev || null);\r\n ctx.popComponent();\r\n return false;\r\n }\r\n }\r\n }\r\n }\r\n } catch (e) {\r\n const error = e instanceof Error ? e : new Error(String(e));\r\n let fallbackHtml: string | null = null;\r\n\r\n if (ctx._onComponentError) {\r\n fallbackHtml = ctx._onComponentError(error, componentName, id);\r\n }\r\n\r\n if (fallbackHtml === null || fallbackHtml === undefined) {\r\n fallbackHtml = `<!--ssr-error:${id}-->`;\r\n }\r\n\r\n if (fallbackHtml) {\r\n buf.push(fallbackHtml);\r\n }\r\n } finally {\r\n setCurrentInstance(prev || null);\r\n }\r\n\r\n if (ctx._plugins) {\r\n for (const plugin of ctx._plugins) {\r\n const transformed = plugin.server?.afterRenderComponent?.(id, vnode, '', ctx);\r\n if (transformed) {\r\n buf.push(transformed);\r\n }\r\n }\r\n }\r\n\r\n buf.push(`<!--$c:${id}-->`);\r\n ctx.popComponent();\r\n return true;\r\n }\r\n\r\n // Handle host elements\r\n if (typeof vnode.type === 'string') {\r\n const tagName = vnode.type;\r\n let props = '';\r\n\r\n let directiveSSRProps: Record<string, any> | null = null;\r\n if (vnode.props) {\r\n for (const key in vnode.props) {\r\n if (key.startsWith('use:')) {\r\n const propValue = vnode.props[key];\r\n let def: DirectiveDefinition | undefined;\r\n let value: any;\r\n\r\n if (isDirective(propValue)) {\r\n def = propValue;\r\n value = undefined;\r\n } else if (\r\n Array.isArray(propValue) &&\r\n propValue.length >= 1 &&\r\n isDirective(propValue[0])\r\n ) {\r\n def = propValue[0];\r\n value = propValue[1];\r\n } else {\r\n const builtIn = resolveBuiltInDirective(key.slice(4));\r\n if (builtIn) {\r\n def = builtIn;\r\n value = propValue;\r\n } else {\r\n const custom = appContext?.directives.get(key.slice(4));\r\n if (custom) {\r\n def = custom;\r\n value = propValue;\r\n }\r\n }\r\n }\r\n\r\n if (def?.getSSRProps) {\r\n const ssrProps = def.getSSRProps({ value });\r\n if (ssrProps) {\r\n if (!directiveSSRProps) directiveSSRProps = {};\r\n for (const k in ssrProps) {\r\n if (k === 'style' && directiveSSRProps.style) {\r\n directiveSSRProps.style = { ...directiveSSRProps.style, ...ssrProps.style };\r\n } else if (k === 'class' && directiveSSRProps.class) {\r\n directiveSSRProps.class = directiveSSRProps.class + ' ' + ssrProps.class;\r\n } else {\r\n directiveSSRProps[k] = ssrProps[k];\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n const allProps = directiveSSRProps\r\n ? { ...vnode.props, ...directiveSSRProps, style: mergeSSRStyles(vnode.props?.style, directiveSSRProps?.style) }\r\n : vnode.props;\r\n\r\n for (const key in allProps) {\r\n const value = allProps[key];\r\n if (key === 'children' || key === 'key' || key === 'ref') continue;\r\n if (key.startsWith('client:')) continue;\r\n if (key.startsWith('use:')) continue;\r\n\r\n if (key === 'style') {\r\n const styleString = typeof value === 'object'\r\n ? stringifyStyle(value)\r\n : String(value);\r\n props += ` style=\"${escapeHtml(styleString)}\"`;\r\n } else if (key === 'className') {\r\n props += ` class=\"${escapeHtml(String(value))}\"`;\r\n } else if (key.startsWith('on')) {\r\n // Skip event listeners on server\r\n } else if (value === true) {\r\n props += ` ${key}`;\r\n } else if (value !== false && value != null) {\r\n props += ` ${key}=\"${escapeHtml(String(value))}\"`;\r\n }\r\n }\r\n\r\n if (VOID_ELEMENTS.has(tagName)) {\r\n buf.push(`<${tagName}${props}>`);\r\n return true;\r\n }\r\n\r\n buf.push(`<${tagName}${props}>`);\r\n\r\n let prevWasText = false;\r\n for (const child of vnode.children) {\r\n const isText = isTextContent(child);\r\n if (isText && prevWasText) {\r\n buf.push('<!--t-->');\r\n }\r\n if (!renderToStringSync(child, ctx, parentCtx, appContext, buf)) return false;\r\n prevWasText = isText;\r\n }\r\n\r\n buf.push(`</${tagName}>`);\r\n return true;\r\n }\r\n\r\n return true;\r\n}\r\n","/**\r\n * Core streaming utilities for async SSR\r\n *\r\n * Provides the client-side `$SIGX_REPLACE` function and replacement script\r\n * generation used by core async streaming. These are strategy-agnostic —\r\n * any async component with `ssr.load()` gets streamed without needing a plugin.\r\n *\r\n * Plugins (e.g., islands) can augment replacements via `onAsyncComponentResolved`.\r\n */\r\n\r\n/**\r\n * Escape a JSON string for safe embedding inside <script> tags.\r\n * Prevents XSS by replacing characters that could break out of the script context.\r\n */\r\nexport function escapeJsonForScript(json: string): string {\r\n return json\r\n .replace(/</g, '\\\\u003c')\r\n .replace(/>/g, '\\\\u003e')\r\n .replace(/\\u2028/g, '\\\\u2028')\r\n .replace(/\\u2029/g, '\\\\u2029');\r\n}\r\n\r\n/**\r\n * Generate the streaming bootstrap script (injected once before any replacements).\r\n * Defines `window.$SIGX_REPLACE` which swaps async placeholders with rendered HTML.\r\n */\r\nexport function generateStreamingScript(): string {\r\n return `\r\n<script>\r\nwindow.$SIGX_REPLACE = function(id, html) {\r\n var placeholder = document.querySelector('[data-async-placeholder=\"' + id + '\"]');\r\n if (placeholder) {\r\n var template = document.createElement('template');\r\n template.innerHTML = html;\r\n placeholder.innerHTML = '';\r\n while (template.content.firstChild) {\r\n placeholder.appendChild(template.content.firstChild);\r\n }\r\n placeholder.dispatchEvent(new CustomEvent('sigx:async-ready', { bubbles: true, detail: { id: id } }));\r\n }\r\n};\r\n</script>`;\r\n}\r\n\r\n/**\r\n * Generate a replacement script for a resolved async component.\r\n */\r\nexport function generateReplacementScript(id: number, html: string, extraScript?: string): string {\r\n const escapedHtml = escapeJsonForScript(JSON.stringify(html));\r\n let script = `<script>$SIGX_REPLACE(${id}, ${escapedHtml});`;\r\n if (extraScript) {\r\n script += extraScript;\r\n }\r\n script += `</script>`;\r\n return script;\r\n}\r\n","/**\r\n * Head management composable for SSR and client-side.\r\n *\r\n * Provides `useHead()` for managing `<head>` elements (title, meta, link, script)\r\n * from within components. Works during SSR (collects into SSRContext._head) and\r\n * on the client (updates DOM directly).\r\n *\r\n * @example\r\n * ```tsx\r\n * import { useHead } from '@sigx/server-renderer/head';\r\n *\r\n * function MyPage(ctx) {\r\n * useHead({\r\n * title: 'My Page',\r\n * meta: [\r\n * { name: 'description', content: 'A great page' },\r\n * { property: 'og:title', content: 'My Page' }\r\n * ],\r\n * link: [\r\n * { rel: 'canonical', href: 'https://example.com/my-page' }\r\n * ]\r\n * });\r\n *\r\n * return () => <div>Page content</div>;\r\n * }\r\n * ```\r\n */\r\n\r\nimport { getCurrentInstance } from 'sigx';\r\n\r\n// ============= Types =============\r\n\r\nexport interface HeadMeta {\r\n name?: string;\r\n property?: string;\r\n 'http-equiv'?: string;\r\n charset?: string;\r\n content?: string;\r\n [key: string]: string | undefined;\r\n}\r\n\r\nexport interface HeadLink {\r\n rel: string;\r\n href?: string;\r\n type?: string;\r\n crossorigin?: string;\r\n [key: string]: string | undefined;\r\n}\r\n\r\nexport interface HeadScript {\r\n src?: string;\r\n type?: string;\r\n async?: boolean;\r\n defer?: boolean;\r\n innerHTML?: string;\r\n [key: string]: string | boolean | undefined;\r\n}\r\n\r\nexport interface HeadConfig {\r\n /** Page title */\r\n title?: string;\r\n /** Title template — use %s as placeholder for the title */\r\n titleTemplate?: string;\r\n /** Meta tags */\r\n meta?: HeadMeta[];\r\n /** Link tags */\r\n link?: HeadLink[];\r\n /** Script tags */\r\n script?: HeadScript[];\r\n /** HTML language attribute */\r\n htmlAttrs?: { lang?: string; dir?: string; [key: string]: string | undefined };\r\n /** Body attributes */\r\n bodyAttrs?: { class?: string; [key: string]: string | undefined };\r\n}\r\n\r\n// ============= SSR Head Collection =============\r\n\r\n// Server-side: head configs are collected during rendering\r\nlet _ssrHeadConfigs: HeadConfig[] = [];\r\nlet _isSSR = false;\r\n\r\n/**\r\n * Enable SSR mode for head management.\r\n * Called by the SSR renderer before rendering starts.\r\n */\r\nexport function enableSSRHead(): void {\r\n _isSSR = true;\r\n _ssrHeadConfigs = [];\r\n}\r\n\r\n/**\r\n * Disable SSR mode and return collected configs.\r\n */\r\nexport function collectSSRHead(): HeadConfig[] {\r\n _isSSR = false;\r\n const configs = _ssrHeadConfigs;\r\n _ssrHeadConfigs = [];\r\n return configs;\r\n}\r\n\r\n/**\r\n * Render collected head configs to an HTML string.\r\n * Deduplicates meta tags by name/property and uses the last title.\r\n */\r\nexport function renderHeadToString(configs: HeadConfig[]): string {\r\n const parts: string[] = [];\r\n const seenMeta = new Map<string, string>();\r\n let finalTitle: string | undefined;\r\n let titleTemplate: string | undefined;\r\n\r\n // Process in order — later configs override earlier ones\r\n for (const config of configs) {\r\n if (config.titleTemplate) {\r\n titleTemplate = config.titleTemplate;\r\n }\r\n if (config.title) {\r\n finalTitle = config.title;\r\n }\r\n\r\n if (config.meta) {\r\n for (const meta of config.meta) {\r\n // Deduplicate by name or property\r\n const key = meta.name ? `name:${meta.name}` :\r\n meta.property ? `property:${meta.property}` :\r\n meta['http-equiv'] ? `http-equiv:${meta['http-equiv']}` :\r\n meta.charset ? 'charset' : null;\r\n\r\n const attrs = Object.entries(meta)\r\n .filter(([, v]) => v !== undefined)\r\n .map(([k, v]) => `${escapeAttr(k)}=\"${escapeAttr(String(v))}\"`)\r\n .join(' ');\r\n\r\n const tag = `<meta ${attrs}>`;\r\n\r\n if (key) {\r\n seenMeta.set(key, tag);\r\n } else {\r\n parts.push(tag);\r\n }\r\n }\r\n }\r\n\r\n if (config.link) {\r\n for (const link of config.link) {\r\n const attrs = Object.entries(link)\r\n .filter(([, v]) => v !== undefined)\r\n .map(([k, v]) => `${escapeAttr(k)}=\"${escapeAttr(String(v))}\"`)\r\n .join(' ');\r\n parts.push(`<link ${attrs}>`);\r\n }\r\n }\r\n\r\n if (config.script) {\r\n for (const script of config.script) {\r\n const { innerHTML, ...rest } = script;\r\n const attrs = Object.entries(rest)\r\n .filter(([, v]) => v !== undefined && v !== false)\r\n .map(([k, v]) => v === true ? escapeAttr(k) : `${escapeAttr(k)}=\"${escapeAttr(String(v))}\"`)\r\n .join(' ');\r\n if (innerHTML) {\r\n parts.push(`<script ${attrs}>${innerHTML}</script>`);\r\n } else {\r\n parts.push(`<script ${attrs}></script>`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n const result: string[] = [];\r\n\r\n // Title first\r\n if (finalTitle) {\r\n const title = titleTemplate\r\n ? titleTemplate.replace('%s', finalTitle)\r\n : finalTitle;\r\n result.push(`<title>${escapeHtml(title)}</title>`);\r\n }\r\n\r\n // Deduplicated meta tags\r\n for (const tag of seenMeta.values()) {\r\n result.push(tag);\r\n }\r\n\r\n // Other parts\r\n result.push(...parts);\r\n\r\n return result.join('\\n');\r\n}\r\n\r\n// ============= Client-side Head Management =============\r\n\r\n/** Track elements managed by useHead for cleanup */\r\nconst _managedElements = new WeakMap<object, HTMLElement[]>();\r\nlet _headToken = 0;\r\n\r\nfunction applyHeadClient(config: HeadConfig): (() => void) {\r\n const managed: HTMLElement[] = [];\r\n const token = ++_headToken;\r\n\r\n if (config.title) {\r\n const title = config.titleTemplate\r\n ? config.titleTemplate.replace('%s', config.title)\r\n : config.title;\r\n document.title = title;\r\n }\r\n\r\n if (config.meta) {\r\n for (const meta of config.meta) {\r\n // Remove existing matching meta\r\n const selector = meta.name ? `meta[name=\"${meta.name}\"]` :\r\n meta.property ? `meta[property=\"${meta.property}\"]` :\r\n meta['http-equiv'] ? `meta[http-equiv=\"${meta['http-equiv']}\"]` : null;\r\n\r\n if (selector) {\r\n const existing = document.querySelector(selector);\r\n if (existing) existing.remove();\r\n }\r\n\r\n const el = document.createElement('meta');\r\n for (const [k, v] of Object.entries(meta)) {\r\n if (v !== undefined) el.setAttribute(k, v);\r\n }\r\n el.setAttribute('data-sigx-head', String(token));\r\n document.head.appendChild(el);\r\n managed.push(el);\r\n }\r\n }\r\n\r\n if (config.link) {\r\n for (const link of config.link) {\r\n const el = document.createElement('link');\r\n for (const [k, v] of Object.entries(link)) {\r\n if (v !== undefined) el.setAttribute(k, v);\r\n }\r\n el.setAttribute('data-sigx-head', String(token));\r\n document.head.appendChild(el);\r\n managed.push(el);\r\n }\r\n }\r\n\r\n if (config.script) {\r\n for (const script of config.script) {\r\n const { innerHTML, ...rest } = script;\r\n const el = document.createElement('script');\r\n for (const [k, v] of Object.entries(rest)) {\r\n if (v === true) el.setAttribute(k, '');\r\n else if (v !== undefined && v !== false) el.setAttribute(k, String(v));\r\n }\r\n if (innerHTML) el.textContent = innerHTML;\r\n el.setAttribute('data-sigx-head', String(token));\r\n document.head.appendChild(el);\r\n managed.push(el);\r\n }\r\n }\r\n\r\n if (config.htmlAttrs) {\r\n for (const [k, v] of Object.entries(config.htmlAttrs)) {\r\n if (v !== undefined) document.documentElement.setAttribute(k, v);\r\n }\r\n }\r\n\r\n if (config.bodyAttrs) {\r\n for (const [k, v] of Object.entries(config.bodyAttrs)) {\r\n if (v !== undefined) document.body.setAttribute(k, v);\r\n }\r\n }\r\n\r\n // Return cleanup function\r\n return () => {\r\n for (const el of managed) {\r\n el.remove();\r\n }\r\n };\r\n}\r\n\r\n// ============= Public API =============\r\n\r\n/**\r\n * Manage `<head>` elements from within a component.\r\n *\r\n * During SSR, collects head configs for later rendering with `renderHeadToString()`.\r\n * On the client, updates the DOM directly. Cleans up on component unmount.\r\n *\r\n * @param config - Head configuration (title, meta, link, script, etc.)\r\n */\r\nexport function useHead(config: HeadConfig): void {\r\n if (_isSSR) {\r\n // Server-side: collect configs\r\n _ssrHeadConfigs.push(config);\r\n return;\r\n }\r\n\r\n // Client-side: apply to DOM and register cleanup\r\n const cleanup = applyHeadClient(config);\r\n\r\n // If we're inside a component setup, register cleanup on unmount\r\n const instance = getCurrentInstance();\r\n if (instance) {\r\n instance.onUnmounted(() => cleanup());\r\n }\r\n}\r\n\r\n// ============= Utilities =============\r\n\r\nfunction escapeHtml(s: string): string {\r\n return s\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>');\r\n}\r\n\r\nfunction escapeAttr(s: string): string {\r\n return s\r\n .replace(/&/g, '&')\r\n .replace(/\"/g, '"');\r\n}\r\n","/**\r\n * SSR Factory\r\n *\r\n * Creates an SSR instance with plugin support.\r\n * Plugins are registered via `.use()` and called at appropriate points\r\n * during server rendering and client hydration.\r\n *\r\n * @example\r\n * ```ts\r\n * import { createSSR } from '@sigx/server-renderer';\r\n * import { islandsPlugin } from '@sigx/ssr-islands';\r\n *\r\n * const ssr = createSSR().use(islandsPlugin());\r\n * const html = await ssr.render(<App />);\r\n * ```\r\n */\r\n\r\nimport type { SSRPlugin } from './plugin';\r\nimport type { JSXElement } from 'sigx';\r\nimport type { App, AppContext } from 'sigx';\r\nimport { SSRContext, createSSRContext, SSRContextOptions, CorePendingAsync } from './server/context';\r\nimport { renderToChunks, renderToStringSync } from './server/render-core';\r\nimport { generateStreamingScript, generateReplacementScript } from './server/streaming';\r\nimport { enableSSRHead, collectSSRHead, renderHeadToString } from './head';\r\nimport type { StreamCallbacks } from './server/types';\r\n\r\n/**\r\n * Check if the input is an App instance (created via defineApp)\r\n */\r\nfunction isApp(input: any): input is App<any> {\r\n return input && typeof input === 'object' && '_rootComponent' in input && '_context' in input;\r\n}\r\n\r\n/**\r\n * Extract the JSX element and optional AppContext from a render input.\r\n */\r\nfunction extractInput(input: JSXElement | App): { element: JSXElement; appContext: AppContext | null } {\r\n if (isApp(input)) {\r\n return { element: input._rootComponent, appContext: input._context };\r\n }\r\n return { element: input, appContext: null };\r\n}\r\n\r\n/**\r\n * Yield all async streaming chunks — core-managed and plugin-managed — interleaved\r\n * so the fastest component streams first regardless of who manages it.\r\n *\r\n * Core-managed: ctx._pendingAsync (from render-core when no plugin overrides)\r\n * Plugin-managed: plugin.server.getStreamingChunks() async generators\r\n *\r\n * Both are raced together using a unified promise race loop.\r\n */\r\nasync function* streamAllAsyncChunks(\r\n ctx: SSRContext,\r\n plugins: SSRPlugin[]\r\n): AsyncGenerator<string> {\r\n type TaggedResult = { index: number; script: string };\r\n\r\n const hasCoreAsync = ctx._pendingAsync.length > 0;\r\n\r\n // Collect plugin streaming generators\r\n const pluginGenerators: AsyncGenerator<string>[] = [];\r\n for (const plugin of plugins) {\r\n const chunks = plugin.server?.getStreamingChunks?.(ctx);\r\n if (chunks) pluginGenerators.push(chunks);\r\n }\r\n\r\n const hasPluginStreaming = pluginGenerators.length > 0;\r\n\r\n // Nothing to stream\r\n if (!hasCoreAsync && !hasPluginStreaming) return;\r\n\r\n // Emit the $SIGX_REPLACE bootstrap script (needed by core replacements)\r\n if (hasCoreAsync) {\r\n yield generateStreamingScript();\r\n }\r\n\r\n // Build tagged promises for core-managed async components\r\n const corePromises: Promise<TaggedResult>[] = ctx._pendingAsync.map(\r\n (pending, index) =>\r\n pending.promise.then(html => {\r\n // Let plugins augment the resolved HTML\r\n let finalHtml = html;\r\n let extraScript = '';\r\n for (const plugin of plugins) {\r\n const result = plugin.server?.onAsyncComponentResolved?.(pending.id, finalHtml, ctx);\r\n if (result) {\r\n if (result.html !== undefined) finalHtml = result.html;\r\n if (result.script) extraScript += result.script;\r\n }\r\n }\r\n return {\r\n index,\r\n script: generateReplacementScript(pending.id, finalHtml, extraScript || undefined)\r\n };\r\n }).catch(error => {\r\n if (process.env.NODE_ENV !== 'production') {\r\n console.error(`Error streaming async component ${pending.id}:`, error);\r\n }\r\n return {\r\n index,\r\n script: generateReplacementScript(\r\n pending.id,\r\n `<div style=\"color:red;\">Error loading component</div>`\r\n )\r\n };\r\n })\r\n );\r\n\r\n const totalCore = corePromises.length;\r\n\r\n // Set up pump pattern for plugin generators so they can be raced alongside core\r\n interface PumpState {\r\n generator: AsyncGenerator<string>;\r\n done: boolean;\r\n }\r\n const pumps: PumpState[] = pluginGenerators.map(g => ({ generator: g, done: false }));\r\n\r\n // Active pump promises, keyed by their slot index (totalCore + i)\r\n const activePumps = new Map<number, Promise<TaggedResult>>();\r\n\r\n function pumpNext(pumpIdx: number): Promise<TaggedResult> {\r\n const slotIndex = totalCore + pumpIdx;\r\n return pumps[pumpIdx].generator.next().then(({ value, done }) => {\r\n if (done) {\r\n pumps[pumpIdx].done = true;\r\n activePumps.delete(slotIndex);\r\n return { index: slotIndex, script: '' };\r\n }\r\n // Re-queue: set new promise for this slot\r\n const nextP = pumpNext(pumpIdx);\r\n activePumps.set(slotIndex, nextP);\r\n return { index: slotIndex, script: value || '' };\r\n });\r\n }\r\n\r\n // Start initial pumps\r\n for (let i = 0; i < pumps.length; i++) {\r\n const slotIndex = totalCore + i;\r\n const p = pumpNext(i);\r\n activePumps.set(slotIndex, p);\r\n }\r\n\r\n // Race loop: core promises + pump promises\r\n const resolvedCore = new Set<number>();\r\n\r\n function getRaceablePromises(): Promise<TaggedResult>[] {\r\n const promises: Promise<TaggedResult>[] = [];\r\n for (let i = 0; i < totalCore; i++) {\r\n if (!resolvedCore.has(i)) promises.push(corePromises[i]);\r\n }\r\n for (const [, p] of activePumps) {\r\n promises.push(p);\r\n }\r\n return promises;\r\n }\r\n\r\n while (true) {\r\n const raceable = getRaceablePromises();\r\n if (raceable.length === 0) break;\r\n\r\n const winner = await Promise.race(raceable);\r\n\r\n if (winner.script) {\r\n yield winner.script;\r\n }\r\n\r\n if (winner.index < totalCore) {\r\n resolvedCore.add(winner.index);\r\n }\r\n // Pump promises auto-re-queue in pumpNext(), so no action needed here\r\n }\r\n}\r\n\r\nexport interface SSRInstance {\r\n /** Register a plugin */\r\n use(plugin: SSRPlugin): SSRInstance;\r\n\r\n /** Render to a complete HTML string */\r\n render(input: JSXElement | App, options?: SSRContextOptions | SSRContext): Promise<string>;\r\n\r\n /** Render to a ReadableStream with streaming support */\r\n renderStream(input: JSXElement | App, options?: SSRContextOptions | SSRContext): ReadableStream<string>;\r\n\r\n /** Render with callbacks for fine-grained streaming control */\r\n renderStreamWithCallbacks(\r\n input: JSXElement | App,\r\n callbacks: StreamCallbacks,\r\n options?: SSRContextOptions | SSRContext\r\n ): Promise<void>;\r\n\r\n /** Create a raw SSRContext with plugins pre-configured */\r\n createContext(options?: SSRContextOptions): SSRContext;\r\n}\r\n\r\n/**\r\n * Create an SSR instance with plugin support.\r\n */\r\nexport function createSSR(): SSRInstance {\r\n const plugins: SSRPlugin[] = [];\r\n\r\n function makeContext(options?: SSRContextOptions | SSRContext): SSRContext {\r\n // Accept an existing SSRContext (has _componentId) or create one from options\r\n const ctx = (options && '_componentId' in options)\r\n ? options as SSRContext\r\n : createSSRContext(options as SSRContextOptions | undefined);\r\n ctx._plugins = plugins;\r\n // Run plugin setup hooks\r\n for (const plugin of plugins) {\r\n plugin.server?.setup?.(ctx);\r\n }\r\n return ctx;\r\n }\r\n\r\n return {\r\n use(plugin: SSRPlugin): SSRInstance {\r\n plugins.push(plugin);\r\n return this;\r\n },\r\n\r\n async render(input, options?) {\r\n const { element, appContext } = extractInput(input);\r\n\r\n // Enable head collection during SSR rendering\r\n enableSSRHead();\r\n\r\n let result = '';\r\n let ctx: SSRContext;\r\n\r\n // Try fast synchronous render path first (works with or without plugins).\r\n // All plugin hooks called during the tree walk are synchronous.\r\n const syncCtx = makeContext(options);\r\n const buf: string[] = [];\r\n const syncOk = renderToStringSync(element, syncCtx, null, appContext, buf);\r\n\r\n if (syncOk) {\r\n result = buf.join('');\r\n ctx = syncCtx;\r\n } else {\r\n // Tree has async operations — fall back to async generator path.\r\n // We need a fresh context since the sync attempt may have partially\r\n // modified plugin state.\r\n ctx = makeContext(options);\r\n for await (const chunk of renderToChunks(element, ctx, null, appContext)) {\r\n result += chunk;\r\n }\r\n }\r\n\r\n // Collect injected HTML from all plugins\r\n for (const plugin of plugins) {\r\n const injected = plugin.server?.getInjectedHTML?.(ctx);\r\n if (injected) {\r\n result += typeof injected === 'string' ? injected : await injected;\r\n }\r\n }\r\n\r\n // Collect streaming chunks (for renderToString, await all)\r\n for (const plugin of plugins) {\r\n const chunks = plugin.server?.getStreamingChunks?.(ctx);\r\n if (chunks) {\r\n for await (const chunk of chunks) {\r\n result += chunk;\r\n }\r\n }\r\n }\r\n\r\n // Collect head elements from useHead() calls during rendering\r\n const headConfigs = collectSSRHead();\r\n if (headConfigs.length > 0) {\r\n ctx.addHead(renderHeadToString(headConfigs));\r\n }\r\n\r\n return result;\r\n },\r\n\r\n renderStream(input, options?) {\r\n const ctx = makeContext(options);\r\n ctx._streaming = true;\r\n const { element, appContext } = extractInput(input);\r\n\r\n return new ReadableStream<string>({\r\n async start(controller) {\r\n try {\r\n // Enable head collection\r\n enableSSRHead();\r\n\r\n // Phase 1: Render the main page (placeholders for async components)\r\n for await (const chunk of renderToChunks(element, ctx, null, appContext)) {\r\n controller.enqueue(chunk);\r\n }\r\n\r\n // Collect head from useHead() calls\r\n const headConfigs = collectSSRHead();\r\n if (headConfigs.length > 0) {\r\n ctx.addHead(renderHeadToString(headConfigs));\r\n }\r\n\r\n // Phase 2: Injected HTML from plugins\r\n for (const plugin of plugins) {\r\n const injected = plugin.server?.getInjectedHTML?.(ctx);\r\n if (injected) {\r\n const html = typeof injected === 'string' ? injected : await injected;\r\n if (html) controller.enqueue(html);\r\n }\r\n }\r\n\r\n // Phase 3: Stream async chunks — core + plugin interleaved\r\n for await (const chunk of streamAllAsyncChunks(ctx, plugins)) {\r\n controller.enqueue(chunk);\r\n }\r\n\r\n // Phase 4: Signal streaming complete\r\n controller.enqueue(\r\n `<script>window.__SIGX_STREAMING_COMPLETE__=true;window.dispatchEvent(new Event('sigx:ready'));</script>`\r\n );\r\n\r\n controller.close();\r\n } catch (error) {\r\n controller.error(error);\r\n }\r\n }\r\n });\r\n },\r\n\r\n async renderStreamWithCallbacks(input, callbacks, options?) {\r\n const ctx = makeContext(options);\r\n ctx._streaming = true;\r\n const { element, appContext } = extractInput(input);\r\n\r\n try {\r\n // Enable head collection\r\n enableSSRHead();\r\n\r\n // Phase 1: Render the shell\r\n let shellHtml = '';\r\n for await (const chunk of renderToChunks(element, ctx, null, appContext)) {\r\n shellHtml += chunk;\r\n }\r\n\r\n // Collect head from useHead() calls\r\n const headConfigs = collectSSRHead();\r\n if (headConfigs.length > 0) {\r\n ctx.addHead(renderHeadToString(headConfigs));\r\n }\r\n\r\n // Phase 2: Append plugin injected HTML to shell\r\n for (const plugin of plugins) {\r\n const injected = plugin.server?.getInjectedHTML?.(ctx);\r\n if (injected) {\r\n shellHtml += typeof injected === 'string' ? injected : await injected;\r\n }\r\n }\r\n\r\n shellHtml += `<script>window.__SIGX_STREAMING_COMPLETE__=true;window.dispatchEvent(new Event('sigx:ready'));</script>`;\r\n\r\n callbacks.onShellReady(shellHtml);\r\n\r\n // Phase 3: Stream async chunks — core + plugin interleaved\r\n for await (const chunk of streamAllAsyncChunks(ctx, plugins)) {\r\n callbacks.onAsyncChunk(chunk);\r\n }\r\n\r\n callbacks.onComplete();\r\n } catch (error) {\r\n callbacks.onError(error as Error);\r\n }\r\n },\r\n\r\n createContext(options?) {\r\n return makeContext(options);\r\n }\r\n };\r\n}\r\n","/**\r\n * Public SSR rendering APIs — convenience wrappers\r\n *\r\n * These delegate to `createSSR()` internally so there is exactly one\r\n * rendering pipeline. When no plugins are registered the plugin hooks\r\n * are simply no-ops, making these equivalent to calling `createSSR()`\r\n * directly — but with a simpler call signature for the common case.\r\n *\r\n * For plugin-driven rendering (islands, streaming async, etc.),\r\n * use `createSSR().use(plugin).render()` from `@sigx/server-renderer`.\r\n *\r\n * Entry points:\r\n * - `renderToString()` — full render to a single string\r\n * - `renderToStream()` — ReadableStream\r\n * - `renderToStreamWithCallbacks()` — callback-based streaming\r\n */\r\n\r\nimport type { JSXElement } from 'sigx';\r\nimport type { App } from 'sigx';\r\nimport type { SSRContext, SSRContextOptions } from './context';\r\nimport { createSSR } from '../ssr';\r\nimport type { StreamCallbacks } from './types';\r\n\r\n// Re-export StreamCallbacks from shared types (avoids circular dependency)\r\nexport type { StreamCallbacks } from './types';\r\n\r\n/** Shared no-plugin instance — created once, reused for all standalone calls. */\r\nconst _defaultSSR = createSSR();\r\n\r\n/**\r\n * Render JSX element or App to a ReadableStream.\r\n *\r\n * Internally delegates to `createSSR().renderStream()`.\r\n *\r\n * @example\r\n * ```tsx\r\n * // Simple usage with JSX\r\n * renderToStream(<App />)\r\n *\r\n * // With App instance for DI/plugins\r\n * const app = defineApp(<App />).use(router);\r\n * renderToStream(app)\r\n * ```\r\n */\r\nexport function renderToStream(input: JSXElement | App, context?: SSRContext): ReadableStream<string> {\r\n return _defaultSSR.renderStream(input, context);\r\n}\r\n\r\n/**\r\n * Render with callbacks for fine-grained streaming control.\r\n *\r\n * Internally delegates to `createSSR().renderStreamWithCallbacks()`.\r\n *\r\n * @example\r\n * ```tsx\r\n * const app = defineApp(<App />).use(router);\r\n * await renderToStreamWithCallbacks(app, callbacks)\r\n * ```\r\n */\r\nexport async function renderToStreamWithCallbacks(\r\n input: JSXElement | App,\r\n callbacks: StreamCallbacks,\r\n context?: SSRContext\r\n): Promise<void> {\r\n return _defaultSSR.renderStreamWithCallbacks(input, callbacks, context);\r\n}\r\n\r\n/**\r\n * Render JSX element or App to string.\r\n *\r\n * Internally delegates to `createSSR().render()`.\r\n *\r\n * @example\r\n * ```tsx\r\n * const html = await renderToString(<App />);\r\n *\r\n * const app = defineApp(<App />).use(router);\r\n * const html = await renderToString(app);\r\n * ```\r\n */\r\nexport async function renderToString(input: JSXElement | App, context?: SSRContext): Promise<string> {\r\n return _defaultSSR.render(input, context);\r\n}\r\n","/**\r\n * @sigx/server-renderer/server\r\n * \r\n * Server-side rendering with streaming support and hydration markers.\r\n * Strategy-agnostic — plugins add islands, Suspense, etc.\r\n */\r\n\r\n// Load SSR type augmentations (SSRHelper, ComponentSetupContext extensions)\r\nimport '../client-directives.js';\r\n\r\n// Load SSR directive type augmentation (adds getSSRProps to DirectiveDefinition)\r\nimport '../directive-ssr-types.js';\r\n\r\n// Patch getSSRProps onto built-in directives (show, etc.)\r\nimport { initDirectivesForSSR } from '../builtin-ssr-directives.js';\r\ninitDirectivesForSSR();\r\n\r\nexport { renderToStream, renderToString, renderToStreamWithCallbacks } from './render-api';\r\nexport { renderVNodeToString } from './render-core';\r\nexport { createSSRContext } from './context';\r\nexport type { SSRContext, SSRContextOptions, RenderOptions, CorePendingAsync } from './context';\r\nexport { generateStreamingScript, generateReplacementScript, escapeJsonForScript } from './streaming';\r\nexport type { SSRSignalFn, StreamCallbacks } from './types';\r\nexport { generateSignalKey } from './types';\r\n"],"mappings":"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"}
|
package/dist/ssr.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates an SSR instance with plugin support.
|
|
5
|
+
* Plugins are registered via `.use()` and called at appropriate points
|
|
6
|
+
* during server rendering and client hydration.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createSSR } from '@sigx/server-renderer';
|
|
11
|
+
* import { islandsPlugin } from '@sigx/ssr-islands';
|
|
12
|
+
*
|
|
13
|
+
* const ssr = createSSR().use(islandsPlugin());
|
|
14
|
+
* const html = await ssr.render(<App />);
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import type { SSRPlugin } from './plugin';
|
|
18
|
+
import type { JSXElement } from 'sigx';
|
|
19
|
+
import type { App } from 'sigx';
|
|
20
|
+
import { SSRContext, SSRContextOptions } from './server/context';
|
|
21
|
+
import type { StreamCallbacks } from './server/types';
|
|
22
|
+
export interface SSRInstance {
|
|
23
|
+
/** Register a plugin */
|
|
24
|
+
use(plugin: SSRPlugin): SSRInstance;
|
|
25
|
+
/** Render to a complete HTML string */
|
|
26
|
+
render(input: JSXElement | App, options?: SSRContextOptions | SSRContext): Promise<string>;
|
|
27
|
+
/** Render to a ReadableStream with streaming support */
|
|
28
|
+
renderStream(input: JSXElement | App, options?: SSRContextOptions | SSRContext): ReadableStream<string>;
|
|
29
|
+
/** Render with callbacks for fine-grained streaming control */
|
|
30
|
+
renderStreamWithCallbacks(input: JSXElement | App, callbacks: StreamCallbacks, options?: SSRContextOptions | SSRContext): Promise<void>;
|
|
31
|
+
/** Create a raw SSRContext with plugins pre-configured */
|
|
32
|
+
createContext(options?: SSRContextOptions): SSRContext;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create an SSR instance with plugin support.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createSSR(): SSRInstance;
|
|
38
|
+
//# sourceMappingURL=ssr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../src/ssr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,KAAK,EAAE,GAAG,EAAc,MAAM,MAAM,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAoB,iBAAiB,EAAoB,MAAM,kBAAkB,CAAC;AAIrG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAsJtD,MAAM,WAAW,WAAW;IACxB,wBAAwB;IACxB,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;IAEpC,uCAAuC;IACvC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE3F,wDAAwD;IACxD,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,UAAU,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAExG,+DAA+D;IAC/D,yBAAyB,CACrB,KAAK,EAAE,UAAU,GAAG,GAAG,EACvB,SAAS,EAAE,eAAe,EAC1B,OAAO,CAAC,EAAE,iBAAiB,GAAG,UAAU,GACzC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB,0DAA0D;IAC1D,aAAa,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,UAAU,CAAC;CAC1D;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,WAAW,CA8KvC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-B4Rf1Xot.js","names":[],"sources":["../src/server/types.ts"],"sourcesContent":["/**\r\n * Shared types for server rendering APIs.\r\n * \r\n * Kept separate to avoid circular imports between ssr.ts and render-api.ts.\r\n */\r\n\r\nimport type { Signal, PrimitiveSignal, Primitive } from 'sigx';\r\n\r\n/**\r\n * SSR-enhanced signal function type.\r\n * Extends the base signal() with an optional `name` parameter used as the\r\n * serialization key for hydration state transfer.\r\n *\r\n * Both `createTrackingSignal` (server/islands) and `createRestoringSignal` (client/hydration)\r\n * use this type and share the same key-generation contract via `generateSignalKey`.\r\n */\r\nexport type SSRSignalFn = {\r\n <T extends Primitive>(initial: T, name?: string): PrimitiveSignal<T>;\r\n <T extends object>(initial: T, name?: string): Signal<T>;\r\n};\r\n\r\n/**\r\n * Generate a stable key for a signal during SSR state tracking/restoration.\r\n *\r\n * Named signals use the provided name; unnamed signals use a positional key (`$0`, `$1`, ...).\r\n * Both server-side tracking (`createTrackingSignal`) and client-side restoration\r\n * (`createRestoringSignal`) MUST use this function to guarantee key parity.\r\n */\r\nexport function generateSignalKey(name: string | undefined, index: number): string {\r\n return name ?? `$${index}`;\r\n}\r\n\r\n/**\r\n * Streaming callbacks interface\r\n */\r\nexport interface StreamCallbacks {\r\n /** Called when the initial shell (synchronous content) is ready */\r\n onShellReady: (html: string) => void;\r\n /** Called for each async chunk (replacement scripts, hydration data) */\r\n onAsyncChunk: (chunk: string) => void;\r\n /** Called when all streaming is complete */\r\n onComplete: () => void;\r\n /** Called on error */\r\n onError: (error: Error) => void;\r\n}\r\n"],"mappings":"AA4BA,SAAgB,kBAAkB,MAA0B,OAAuB;AAC/E,QAAO,QAAQ,IAAI"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigx/server-renderer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Server-side rendering and client hydration for SigX",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,14 +17,10 @@
|
|
|
17
17
|
"./client": {
|
|
18
18
|
"import": "./dist/client/index.js",
|
|
19
19
|
"types": "./dist/client/index.d.ts"
|
|
20
|
-
},
|
|
21
|
-
"./jsx": {
|
|
22
|
-
"types": "./src/jsx.d.ts"
|
|
23
20
|
}
|
|
24
21
|
},
|
|
25
22
|
"files": [
|
|
26
|
-
"dist"
|
|
27
|
-
"src/jsx.d.ts"
|
|
23
|
+
"dist"
|
|
28
24
|
],
|
|
29
25
|
"keywords": [
|
|
30
26
|
"sigx",
|
|
@@ -32,8 +28,7 @@
|
|
|
32
28
|
"server-side-rendering",
|
|
33
29
|
"server",
|
|
34
30
|
"renderer",
|
|
35
|
-
"hydration"
|
|
36
|
-
"islands"
|
|
31
|
+
"hydration"
|
|
37
32
|
],
|
|
38
33
|
"author": "Andreas Ekdahl",
|
|
39
34
|
"license": "MIT",
|
|
@@ -47,16 +42,15 @@
|
|
|
47
42
|
"url": "https://github.com/signalxjs/core/issues"
|
|
48
43
|
},
|
|
49
44
|
"dependencies": {
|
|
50
|
-
"
|
|
51
|
-
"@sigx/reactivity": "^0.1.5",
|
|
52
|
-
"@sigx/runtime-dom": "^0.1.5"
|
|
45
|
+
"sigx": "^0.1.7"
|
|
53
46
|
},
|
|
54
47
|
"devDependencies": {
|
|
55
|
-
"
|
|
56
|
-
"
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
|
+
"vite": "^8.0.0-beta.9",
|
|
50
|
+
"@sigx/vite": "^0.1.7"
|
|
57
51
|
},
|
|
58
52
|
"scripts": {
|
|
59
|
-
"build": "
|
|
60
|
-
"dev": "
|
|
53
|
+
"build": "vite build && tsc --emitDeclarationOnly",
|
|
54
|
+
"dev": "vite build --watch"
|
|
61
55
|
}
|
|
62
56
|
}
|
package/dist/client/hydrate.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-side hydration for SSR'd content
|
|
3
|
-
*
|
|
4
|
-
* Hydration attaches the app to existing server-rendered DOM,
|
|
5
|
-
* then delegates all updates to the runtime-dom renderer.
|
|
6
|
-
*/
|
|
7
|
-
export type { HydrationOptions } from './types.js';
|
|
8
|
-
/**
|
|
9
|
-
* Hydrate a server-rendered app.
|
|
10
|
-
*
|
|
11
|
-
* This walks the existing DOM to attach event handlers, runs component
|
|
12
|
-
* setup functions to establish reactivity, then uses runtime-dom for updates.
|
|
13
|
-
*/
|
|
14
|
-
export declare function hydrate(element: any, container: Element): void;
|
|
15
|
-
/**
|
|
16
|
-
* Hydrate islands based on their strategies (selective hydration)
|
|
17
|
-
*/
|
|
18
|
-
export declare function hydrateIslands(): void;
|
|
19
|
-
//# sourceMappingURL=hydrate.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../../src/client/hydrate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAsBH,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAuFnD;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAS9D;AA0hBD;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAkBrC"}
|
package/dist/client/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["isComponent","_pendingServerState: Record<string, any> | null","_cachedIslandData: Record<string, IslandInfo> | null","childDom: Node | null","endNode: Node | null","current: Node | null","targetElement: Element | null","anchor: Comment | null","componentId: number | null","mountHooks: ((ctx: any) => void)[]","cleanupHooks: ((ctx: any) => void)[]","componentCtx: ComponentSetupContext","renderFn: (() => any) | undefined","endDom: Node | null","filtered: Record<string, any>","islandData: Record<string, IslandInfo>","node: Comment | null","node: Node | null","container: Node | null","vnode: VNode"],"sources":["../../src/client/registry.ts","../../src/client/hydrate.ts"],"sourcesContent":["/**\r\n * Component Registry for Island Hydration\r\n * \r\n * Components must be registered before they can be hydrated as islands.\r\n */\r\n\r\nimport type { ComponentFactory } from './types.js';\r\n\r\n/**\r\n * Registry mapping component names to component factories\r\n */\r\nconst componentRegistry = new Map<string, ComponentFactory>();\r\n\r\n/**\r\n * Register a component for island hydration.\r\n * Components must be registered before hydrateIslands() is called.\r\n * \r\n * @example\r\n * ```ts\r\n * import { registerComponent } from '@sigx/server-renderer/client';\r\n * import { Counter } from './components/Counter';\r\n * \r\n * registerComponent('Counter', Counter);\r\n * ```\r\n */\r\nexport function registerComponent(name: string, component: ComponentFactory): void {\r\n componentRegistry.set(name, component);\r\n}\r\n\r\n/**\r\n * Register multiple components at once\r\n * \r\n * @example\r\n * ```ts\r\n * import { registerComponents } from '@sigx/server-renderer/client';\r\n * import * as Components from './components';\r\n * \r\n * registerComponents(Components);\r\n * ```\r\n */\r\nexport function registerComponents(components: Record<string, ComponentFactory>): void {\r\n for (const [name, component] of Object.entries(components)) {\r\n if (isComponent(component)) {\r\n registerComponent(name, component);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Get a registered component by name\r\n */\r\nexport function getComponent(name: string): ComponentFactory | undefined {\r\n return componentRegistry.get(name);\r\n}\r\n\r\n/**\r\n * Check if a value is a component factory\r\n */\r\nfunction isComponent(value: unknown): value is ComponentFactory {\r\n return typeof value === 'function' && '__setup' in value;\r\n}\r\n\r\n/**\r\n * Hydration Registry class for more advanced use cases\r\n */\r\nexport class HydrationRegistry {\r\n private components = new Map<string, ComponentFactory>();\r\n\r\n register(name: string, component: ComponentFactory): this {\r\n this.components.set(name, component);\r\n return this;\r\n }\r\n\r\n registerAll(components: Record<string, ComponentFactory>): this {\r\n for (const [name, component] of Object.entries(components)) {\r\n if (isComponent(component)) {\r\n this.register(name, component);\r\n }\r\n }\r\n return this;\r\n }\r\n\r\n get(name: string): ComponentFactory | undefined {\r\n return this.components.get(name);\r\n }\r\n\r\n has(name: string): boolean {\r\n return this.components.has(name);\r\n }\r\n}\r\n","/**\r\n * Client-side hydration for SSR'd content\r\n * \r\n * Hydration attaches the app to existing server-rendered DOM,\r\n * then delegates all updates to the runtime-dom renderer.\r\n */\r\n\r\nimport {\r\n VNode,\r\n Fragment,\r\n Text,\r\n ComponentSetupContext,\r\n setCurrentInstance,\r\n getCurrentInstance,\r\n signal,\r\n SlotsObject,\r\n createPropsAccessor,\r\n createSlots,\r\n normalizeSubTree,\r\n registerContextExtension\r\n} from '@sigx/runtime-core';\r\nimport { effect } from '@sigx/reactivity';\r\nimport { render, patchProp } from '@sigx/runtime-dom';\r\nimport { getComponent, registerComponent } from './registry.js';\r\nimport type { ComponentFactory, IslandInfo, HydrationOptions } from './types.js';\r\n\r\n// Re-export types\r\nexport type { HydrationOptions } from './types.js';\r\n\r\n// Track server state for async components being mounted after streaming\r\nlet _pendingServerState: Record<string, any> | null = null;\r\n\r\n/**\r\n * Set server state that should be used for the next component mount.\r\n * Used internally when mounting async components after streaming.\r\n */\r\nfunction setPendingServerState(state: Record<string, any> | null): void {\r\n _pendingServerState = state;\r\n}\r\n\r\n/**\r\n * Register the SSR context extension for all components.\r\n * This provides the `ssr` object with a no-op `load()` for client-side rendering.\r\n * Also handles server state restoration for async streamed components.\r\n */\r\nregisterContextExtension((ctx: any) => {\r\n // Check if we have pending server state (from async streaming)\r\n const serverState = _pendingServerState;\r\n if (serverState) {\r\n ctx._serverState = serverState;\r\n _pendingServerState = null; // Clear after use\r\n\r\n // Override signal function to use restoring signal\r\n ctx.signal = createRestoringSignal(serverState);\r\n\r\n // ssr.load() should be a no-op since we have restored state\r\n ctx.ssr = {\r\n load: (_fn: () => Promise<void>) => {\r\n // Skip - using restored server state\r\n },\r\n isServer: false,\r\n isHydrating: true\r\n };\r\n } else if (ctx._serverState) {\r\n // Already has server state (from hydration)\r\n ctx.ssr = {\r\n load: (_fn: () => Promise<void>) => {\r\n // Skip - using restored server state\r\n },\r\n isServer: false,\r\n isHydrating: true\r\n };\r\n } else {\r\n // Default client-side ssr helper - runs async functions for client-side navigation\r\n ctx.ssr = {\r\n load: (fn: () => Promise<void>) => {\r\n // On client-side navigation (not hydration), execute the async function\r\n fn().catch(err => console.error('[SSR] load error:', err));\r\n },\r\n isServer: false,\r\n isHydrating: false\r\n };\r\n }\r\n});\r\n\r\ninterface InternalVNode extends VNode {\r\n _subTree?: VNode;\r\n _effect?: any;\r\n _componentProps?: any;\r\n _slots?: any;\r\n}\r\n\r\n/**\r\n * Creates a signal function that restores state from server-captured values.\r\n * Used during hydration of async components to avoid re-fetching data.\r\n */\r\nfunction createRestoringSignal(serverState: Record<string, any>) {\r\n let signalIndex = 0;\r\n\r\n return function restoringSignal<T extends object>(initial: T, name?: string): ReturnType<typeof signal<T>> {\r\n // Generate a stable key for this signal (must match server-side)\r\n const key = name ?? `$${signalIndex++}`;\r\n\r\n // Check if we have server state for this signal\r\n if (key in serverState) {\r\n console.log(`[Hydrate] Restoring signal \"${key}\" from server state:`, serverState[key]);\r\n return signal(serverState[key] as T);\r\n }\r\n\r\n // No server state, use initial value\r\n return signal(initial);\r\n };\r\n}\r\n\r\n/**\r\n * Hydrate a server-rendered app.\r\n * \r\n * This walks the existing DOM to attach event handlers, runs component\r\n * setup functions to establish reactivity, then uses runtime-dom for updates.\r\n */\r\nexport function hydrate(element: any, container: Element): void {\r\n const vnode = normalizeElement(element);\r\n if (!vnode) return;\r\n\r\n // Walk existing DOM, attach handlers, and mount components\r\n hydrateNode(vnode, container.firstChild, container);\r\n\r\n // Store vnode on container for potential future use\r\n (container as any)._vnode = vnode;\r\n}\r\n\r\n// Cache for parsed island data - can be invalidated when async components stream in\r\nlet _cachedIslandData: Record<string, IslandInfo> | null = null;\r\n\r\n/**\r\n * Invalidate the island data cache (called when async components stream in)\r\n */\r\nfunction invalidateIslandCache(): void {\r\n _cachedIslandData = null;\r\n}\r\n\r\n/**\r\n * Get island data from the __SIGX_ISLANDS__ script tag\r\n */\r\nfunction getIslandData(): Record<string, IslandInfo> {\r\n if (_cachedIslandData !== null) {\r\n return _cachedIslandData;\r\n }\r\n\r\n const dataScript = document.getElementById('__SIGX_ISLANDS__');\r\n if (!dataScript) {\r\n _cachedIslandData = {};\r\n return _cachedIslandData!;\r\n }\r\n\r\n try {\r\n _cachedIslandData = JSON.parse(dataScript.textContent || '{}');\r\n } catch {\r\n console.error('Failed to parse island data');\r\n _cachedIslandData = {};\r\n }\r\n\r\n return _cachedIslandData!;\r\n}\r\n\r\n/**\r\n * Get server state for a specific island/component by ID\r\n */\r\nfunction getIslandServerState(componentId: number): Record<string, any> | undefined {\r\n const islandData = getIslandData();\r\n const info = islandData[String(componentId)];\r\n return info?.state;\r\n}\r\n\r\n/**\r\n * Normalize any element to VNode\r\n */\r\nfunction normalizeElement(element: any): VNode | null {\r\n if (element == null || element === true || element === false) {\r\n return null;\r\n }\r\n\r\n if (typeof element === 'string' || typeof element === 'number') {\r\n return {\r\n type: Text,\r\n props: {},\r\n key: null,\r\n children: [],\r\n dom: null,\r\n text: element\r\n };\r\n }\r\n\r\n return element as VNode;\r\n}\r\n\r\n/**\r\n * Check if type is a component\r\n */\r\nfunction isComponent(type: any): type is ComponentFactory {\r\n return typeof type === 'function' && '__setup' in type;\r\n}\r\n\r\n/**\r\n * Get hydration strategy from vnode props\r\n */\r\nfunction getHydrationStrategy(props: Record<string, any>): { strategy: 'load' | 'idle' | 'visible' | 'media' | 'only'; media?: string } | null {\r\n if (props['client:load'] !== undefined) return { strategy: 'load' };\r\n if (props['client:idle'] !== undefined) return { strategy: 'idle' };\r\n if (props['client:visible'] !== undefined) return { strategy: 'visible' };\r\n if (props['client:only'] !== undefined) return { strategy: 'only' };\r\n if (props['client:media'] !== undefined) {\r\n return { strategy: 'media', media: props['client:media'] };\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Hydrate a VNode against existing DOM\r\n * This only attaches event handlers and refs - no DOM creation\r\n */\r\nfunction hydrateNode(vnode: VNode, dom: Node | null, parent: Node): Node | null {\r\n if (!vnode) return dom;\r\n\r\n // Skip text separator comments (<!--t-->) but NOT component markers\r\n while (dom && dom.nodeType === Node.COMMENT_NODE) {\r\n const commentText = (dom as Comment).data;\r\n // Component markers start with $c: or /$c: - don't skip those\r\n if (commentText.startsWith('$c:') || commentText.startsWith('/$c:') ||\r\n commentText.startsWith('$island:')) {\r\n break;\r\n }\r\n dom = dom.nextSibling;\r\n }\r\n\r\n if (vnode.type === Text) {\r\n if (dom && dom.nodeType === Node.TEXT_NODE) {\r\n vnode.dom = dom;\r\n return dom.nextSibling;\r\n }\r\n return dom;\r\n }\r\n\r\n if (vnode.type === Fragment) {\r\n let current = dom;\r\n for (const child of vnode.children) {\r\n current = hydrateNode(child, current, parent);\r\n }\r\n return current;\r\n }\r\n\r\n if (isComponent(vnode.type)) {\r\n // Check for client:* hydration strategy\r\n const strategy = vnode.props ? getHydrationStrategy(vnode.props) : null;\r\n\r\n if (strategy) {\r\n // Schedule hydration based on strategy\r\n return scheduleComponentHydration(vnode, dom, parent, strategy);\r\n }\r\n\r\n // No directive - hydrate immediately\r\n return hydrateComponent(vnode, dom, parent);\r\n }\r\n\r\n if (typeof vnode.type === 'string') {\r\n if (!dom || dom.nodeType !== Node.ELEMENT_NODE) {\r\n console.warn('[Hydrate] Expected element but got:', dom);\r\n return dom;\r\n }\r\n\r\n const el = dom as Element;\r\n vnode.dom = el;\r\n\r\n // Attach event handlers and props using patchProp from runtime-dom\r\n if (vnode.props) {\r\n for (const key in vnode.props) {\r\n if (key === 'children' || key === 'key') continue;\r\n if (key.startsWith('client:')) continue;\r\n\r\n // Use patchProp for consistent prop handling (events, refs, etc.)\r\n patchProp(el, key, null, vnode.props[key]);\r\n }\r\n }\r\n\r\n // Hydrate children\r\n let childDom: Node | null = el.firstChild;\r\n for (const child of vnode.children) {\r\n childDom = hydrateNode(child, childDom, el);\r\n }\r\n\r\n // Fix select value after children are hydrated\r\n if (vnode.type === 'select' && vnode.props) {\r\n fixSelectValue(el as HTMLElement, vnode.props);\r\n }\r\n\r\n return el.nextSibling;\r\n }\r\n\r\n return dom;\r\n}\r\n\r\n/**\r\n * Schedule component hydration based on strategy.\r\n * Returns the next DOM node after this component's content.\r\n */\r\nfunction scheduleComponentHydration(\r\n vnode: VNode,\r\n dom: Node | null,\r\n parent: Node,\r\n strategy: { strategy: 'load' | 'idle' | 'visible' | 'media' | 'only'; media?: string }\r\n): Node | null {\r\n // Find component boundaries in DOM (start/end markers)\r\n const { startNode, endNode } = findComponentBoundaries(dom);\r\n\r\n const doHydrate = () => {\r\n hydrateComponent(vnode, startNode, parent);\r\n };\r\n\r\n switch (strategy.strategy) {\r\n case 'load':\r\n // Hydrate immediately\r\n console.log('[Hydrate] client:load - hydrating immediately');\r\n doHydrate();\r\n break;\r\n\r\n case 'idle':\r\n // Hydrate during browser idle time\r\n console.log('[Hydrate] client:idle - scheduling for idle time');\r\n if ('requestIdleCallback' in window) {\r\n requestIdleCallback(() => {\r\n console.log('[Hydrate] client:idle - idle callback fired');\r\n doHydrate();\r\n });\r\n } else {\r\n setTimeout(() => {\r\n console.log('[Hydrate] client:idle - timeout fallback fired');\r\n doHydrate();\r\n }, 200);\r\n }\r\n break;\r\n\r\n case 'visible':\r\n // Hydrate when component scrolls into view\r\n console.log('[Hydrate] client:visible - observing visibility');\r\n observeComponentVisibility(startNode, endNode, doHydrate);\r\n break;\r\n\r\n case 'media':\r\n // Hydrate when media query matches\r\n if (strategy.media) {\r\n const mql = window.matchMedia(strategy.media);\r\n if (mql.matches) {\r\n doHydrate();\r\n } else {\r\n const handler = (e: MediaQueryListEvent) => {\r\n if (e.matches) {\r\n mql.removeEventListener('change', handler);\r\n doHydrate();\r\n }\r\n };\r\n mql.addEventListener('change', handler);\r\n }\r\n }\r\n break;\r\n\r\n case 'only':\r\n // client:only - component was not server rendered, mount fresh\r\n // For now, just hydrate (server renders a placeholder)\r\n doHydrate();\r\n break;\r\n }\r\n\r\n // Return the node after the component's closing marker\r\n return endNode ? endNode.nextSibling : dom;\r\n}\r\n\r\n/**\r\n * Find component start/end markers in DOM\r\n */\r\nfunction findComponentBoundaries(dom: Node | null): { startNode: Node | null; endNode: Node | null } {\r\n let startNode = dom;\r\n let endNode: Node | null = null;\r\n\r\n // Look for component marker <!--$c:N-->\r\n if (dom && dom.nodeType === Node.COMMENT_NODE) {\r\n const text = (dom as Comment).data;\r\n if (text.startsWith('$c:')) {\r\n const id = text.slice(3);\r\n startNode = dom;\r\n\r\n // Find the closing marker <!--/$c:N-->\r\n let current: Node | null = dom.nextSibling;\r\n while (current) {\r\n if (current.nodeType === Node.COMMENT_NODE) {\r\n const closeText = (current as Comment).data;\r\n if (closeText === `/$c:${id}`) {\r\n endNode = current;\r\n break;\r\n }\r\n }\r\n current = current.nextSibling;\r\n }\r\n }\r\n }\r\n\r\n return { startNode, endNode };\r\n}\r\n\r\n/**\r\n * Observe when a component's DOM becomes visible\r\n */\r\nfunction observeComponentVisibility(startNode: Node | null, endNode: Node | null, callback: () => void): void {\r\n // Find an element to observe between start and end markers\r\n let targetElement: Element | null = null;\r\n let current = startNode?.nextSibling || null;\r\n\r\n while (current && current !== endNode) {\r\n if (current.nodeType === Node.ELEMENT_NODE) {\r\n targetElement = current as Element;\r\n break;\r\n }\r\n current = current.nextSibling;\r\n }\r\n\r\n if (!targetElement) {\r\n // No element found, hydrate immediately\r\n callback();\r\n return;\r\n }\r\n\r\n const observer = new IntersectionObserver((entries) => {\r\n for (const entry of entries) {\r\n if (entry.isIntersecting) {\r\n observer.disconnect();\r\n callback();\r\n break;\r\n }\r\n }\r\n }, { rootMargin: '50px' });\r\n\r\n observer.observe(targetElement);\r\n}\r\n\r\n/**\r\n * Hydrate a component - run setup and create reactive effect\r\n * @param vnode - The VNode to hydrate\r\n * @param dom - The DOM node to start from\r\n * @param parent - The parent node\r\n * @param serverState - Optional state captured from server for async components\r\n */\r\nfunction hydrateComponent(vnode: VNode, dom: Node | null, parent: Node, serverState?: Record<string, any>): Node | null {\r\n const componentFactory = vnode.type as unknown as ComponentFactory;\r\n const setup = componentFactory.__setup;\r\n const componentName = componentFactory.__name || 'Anonymous';\r\n\r\n // Auto-register component for async streaming hydration\r\n if (componentName && componentName !== 'Anonymous') {\r\n registerComponent(componentName, componentFactory);\r\n }\r\n\r\n // Check if we're at a component marker comment\r\n let anchor: Comment | null = null;\r\n let componentId: number | null = null;\r\n if (dom && dom.nodeType === Node.COMMENT_NODE) {\r\n const commentText = (dom as Comment).data;\r\n if (commentText.startsWith('$c:')) {\r\n // Use this comment as the component's anchor (like runtime-dom does)\r\n anchor = dom as Comment;\r\n componentId = parseInt(commentText.slice(3), 10);\r\n dom = dom.nextSibling;\r\n\r\n // Skip island markers too\r\n while (dom && dom.nodeType === Node.COMMENT_NODE) {\r\n const text = (dom as Comment).data;\r\n if (text.startsWith('$island:')) {\r\n dom = dom.nextSibling;\r\n } else {\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Try to get server state from island data if not provided\r\n if (!serverState && componentId !== null) {\r\n serverState = getIslandServerState(componentId);\r\n }\r\n\r\n const internalVNode = vnode as InternalVNode;\r\n const initialProps = vnode.props || {};\r\n const { children, slots: slotsFromProps, ...propsData } = filterClientDirectives(initialProps);\r\n\r\n // Create reactive props\r\n const reactiveProps = signal(propsData);\r\n internalVNode._componentProps = reactiveProps;\r\n\r\n // Create slots\r\n const slots = createSlots(children, slotsFromProps);\r\n internalVNode._slots = slots;\r\n\r\n const mountHooks: ((ctx: any) => void)[] = [];\r\n const cleanupHooks: ((ctx: any) => void)[] = [];\r\n\r\n const parentInstance = getCurrentInstance();\r\n\r\n // Use restoring signal when we have server state to restore\r\n const signalFn = serverState\r\n ? createRestoringSignal(serverState)\r\n : signal;\r\n\r\n // Create SSR helper for client-side\r\n // When hydrating with server state, ssr.load() is a no-op (data already restored)\r\n const hasServerState = !!serverState;\r\n const ssrHelper = {\r\n load(_fn: () => Promise<void>): void {\r\n // No-op on client when hydrating - signal state was restored from server\r\n // The data is already in the signals, no need to re-fetch\r\n if (hasServerState) {\r\n console.log(`[Hydrate] Skipping ssr.load() - using restored server state`);\r\n }\r\n // If no server state, this is a client-only render, but ssr.load() \r\n // shouldn't be called on pure client components anyway\r\n },\r\n isServer: false,\r\n isHydrating: hasServerState\r\n };\r\n\r\n const componentCtx: ComponentSetupContext = {\r\n el: parent as HTMLElement,\r\n signal: signalFn as typeof signal,\r\n props: createPropsAccessor(reactiveProps),\r\n slots: slots,\r\n emit: (event: string, ...args: any[]) => {\r\n const eventName = `on${event[0].toUpperCase() + event.slice(1)}`;\r\n const handler = (reactiveProps as any)[eventName];\r\n if (handler && typeof handler === 'function') {\r\n handler(...args);\r\n }\r\n },\r\n parent: parentInstance,\r\n onMount: (fn) => { mountHooks.push(fn); },\r\n onCleanup: (fn) => { cleanupHooks.push(fn); },\r\n expose: () => { },\r\n renderFn: null,\r\n update: () => { },\r\n ssr: ssrHelper,\r\n _serverState: serverState\r\n };\r\n\r\n const prev = setCurrentInstance(componentCtx);\r\n let renderFn: (() => any) | undefined;\r\n\r\n try {\r\n renderFn = setup(componentCtx);\r\n } catch (err) {\r\n console.error(`Error hydrating component ${componentName}:`, err);\r\n } finally {\r\n setCurrentInstance(prev);\r\n }\r\n\r\n // Track where the component's DOM starts (use anchor if found)\r\n const startDom = anchor || dom;\r\n let endDom: Node | null = dom;\r\n\r\n if (renderFn) {\r\n componentCtx.renderFn = renderFn;\r\n let isFirstRender = true;\r\n\r\n // Create reactive effect - on first run, hydrate; on subsequent, use render()\r\n const componentEffect = effect(() => {\r\n const prevInstance = setCurrentInstance(componentCtx);\r\n try {\r\n const subTreeResult = componentCtx.renderFn!();\r\n if (subTreeResult == null) return;\r\n\r\n const subTree = normalizeSubTree(subTreeResult);\r\n const prevSubTree = internalVNode._subTree;\r\n\r\n if (isFirstRender) {\r\n // First render - hydrate against existing DOM\r\n isFirstRender = false;\r\n endDom = hydrateNode(subTree, dom, parent);\r\n internalVNode._subTree = subTree;\r\n } else {\r\n // Subsequent renders - use runtime-dom's render for patching\r\n // The container is where prevSubTree.dom lives\r\n if (prevSubTree && prevSubTree.dom) {\r\n const container = prevSubTree.dom.parentNode as Element;\r\n if (container) {\r\n // Set _vnode so render() knows what's there\r\n (container as any)._vnode = prevSubTree;\r\n render(subTree, container);\r\n }\r\n }\r\n internalVNode._subTree = subTree;\r\n }\r\n } finally {\r\n setCurrentInstance(prevInstance);\r\n }\r\n });\r\n\r\n internalVNode._effect = componentEffect;\r\n componentCtx.update = () => componentEffect();\r\n }\r\n\r\n // Use anchor comment as the component's dom reference (like runtime-dom does)\r\n // This ensures the reconciler can use it as a stable anchor\r\n vnode.dom = anchor || startDom;\r\n\r\n // Run mount hooks\r\n const mountCtx = { el: parent as Element };\r\n mountHooks.forEach(hook => hook(mountCtx));\r\n\r\n // Store cleanup\r\n vnode.cleanup = () => {\r\n cleanupHooks.forEach(hook => hook(mountCtx));\r\n };\r\n\r\n // Skip past the end marker comment (<!--/$c:id-->)\r\n let result = endDom;\r\n while (result && result.nodeType === Node.COMMENT_NODE) {\r\n const commentText = (result as Comment).data;\r\n if (commentText.startsWith('/$c:')) {\r\n result = result.nextSibling;\r\n break;\r\n }\r\n result = result.nextSibling;\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ============= Helper Functions =============\r\n\r\nfunction filterClientDirectives(props: Record<string, any>): Record<string, any> {\r\n const filtered: Record<string, any> = {};\r\n for (const key in props) {\r\n if (!key.startsWith('client:')) {\r\n filtered[key] = props[key];\r\n }\r\n }\r\n return filtered;\r\n}\r\n\r\n/**\r\n * Fix select element value after hydrating children.\r\n * This is needed because <select>.value only works after <option> children exist in DOM.\r\n */\r\nfunction fixSelectValue(dom: HTMLElement, props: any) {\r\n if (dom.tagName === 'SELECT' && 'value' in props) {\r\n const val = props.value;\r\n if ((dom as HTMLSelectElement).multiple) {\r\n const options = (dom as HTMLSelectElement).options;\r\n const valArray = Array.isArray(val) ? val : [val];\r\n for (let i = 0; i < options.length; i++) {\r\n options[i].selected = valArray.includes(options[i].value);\r\n }\r\n } else {\r\n (dom as HTMLSelectElement).value = String(val);\r\n }\r\n }\r\n}\r\n\r\n// createPropsAccessor, createSlots, and normalizeSubTree are now imported from @sigx/runtime-core\r\n\r\n// ============= Island Hydration =============\r\n\r\n/**\r\n * Hydrate islands based on their strategies (selective hydration)\r\n */\r\nexport function hydrateIslands(): void {\r\n const dataScript = document.getElementById('__SIGX_ISLANDS__');\r\n if (!dataScript) {\r\n return;\r\n }\r\n\r\n let islandData: Record<string, IslandInfo>;\r\n try {\r\n islandData = JSON.parse(dataScript.textContent || '{}');\r\n } catch {\r\n console.error('Failed to parse island data');\r\n return;\r\n }\r\n\r\n for (const [idStr, info] of Object.entries(islandData)) {\r\n const id = parseInt(idStr, 10);\r\n scheduleHydration(id, info);\r\n }\r\n}\r\n\r\n/**\r\n * Schedule hydration based on strategy\r\n */\r\nfunction scheduleHydration(id: number, info: IslandInfo): void {\r\n const marker = findIslandMarker(id);\r\n if (!marker) {\r\n console.warn(`Island marker not found for id ${id}`);\r\n return;\r\n }\r\n\r\n const component = info.componentId ? getComponent(info.componentId) : null;\r\n if (!component && info.strategy !== 'only') {\r\n console.warn(`Component \"${info.componentId}\" not registered for hydration`);\r\n return;\r\n }\r\n\r\n switch (info.strategy) {\r\n case 'load':\r\n hydrateIsland(marker, component!, info);\r\n break;\r\n\r\n case 'idle':\r\n if ('requestIdleCallback' in window) {\r\n requestIdleCallback(() => hydrateIsland(marker, component!, info));\r\n } else {\r\n setTimeout(() => hydrateIsland(marker, component!, info), 200);\r\n }\r\n break;\r\n\r\n case 'visible':\r\n observeVisibility(marker, () => hydrateIsland(marker, component!, info));\r\n break;\r\n\r\n case 'media':\r\n if (info.media) {\r\n const mql = window.matchMedia(info.media);\r\n if (mql.matches) {\r\n hydrateIsland(marker, component!, info);\r\n } else {\r\n mql.addEventListener('change', function handler(e) {\r\n if (e.matches) {\r\n mql.removeEventListener('change', handler);\r\n hydrateIsland(marker, component!, info);\r\n }\r\n });\r\n }\r\n }\r\n break;\r\n\r\n case 'only':\r\n if (component) {\r\n mountClientOnly(marker, component, info);\r\n }\r\n break;\r\n }\r\n}\r\n\r\nfunction findIslandMarker(id: number): Comment | null {\r\n const walker = document.createTreeWalker(\r\n document.body,\r\n NodeFilter.SHOW_COMMENT,\r\n null\r\n );\r\n\r\n let node: Comment | null;\r\n while ((node = walker.nextNode() as Comment | null)) {\r\n if (node.data === `$c:${id}`) {\r\n return node;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nfunction observeVisibility(marker: Comment, callback: () => void): void {\r\n let node: Node | null = marker.nextSibling;\r\n while (node && node.nodeType !== Node.ELEMENT_NODE) {\r\n node = node.nextSibling;\r\n }\r\n\r\n if (!node) return;\r\n\r\n const observer = new IntersectionObserver((entries) => {\r\n for (const entry of entries) {\r\n if (entry.isIntersecting) {\r\n observer.disconnect();\r\n callback();\r\n break;\r\n }\r\n }\r\n });\r\n\r\n observer.observe(node as Element);\r\n}\r\n\r\nfunction hydrateIsland(marker: Comment, component: ComponentFactory, info: IslandInfo): void {\r\n // Find the container element after the marker\r\n let container: Node | null = marker.nextSibling;\r\n while (container && container.nodeType === Node.COMMENT_NODE) {\r\n container = container.nextSibling;\r\n }\r\n\r\n if (!container || container.nodeType !== Node.ELEMENT_NODE) {\r\n console.warn('No element found for island hydration');\r\n return;\r\n }\r\n\r\n const props = info.props || {};\r\n\r\n // Set pending server state so context extension picks it up\r\n if (info.state) {\r\n setPendingServerState(info.state);\r\n }\r\n\r\n // Create VNode and render with runtime-dom\r\n const vnode: VNode = {\r\n type: component as any,\r\n props: props,\r\n key: null,\r\n children: [],\r\n dom: null\r\n };\r\n\r\n // Use a wrapper to contain the component\r\n const wrapper = document.createElement('div');\r\n wrapper.style.display = 'contents';\r\n\r\n // Replace the SSR content with the hydrated component\r\n const parent = container.parentNode!;\r\n parent.insertBefore(wrapper, container);\r\n parent.removeChild(container);\r\n\r\n render(vnode, wrapper);\r\n}\r\n\r\nfunction mountClientOnly(marker: Comment, component: ComponentFactory, info: IslandInfo): void {\r\n let placeholder = marker.nextSibling;\r\n while (placeholder && placeholder.nodeType === Node.COMMENT_NODE) {\r\n placeholder = placeholder.nextSibling;\r\n }\r\n\r\n if (!placeholder || !(placeholder as Element).hasAttribute?.('data-island')) {\r\n return;\r\n }\r\n\r\n const props = info.props || {};\r\n const container = placeholder as Element;\r\n container.innerHTML = '';\r\n\r\n const vnode: VNode = {\r\n type: component as any,\r\n props,\r\n key: null,\r\n children: [],\r\n dom: null\r\n };\r\n\r\n render(vnode, container);\r\n}\r\n\r\n/**\r\n * Set up listener for async components streaming in.\r\n * This is set up at module load time to catch events early.\r\n */\r\nlet _asyncListenerSetup = false;\r\n\r\nfunction ensureAsyncHydrationListener(): void {\r\n if (_asyncListenerSetup) return;\r\n _asyncListenerSetup = true;\r\n\r\n // Listen for async component ready events\r\n document.addEventListener('sigx:async-ready', (event: Event) => {\r\n const customEvent = event as CustomEvent;\r\n const { id, state } = customEvent.detail || {};\r\n\r\n console.log(`[Hydrate] Async component ${id} ready, hydrating...`);\r\n\r\n // Invalidate cache so we get fresh island data\r\n invalidateIslandCache();\r\n\r\n // Find the placeholder element\r\n const placeholder = document.querySelector(`[data-async-placeholder=\"${id}\"]`);\r\n if (!placeholder) {\r\n console.warn(`[Hydrate] Could not find placeholder for async component ${id}`);\r\n return;\r\n }\r\n\r\n // Get the island info (should have been updated by the streaming script)\r\n const islandData = getIslandData();\r\n const info = islandData[String(id)];\r\n\r\n if (!info) {\r\n console.warn(`[Hydrate] No island data for async component ${id}`);\r\n return;\r\n }\r\n\r\n // Hydrate the async component\r\n hydrateAsyncComponent(placeholder as Element, info);\r\n });\r\n}\r\n\r\n// Set up the listener immediately when this module loads (browser only)\r\nif (typeof document !== 'undefined') {\r\n ensureAsyncHydrationListener();\r\n}\r\n\r\n/**\r\n * Hydrate an async component that just streamed in.\r\n * The DOM has already been replaced with server-rendered content by $SIGX_REPLACE.\r\n * Since the streamed content doesn't include component markers, we mount fresh\r\n * with the server state restored via the context extension.\r\n */\r\nfunction hydrateAsyncComponent(container: Element, info: IslandInfo): void {\r\n if (!info.componentId) {\r\n console.error(`[Hydrate] No componentId in island info`);\r\n return;\r\n }\r\n\r\n const component = getComponent(info.componentId);\r\n if (!component) {\r\n console.error(`[Hydrate] Component \"${info.componentId}\" not registered`);\r\n return;\r\n }\r\n\r\n const props = info.props || {};\r\n const serverState = info.state;\r\n\r\n // Clear the streamed HTML - we'll mount fresh with reactivity\r\n container.innerHTML = '';\r\n\r\n // Set pending server state so context extension picks it up\r\n if (serverState) {\r\n setPendingServerState(serverState);\r\n }\r\n\r\n // Create vnode (no need for __serverState prop, context extension handles it)\r\n const vnode: VNode = {\r\n type: component as any,\r\n props: props,\r\n key: null,\r\n children: [],\r\n dom: null\r\n };\r\n\r\n // Mount the component fresh\r\n render(vnode, container);\r\n\r\n console.log(`[Hydrate] Async component \"${info.componentId}\" mounted with server state`);\r\n}\r\n"],"mappings":"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"}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Component Registry for Island Hydration
|
|
3
|
-
*
|
|
4
|
-
* Components must be registered before they can be hydrated as islands.
|
|
5
|
-
*/
|
|
6
|
-
import type { ComponentFactory } from './types.js';
|
|
7
|
-
/**
|
|
8
|
-
* Register a component for island hydration.
|
|
9
|
-
* Components must be registered before hydrateIslands() is called.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* import { registerComponent } from '@sigx/server-renderer/client';
|
|
14
|
-
* import { Counter } from './components/Counter';
|
|
15
|
-
*
|
|
16
|
-
* registerComponent('Counter', Counter);
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
export declare function registerComponent(name: string, component: ComponentFactory): void;
|
|
20
|
-
/**
|
|
21
|
-
* Register multiple components at once
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```ts
|
|
25
|
-
* import { registerComponents } from '@sigx/server-renderer/client';
|
|
26
|
-
* import * as Components from './components';
|
|
27
|
-
*
|
|
28
|
-
* registerComponents(Components);
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export declare function registerComponents(components: Record<string, ComponentFactory>): void;
|
|
32
|
-
/**
|
|
33
|
-
* Get a registered component by name
|
|
34
|
-
*/
|
|
35
|
-
export declare function getComponent(name: string): ComponentFactory | undefined;
|
|
36
|
-
/**
|
|
37
|
-
* Hydration Registry class for more advanced use cases
|
|
38
|
-
*/
|
|
39
|
-
export declare class HydrationRegistry {
|
|
40
|
-
private components;
|
|
41
|
-
register(name: string, component: ComponentFactory): this;
|
|
42
|
-
registerAll(components: Record<string, ComponentFactory>): this;
|
|
43
|
-
get(name: string): ComponentFactory | undefined;
|
|
44
|
-
has(name: string): boolean;
|
|
45
|
-
}
|
|
46
|
-
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/client/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAOnD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAEjF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,IAAI,CAMrF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAEvE;AASD;;GAEG;AACH,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,UAAU,CAAuC;IAEzD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAKzD,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,IAAI;IAS/D,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAI/C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAG7B"}
|
package/dist/client/types.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types for client-side hydration
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Component factory with setup function
|
|
6
|
-
*/
|
|
7
|
-
export interface ComponentFactory {
|
|
8
|
-
__setup: Function;
|
|
9
|
-
__name?: string;
|
|
10
|
-
__async?: boolean;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Hydration options
|
|
14
|
-
*/
|
|
15
|
-
export interface HydrationOptions {
|
|
16
|
-
recover?: boolean;
|
|
17
|
-
onMismatch?: (message: string, node: Node | null, vnode: any) => void;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Island information serialized from server
|
|
21
|
-
*/
|
|
22
|
-
export interface IslandInfo {
|
|
23
|
-
strategy: 'load' | 'idle' | 'visible' | 'media' | 'only';
|
|
24
|
-
media?: string;
|
|
25
|
-
props?: Record<string, any>;
|
|
26
|
-
componentId?: string;
|
|
27
|
-
/** Captured signal state from async setup for client hydration */
|
|
28
|
-
state?: Record<string, any>;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* VNode representation
|
|
32
|
-
*/
|
|
33
|
-
export interface VNode {
|
|
34
|
-
type: any;
|
|
35
|
-
props: Record<string, any>;
|
|
36
|
-
key: string | number | null;
|
|
37
|
-
children: VNode[];
|
|
38
|
-
dom: any | null;
|
|
39
|
-
text?: string | number;
|
|
40
|
-
parent?: VNode | null;
|
|
41
|
-
cleanup?: () => void;
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACzE;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,KAAK;IAClB,IAAI,EAAE,GAAG,CAAC;IACV,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB"}
|