@sigx/server-renderer 0.1.5 → 0.1.6

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.
@@ -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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"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":["componentStack: number[]","head: string[]","pendingAsync: PendingAsyncComponent[]","state: Record<string, any>","isComponent","defaults: Partial<TProps>","filterClientDirectives","filtered: Record<string, any>","serialized: Record<string, any>","islandInfo: IslandInfo","slots: SlotsObject<any>","ssrLoads: Promise<void>[]","componentCtx: ComponentSetupContext","islandInfo","islandData: Record<number, IslandInfo>","ESCAPE: Record<string, string>","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/server/context.ts","../src/server/stream.ts","../src/client/registry.ts","../src/client/hydrate.ts"],"sourcesContent":["/**\r\n * SSR Context - tracks component boundaries and hydration markers during rendering\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\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 * Registered islands and their hydration strategies\r\n */\r\n _islands: Map<number, IslandInfo>;\r\n\r\n /**\r\n * Collected head elements (scripts, styles, etc.)\r\n */\r\n _head: string[];\r\n\r\n /**\r\n * Pending async components for streaming\r\n */\r\n _pendingAsync: PendingAsyncComponent[];\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 * Register an island for selective hydration\r\n */\r\n registerIsland(id: number, info: IslandInfo): void;\r\n\r\n /**\r\n * Get all registered islands\r\n */\r\n getIslands(): Map<number, IslandInfo>;\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 * Register a pending async component for streaming\r\n */\r\n addPendingAsync(pending: PendingAsyncComponent): void;\r\n\r\n /**\r\n * Get all pending async components\r\n */\r\n getPendingAsync(): PendingAsyncComponent[];\r\n}\r\n\r\nexport interface IslandInfo {\r\n /**\r\n * Hydration strategy: 'load' | 'idle' | 'visible' | 'media' | 'only'\r\n */\r\n strategy: HydrationStrategy;\r\n\r\n /**\r\n * Media query for 'media' strategy\r\n */\r\n media?: string;\r\n\r\n /**\r\n * Component props to serialize for client hydration\r\n */\r\n props?: Record<string, any>;\r\n\r\n /**\r\n * Component name/identifier for client\r\n */\r\n componentId?: string;\r\n\r\n /**\r\n * Captured signal state from async setup for client hydration\r\n */\r\n state?: Record<string, any>;\r\n\r\n /**\r\n * Placeholder HTML for streaming async components\r\n */\r\n placeholder?: string;\r\n}\r\n\r\n/**\r\n * Pending async component that will be streamed later\r\n */\r\nexport interface PendingAsyncComponent {\r\n /** Component ID */\r\n id: number;\r\n /** Promise that resolves to rendered HTML */\r\n promise: Promise<string>;\r\n /** Signal state captured during render */\r\n signalMap: Map<string, any>;\r\n /** Island info reference */\r\n islandInfo: IslandInfo;\r\n}\r\n\r\nexport type HydrationStrategy = 'load' | 'idle' | 'visible' | 'media' | 'only';\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 islands = new Map<number, IslandInfo>();\r\n const head: string[] = [];\r\n const pendingAsync: PendingAsyncComponent[] = [];\r\n\r\n return {\r\n _componentId: componentId,\r\n _componentStack: componentStack,\r\n _islands: islands,\r\n _head: head,\r\n _pendingAsync: 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 registerIsland(id: number, info: IslandInfo) {\r\n islands.set(id, info);\r\n },\r\n\r\n getIslands() {\r\n return islands;\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 addPendingAsync(pending: PendingAsyncComponent) {\r\n pendingAsync.push(pending);\r\n },\r\n\r\n getPendingAsync() {\r\n return pendingAsync;\r\n }\r\n };\r\n}\r\n","/**\r\n * Streaming SSR renderer with hydration markers\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 PropsAccessor\r\n} from '@sigx/runtime-core';\r\nimport { SSRContext, createSSRContext, IslandInfo, HydrationStrategy, PendingAsyncComponent } from './context.js';\r\n\r\n// Client directive props\r\nconst CLIENT_DIRECTIVES = ['client:load', 'client:idle', 'client:visible', 'client:media', 'client:only'] as const;\r\ntype ClientDirective = typeof CLIENT_DIRECTIVES[number];\r\n\r\n/**\r\n * Creates a tracking signal function that records signal names and values.\r\n * Used during async setup to capture state for client hydration.\r\n */\r\nfunction createTrackingSignal(signalMap: Map<string, any>) {\r\n let signalIndex = 0;\r\n\r\n return function trackingSignal<T extends object>(initial: T, name?: string): ReturnType<typeof signal<T>> {\r\n // Generate a stable key for this signal\r\n const key = name ?? `$${signalIndex++}`;\r\n\r\n // Create the real signal\r\n const sig = signal(initial);\r\n\r\n // Capture initial value\r\n signalMap.set(key, initial);\r\n\r\n // Create a proxy that tracks writes\r\n const proxy = new Proxy(sig, {\r\n get(target, prop) {\r\n if (prop === 'value') {\r\n // Return current value from the real signal\r\n return (target as any).value;\r\n }\r\n return (target as any)[prop];\r\n },\r\n set(target, prop, newValue) {\r\n if (prop === 'value') {\r\n // Update the signal and track the new value\r\n (target as any).value = newValue;\r\n signalMap.set(key, newValue);\r\n return true;\r\n }\r\n (target as any)[prop] = newValue;\r\n return true;\r\n }\r\n });\r\n\r\n return proxy as ReturnType<typeof signal<T>>;\r\n };\r\n}\r\n\r\n/**\r\n * Serialize captured signal state for client hydration\r\n */\r\nfunction serializeSignalState(signalMap: Map<string, any>): Record<string, any> | undefined {\r\n if (signalMap.size === 0) return undefined;\r\n\r\n const state: Record<string, any> = {};\r\n for (const [key, value] of signalMap) {\r\n try {\r\n // Test if serializable\r\n JSON.stringify(value);\r\n state[key] = value;\r\n } catch {\r\n // Skip non-serializable values\r\n console.warn(`SSR: Signal \"${key}\" has non-serializable value, skipping`);\r\n }\r\n }\r\n return Object.keys(state).length > 0 ? state : undefined;\r\n}\r\n\r\n/**\r\n * Check if a vnode type is a component (has __setup)\r\n */\r\nfunction isComponent(type: any): type is { __setup: Function; __name?: string; __async?: boolean } {\r\n return typeof type === 'function' && '__setup' in type;\r\n}\r\n\r\n/**\r\n * Creates a simple props accessor for SSR (no reactivity needed)\r\n */\r\nfunction createSSRPropsAccessor<TProps extends Record<string, any>>(rawProps: TProps): PropsAccessor<TProps> {\r\n let defaults: Partial<TProps> = {};\r\n\r\n const handler: ProxyHandler<any> = {\r\n get(_, key: string | symbol) {\r\n if (typeof key === 'symbol') return undefined;\r\n const value = (rawProps as any)[key];\r\n return value != null ? value : (defaults as any)[key];\r\n },\r\n apply(_, __, args: [Partial<TProps>]) {\r\n if (args[0] && typeof args[0] === 'object') {\r\n defaults = { ...defaults, ...args[0] };\r\n }\r\n return proxy;\r\n },\r\n has(_, key: string | symbol) {\r\n if (typeof key === 'symbol') return false;\r\n return key in rawProps || key in defaults;\r\n },\r\n ownKeys() {\r\n return [...new Set([...Object.keys(rawProps), ...Object.keys(defaults)])];\r\n },\r\n getOwnPropertyDescriptor(_, key: string | symbol) {\r\n if (typeof key === 'symbol') return undefined;\r\n if (key in rawProps || key in defaults) {\r\n return { enumerable: true, configurable: true, writable: false };\r\n }\r\n return undefined;\r\n }\r\n };\r\n\r\n const proxy = new Proxy(\r\n function propsAccessor() { } as unknown as PropsAccessor<TProps>,\r\n handler\r\n );\r\n return proxy;\r\n}\r\n\r\n/**\r\n * Detect hydration directive from props\r\n */\r\nfunction getHydrationDirective(props: Record<string, any>): { strategy: HydrationStrategy; 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 * Filter out client directives from props\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 * Serialize props for client hydration (must be JSON-safe)\r\n */\r\nfunction serializeProps(props: Record<string, any>): Record<string, any> {\r\n const serialized: Record<string, any> = {};\r\n for (const key in props) {\r\n const value = props[key];\r\n // Skip functions, symbols, and internal props\r\n if (typeof value === 'function' || typeof value === 'symbol') continue;\r\n if (key === 'children' || key === 'key' || key === 'ref' || key === 'slots') continue;\r\n if (key.startsWith('on')) continue; // Skip event handlers\r\n\r\n try {\r\n // Test if serializable\r\n JSON.stringify(value);\r\n serialized[key] = value;\r\n } catch {\r\n // Skip non-serializable values\r\n }\r\n }\r\n return serialized;\r\n}\r\n\r\n/**\r\n * Render element to string chunks (generator for streaming)\r\n */\r\nasync function* renderToChunks(element: JSXElement, ctx: SSRContext): 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);\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 // Check for hydration directive\r\n const hydration = getHydrationDirective(allProps);\r\n const { children, slots: slotsFromProps, ...propsData } = filterClientDirectives(allProps);\r\n\r\n const id = ctx.nextId();\r\n ctx.pushComponent(id);\r\n\r\n // Emit component start marker\r\n yield `<!--$c:${id}-->`;\r\n\r\n // Track signals for islands that may need state serialization\r\n const signalMap = new Map<string, any>();\r\n const shouldTrackState = !!hydration;\r\n\r\n // If this is an island, register it and add island marker\r\n if (hydration) {\r\n const islandInfo: IslandInfo = {\r\n strategy: hydration.strategy,\r\n media: hydration.media,\r\n props: serializeProps(propsData),\r\n componentId: componentName\r\n };\r\n ctx.registerIsland(id, islandInfo);\r\n\r\n // Add island marker with strategy\r\n yield `<!--$island:${hydration.strategy}:${id}${hydration.media ? `:${hydration.media}` : ''}-->`;\r\n }\r\n\r\n // For client:only, don't render component content (placeholder only)\r\n if (hydration?.strategy === 'only') {\r\n yield `<div data-island=\"${id}\"></div>`;\r\n yield `<!--/$c:${id}-->`;\r\n ctx.popComponent();\r\n return;\r\n }\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 // Use tracking signal for async islands, regular signal otherwise\r\n const signalFn = shouldTrackState ? createTrackingSignal(signalMap) : signal;\r\n\r\n // Track SSR loads for this component\r\n const ssrLoads: Promise<void>[] = [];\r\n let ssrLoadResolved = false;\r\n\r\n // Create SSR helper for async data loading\r\n const ssrHelper = {\r\n load(fn: () => Promise<void>): void {\r\n // Queue the async work - will be processed in parallel\r\n ssrLoads.push(fn());\r\n },\r\n isServer: true,\r\n isHydrating: false\r\n };\r\n\r\n const componentCtx: ComponentSetupContext = {\r\n el: null as any,\r\n signal: signalFn as typeof signal,\r\n props: createSSRPropsAccessor(propsData),\r\n slots: slots,\r\n emit: () => { },\r\n parent: null,\r\n onMount: () => { },\r\n onCleanup: () => { },\r\n expose: () => { },\r\n renderFn: null,\r\n update: () => { },\r\n ssr: ssrHelper,\r\n _signals: shouldTrackState ? signalMap : undefined,\r\n _ssrLoads: ssrLoads\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 && hydration) {\r\n // NON-BLOCKING: Don't await! Render placeholder, stream content later\r\n\r\n // Create a promise that will render the component after data loads\r\n const deferredRender = (async () => {\r\n await Promise.all(ssrLoads);\r\n ssrLoadResolved = true;\r\n\r\n // Now render the component with loaded data\r\n let html = '';\r\n if (renderFn) {\r\n const result = (renderFn as () => any)();\r\n if (result) {\r\n // Render to string (no streaming for deferred content)\r\n html = await renderVNodeToString(result, ctx);\r\n }\r\n }\r\n\r\n // Capture signal state for hydration\r\n if (signalMap.size > 0) {\r\n const state = serializeSignalState(signalMap);\r\n if (state) {\r\n const islandInfo = ctx.getIslands().get(id);\r\n if (islandInfo) {\r\n islandInfo.state = state;\r\n }\r\n }\r\n }\r\n\r\n return html;\r\n })();\r\n\r\n // Register for streaming later\r\n const islandInfo = ctx.getIslands().get(id)!;\r\n ctx.addPendingAsync({\r\n id,\r\n promise: deferredRender,\r\n signalMap,\r\n islandInfo\r\n });\r\n\r\n // Render placeholder immediately\r\n yield `<div data-async-placeholder=\"${id}\" style=\"display:contents;\">`;\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);\r\n }\r\n } else {\r\n yield* renderToChunks(result, ctx);\r\n }\r\n }\r\n }\r\n\r\n yield `</div>`;\r\n } else {\r\n // Synchronous component or no ssr.load() calls - await if needed\r\n if (ssrLoads.length > 0) {\r\n await Promise.all(ssrLoads);\r\n }\r\n\r\n // After async loads complete, capture signal state for hydration\r\n if (shouldTrackState && signalMap.size > 0) {\r\n const state = serializeSignalState(signalMap);\r\n if (state) {\r\n // Update the island info with captured state\r\n const islandInfo = ctx.getIslands().get(id);\r\n if (islandInfo) {\r\n islandInfo.state = state;\r\n }\r\n }\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 yield* renderToChunks(item, ctx);\r\n }\r\n } else {\r\n yield* renderToChunks(result, ctx);\r\n }\r\n }\r\n }\r\n }\r\n } catch (e) {\r\n console.error(`Error rendering component ${componentName}:`, e);\r\n } finally {\r\n setCurrentInstance(prev || null);\r\n }\r\n\r\n // Emit component end 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 // Serialize props\r\n for (const key in vnode.props) {\r\n const value = vnode.props[key];\r\n if (key === 'children' || key === 'key' || key === 'ref') continue;\r\n if (key.startsWith('client:')) continue; // Skip client directives\r\n\r\n if (key === 'style') {\r\n const styleString = typeof value === 'object'\r\n ? Object.entries(value).map(([k, v]) => `${camelToKebab(k)}:${v}`).join(';')\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 const voidElements = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];\r\n if (voidElements.includes(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);\r\n prevWasText = isText;\r\n }\r\n\r\n yield `</${tagName}>`;\r\n }\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 * Helper to render a VNode to string (for deferred async content)\r\n */\r\nasync function renderVNodeToString(element: JSXElement, ctx: SSRContext): Promise<string> {\r\n let result = '';\r\n for await (const chunk of renderToChunks(element, ctx)) {\r\n result += chunk;\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Render JSX element to a ReadableStream with streaming async support\r\n */\r\nexport function renderToStream(element: JSXElement, context?: SSRContext): ReadableStream<string> {\r\n const ctx = context || createSSRContext();\r\n\r\n return new ReadableStream<string>({\r\n async start(controller) {\r\n try {\r\n // Phase 1: Render the main page (async components get placeholders)\r\n for await (const chunk of renderToChunks(element, ctx)) {\r\n controller.enqueue(chunk);\r\n }\r\n\r\n // Phase 2: Stream async component replacements as they resolve\r\n const pendingAsync = ctx.getPendingAsync();\r\n if (pendingAsync.length > 0) {\r\n // Inject the streaming replacement script\r\n controller.enqueue(generateStreamingScript());\r\n\r\n // Wait for all pending async components and stream their content\r\n await Promise.all(\r\n pendingAsync.map(async (pending) => {\r\n try {\r\n const html = await pending.promise;\r\n\r\n // Get the updated state after data loaded\r\n const state = serializeSignalState(pending.signalMap);\r\n if (state) {\r\n pending.islandInfo.state = state;\r\n }\r\n\r\n // Stream the replacement\r\n controller.enqueue(generateReplacementScript(\r\n pending.id,\r\n html,\r\n state\r\n ));\r\n } catch (error) {\r\n console.error(`Error streaming async component ${pending.id}:`, error);\r\n // Stream error fallback\r\n controller.enqueue(generateReplacementScript(\r\n pending.id,\r\n `<div style=\"color:red;\">Error loading component</div>`,\r\n undefined\r\n ));\r\n }\r\n })\r\n );\r\n }\r\n\r\n // Phase 3: Append the hydration data script (with final state)\r\n if (ctx.getIslands().size > 0) {\r\n controller.enqueue(generateHydrationScript(ctx));\r\n }\r\n\r\n // Phase 4: Signal that streaming is complete - client can now hydrate\r\n controller.enqueue(`<script>window.__SIGX_STREAMING_COMPLETE__ = true; window.dispatchEvent(new Event('sigx:ready'));</script>`);\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/**\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\r\n/**\r\n * Render with callbacks for fine-grained streaming control.\r\n * This allows the server to inject scripts between shell and async content.\r\n */\r\nexport async function renderToStreamWithCallbacks(\r\n element: JSXElement,\r\n callbacks: StreamCallbacks,\r\n context?: SSRContext\r\n): Promise<void> {\r\n const ctx = context || createSSRContext();\r\n\r\n try {\r\n // Phase 1: Render the shell (sync content + async placeholders)\r\n let shellHtml = '';\r\n for await (const chunk of renderToChunks(element, ctx)) {\r\n shellHtml += chunk;\r\n }\r\n\r\n // Phase 2: Include sync island data immediately so sync components can hydrate\r\n // At this point, only sync component islands are registered (async have placeholders)\r\n const pendingAsync = ctx.getPendingAsync();\r\n const syncIslandIds = new Set<number>();\r\n\r\n // Get islands that are NOT pending async (they're sync)\r\n const pendingAsyncIds = new Set(pendingAsync.map(p => p.id));\r\n ctx.getIslands().forEach((_, id) => {\r\n if (!pendingAsyncIds.has(id)) {\r\n syncIslandIds.add(id);\r\n }\r\n });\r\n\r\n // Generate sync islands data script\r\n if (syncIslandIds.size > 0) {\r\n shellHtml += generateSyncHydrationScript(ctx, syncIslandIds);\r\n }\r\n\r\n // If there are pending async, set up the async islands container\r\n if (pendingAsync.length > 0) {\r\n shellHtml += `<script>window.__SIGX_PENDING_ISLANDS__ = {};</script>`;\r\n }\r\n\r\n // Signal that sync hydration can start\r\n shellHtml += `<script>window.__SIGX_STREAMING_COMPLETE__ = true; window.dispatchEvent(new Event('sigx:ready'));</script>`;\r\n\r\n // Send the shell immediately - this lets scripts start loading and sync components hydrate!\r\n callbacks.onShellReady(shellHtml);\r\n\r\n // Phase 3: Wait for async components and stream replacements\r\n if (pendingAsync.length > 0) {\r\n // Send the streaming replacement script\r\n callbacks.onAsyncChunk(generateStreamingScript());\r\n\r\n // Wait for all pending async components\r\n await Promise.all(\r\n pendingAsync.map(async (pending) => {\r\n try {\r\n const html = await pending.promise;\r\n\r\n const state = serializeSignalState(pending.signalMap);\r\n if (state) {\r\n pending.islandInfo.state = state;\r\n }\r\n\r\n // Include island data with the replacement\r\n callbacks.onAsyncChunk(generateReplacementScriptWithIsland(pending.id, html, pending.islandInfo));\r\n } catch (error) {\r\n console.error(`Error streaming async component ${pending.id}:`, error);\r\n callbacks.onAsyncChunk(generateReplacementScript(\r\n pending.id,\r\n `<div style=\"color:red;\">Error loading component</div>`,\r\n undefined\r\n ));\r\n }\r\n })\r\n );\r\n\r\n // Signal async streaming complete\r\n callbacks.onAsyncChunk(`<script>window.dispatchEvent(new Event('sigx:async-complete'));</script>`);\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/**\r\n * Render JSX element to string (convenience wrapper around stream)\r\n * For renderToString, we wait for all async components to complete,\r\n * then include the replacement scripts inline so the final HTML is complete.\r\n */\r\nexport async function renderToString(element: JSXElement, context?: SSRContext): Promise<string> {\r\n const ctx = context || createSSRContext();\r\n let result = '';\r\n\r\n // Phase 1: Render main content (async components get placeholders)\r\n for await (const chunk of renderToChunks(element, ctx)) {\r\n result += chunk;\r\n }\r\n\r\n // Phase 2: Wait for pending async components and add replacement scripts\r\n const pendingAsync = ctx.getPendingAsync();\r\n if (pendingAsync.length > 0) {\r\n // Add the streaming replacement script\r\n result += generateStreamingScript();\r\n\r\n // Wait for all pending async components\r\n await Promise.all(\r\n pendingAsync.map(async (pending) => {\r\n try {\r\n const html = await pending.promise;\r\n\r\n // Get the updated state after data loaded\r\n const state = serializeSignalState(pending.signalMap);\r\n if (state) {\r\n pending.islandInfo.state = state;\r\n }\r\n\r\n // Add the replacement script\r\n result += generateReplacementScript(pending.id, html, state);\r\n } catch (error) {\r\n console.error(`Error rendering async component ${pending.id}:`, error);\r\n result += generateReplacementScript(\r\n pending.id,\r\n `<div style=\"color:red;\">Error loading component</div>`,\r\n undefined\r\n );\r\n }\r\n })\r\n );\r\n }\r\n\r\n // Phase 3: Append hydration script with final state\r\n if (ctx.getIslands().size > 0) {\r\n result += generateHydrationScript(ctx);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Generate the streaming replacement script (injected once before any replacements)\r\n * This script provides the $SIGX_REPLACE function used by replacement chunks\r\n */\r\nfunction generateStreamingScript(): string {\r\n return `\r\n<script>\r\nwindow.$SIGX_REPLACE = function(id, html, state) {\r\n var placeholder = document.querySelector('[data-async-placeholder=\"' + id + '\"]');\r\n if (placeholder) {\r\n // Create a template to parse the HTML\r\n var template = document.createElement('template');\r\n template.innerHTML = html;\r\n \r\n // Replace placeholder content\r\n placeholder.innerHTML = '';\r\n while (template.content.firstChild) {\r\n placeholder.appendChild(template.content.firstChild);\r\n }\r\n \r\n // Update island state in the hydration data\r\n if (state) {\r\n var dataScript = document.getElementById('__SIGX_ISLANDS__');\r\n if (dataScript) {\r\n try {\r\n var data = JSON.parse(dataScript.textContent || '{}');\r\n if (data[id]) {\r\n data[id].state = state;\r\n dataScript.textContent = JSON.stringify(data);\r\n }\r\n } catch(e) {}\r\n }\r\n }\r\n \r\n // Dispatch event for hydration to pick up\r\n placeholder.dispatchEvent(new CustomEvent('sigx:async-ready', { bubbles: true, detail: { id: id, state: state } }));\r\n }\r\n};\r\n</script>`;\r\n}\r\n\r\n/**\r\n * Generate a replacement script for a specific async component\r\n */\r\nfunction generateReplacementScript(id: number, html: string, state?: Record<string, any>): string {\r\n // Escape the HTML for embedding in a script\r\n const escapedHtml = JSON.stringify(html);\r\n const stateJson = state ? JSON.stringify(state) : 'null';\r\n\r\n return `<script>$SIGX_REPLACE(${id}, ${escapedHtml}, ${stateJson});</script>`;\r\n}\r\n\r\n/**\r\n * Generate a replacement script that also includes island data for async component\r\n */\r\nfunction generateReplacementScriptWithIsland(id: number, html: string, islandInfo: IslandInfo): string {\r\n const escapedHtml = JSON.stringify(html);\r\n const islandJson = JSON.stringify(islandInfo);\r\n\r\n return `<script>\r\n(function() {\r\n // Add island data to the existing hydration data\r\n var dataScript = document.getElementById('__SIGX_ISLANDS__');\r\n if (dataScript) {\r\n try {\r\n var data = JSON.parse(dataScript.textContent || '{}');\r\n data[${id}] = ${islandJson};\r\n dataScript.textContent = JSON.stringify(data);\r\n } catch(e) { console.error('Failed to update island data:', e); }\r\n }\r\n // Replace the placeholder content\r\n $SIGX_REPLACE(${id}, ${escapedHtml}, ${islandInfo.state ? JSON.stringify(islandInfo.state) : 'null'});\r\n})();\r\n</script>`;\r\n}\r\n\r\n/**\r\n * Generate hydration script for sync islands only\r\n */\r\nfunction generateSyncHydrationScript(ctx: SSRContext, syncIslandIds: Set<number>): string {\r\n const islands = ctx.getIslands();\r\n const islandData: Record<number, IslandInfo> = {};\r\n\r\n islands.forEach((info, id) => {\r\n if (syncIslandIds.has(id)) {\r\n islandData[id] = info;\r\n }\r\n });\r\n\r\n if (Object.keys(islandData).length === 0) return '';\r\n\r\n return `\r\n<script type=\"application/json\" id=\"__SIGX_ISLANDS__\">${JSON.stringify(islandData)}</script>`;\r\n}\r\n\r\n/**\r\n * Generate the hydration bootstrap script\r\n */\r\nfunction generateHydrationScript(ctx: SSRContext): string {\r\n const islands = ctx.getIslands();\r\n if (islands.size === 0) return '';\r\n\r\n const islandData: Record<number, IslandInfo> = {};\r\n islands.forEach((info, id) => {\r\n islandData[id] = info;\r\n });\r\n\r\n // Only output the JSON data - the client entry is responsible for calling hydrateIslands()\r\n // We don't inject a script tag because the browser can't resolve npm package specifiers\r\n // The client bundle (via Vite/Rollup) handles the import resolution\r\n return `\r\n<script type=\"application/json\" id=\"__SIGX_ISLANDS__\">${JSON.stringify(islandData)}</script>`;\r\n}\r\n\r\n// HTML escaping\r\nconst ESCAPE: Record<string, string> = {\r\n '&': '&amp;',\r\n '<': '&lt;',\r\n '>': '&gt;',\r\n '\"': '&quot;',\r\n \"'\": '&#39;'\r\n};\r\n\r\nfunction escapeHtml(s: string): string {\r\n return s.replace(/[&<>\"']/g, c => ESCAPE[c]);\r\n}\r\n\r\nfunction camelToKebab(str: string): string {\r\n return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\r\n}\r\n","/**\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 +0,0 @@
1
- {"version":3,"file":"index.js","names":["componentStack: number[]","head: string[]","pendingAsync: PendingAsyncComponent[]","state: Record<string, any>","defaults: Partial<TProps>","filtered: Record<string, any>","serialized: Record<string, any>","islandInfo: IslandInfo","slots: SlotsObject<any>","ssrLoads: Promise<void>[]","componentCtx: ComponentSetupContext","islandInfo","islandData: Record<number, IslandInfo>","ESCAPE: Record<string, string>"],"sources":["../../src/server/context.ts","../../src/server/stream.ts"],"sourcesContent":["/**\r\n * SSR Context - tracks component boundaries and hydration markers during rendering\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\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 * Registered islands and their hydration strategies\r\n */\r\n _islands: Map<number, IslandInfo>;\r\n\r\n /**\r\n * Collected head elements (scripts, styles, etc.)\r\n */\r\n _head: string[];\r\n\r\n /**\r\n * Pending async components for streaming\r\n */\r\n _pendingAsync: PendingAsyncComponent[];\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 * Register an island for selective hydration\r\n */\r\n registerIsland(id: number, info: IslandInfo): void;\r\n\r\n /**\r\n * Get all registered islands\r\n */\r\n getIslands(): Map<number, IslandInfo>;\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 * Register a pending async component for streaming\r\n */\r\n addPendingAsync(pending: PendingAsyncComponent): void;\r\n\r\n /**\r\n * Get all pending async components\r\n */\r\n getPendingAsync(): PendingAsyncComponent[];\r\n}\r\n\r\nexport interface IslandInfo {\r\n /**\r\n * Hydration strategy: 'load' | 'idle' | 'visible' | 'media' | 'only'\r\n */\r\n strategy: HydrationStrategy;\r\n\r\n /**\r\n * Media query for 'media' strategy\r\n */\r\n media?: string;\r\n\r\n /**\r\n * Component props to serialize for client hydration\r\n */\r\n props?: Record<string, any>;\r\n\r\n /**\r\n * Component name/identifier for client\r\n */\r\n componentId?: string;\r\n\r\n /**\r\n * Captured signal state from async setup for client hydration\r\n */\r\n state?: Record<string, any>;\r\n\r\n /**\r\n * Placeholder HTML for streaming async components\r\n */\r\n placeholder?: string;\r\n}\r\n\r\n/**\r\n * Pending async component that will be streamed later\r\n */\r\nexport interface PendingAsyncComponent {\r\n /** Component ID */\r\n id: number;\r\n /** Promise that resolves to rendered HTML */\r\n promise: Promise<string>;\r\n /** Signal state captured during render */\r\n signalMap: Map<string, any>;\r\n /** Island info reference */\r\n islandInfo: IslandInfo;\r\n}\r\n\r\nexport type HydrationStrategy = 'load' | 'idle' | 'visible' | 'media' | 'only';\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 islands = new Map<number, IslandInfo>();\r\n const head: string[] = [];\r\n const pendingAsync: PendingAsyncComponent[] = [];\r\n\r\n return {\r\n _componentId: componentId,\r\n _componentStack: componentStack,\r\n _islands: islands,\r\n _head: head,\r\n _pendingAsync: 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 registerIsland(id: number, info: IslandInfo) {\r\n islands.set(id, info);\r\n },\r\n\r\n getIslands() {\r\n return islands;\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 addPendingAsync(pending: PendingAsyncComponent) {\r\n pendingAsync.push(pending);\r\n },\r\n\r\n getPendingAsync() {\r\n return pendingAsync;\r\n }\r\n };\r\n}\r\n","/**\r\n * Streaming SSR renderer with hydration markers\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 PropsAccessor\r\n} from '@sigx/runtime-core';\r\nimport { SSRContext, createSSRContext, IslandInfo, HydrationStrategy, PendingAsyncComponent } from './context.js';\r\n\r\n// Client directive props\r\nconst CLIENT_DIRECTIVES = ['client:load', 'client:idle', 'client:visible', 'client:media', 'client:only'] as const;\r\ntype ClientDirective = typeof CLIENT_DIRECTIVES[number];\r\n\r\n/**\r\n * Creates a tracking signal function that records signal names and values.\r\n * Used during async setup to capture state for client hydration.\r\n */\r\nfunction createTrackingSignal(signalMap: Map<string, any>) {\r\n let signalIndex = 0;\r\n\r\n return function trackingSignal<T extends object>(initial: T, name?: string): ReturnType<typeof signal<T>> {\r\n // Generate a stable key for this signal\r\n const key = name ?? `$${signalIndex++}`;\r\n\r\n // Create the real signal\r\n const sig = signal(initial);\r\n\r\n // Capture initial value\r\n signalMap.set(key, initial);\r\n\r\n // Create a proxy that tracks writes\r\n const proxy = new Proxy(sig, {\r\n get(target, prop) {\r\n if (prop === 'value') {\r\n // Return current value from the real signal\r\n return (target as any).value;\r\n }\r\n return (target as any)[prop];\r\n },\r\n set(target, prop, newValue) {\r\n if (prop === 'value') {\r\n // Update the signal and track the new value\r\n (target as any).value = newValue;\r\n signalMap.set(key, newValue);\r\n return true;\r\n }\r\n (target as any)[prop] = newValue;\r\n return true;\r\n }\r\n });\r\n\r\n return proxy as ReturnType<typeof signal<T>>;\r\n };\r\n}\r\n\r\n/**\r\n * Serialize captured signal state for client hydration\r\n */\r\nfunction serializeSignalState(signalMap: Map<string, any>): Record<string, any> | undefined {\r\n if (signalMap.size === 0) return undefined;\r\n\r\n const state: Record<string, any> = {};\r\n for (const [key, value] of signalMap) {\r\n try {\r\n // Test if serializable\r\n JSON.stringify(value);\r\n state[key] = value;\r\n } catch {\r\n // Skip non-serializable values\r\n console.warn(`SSR: Signal \"${key}\" has non-serializable value, skipping`);\r\n }\r\n }\r\n return Object.keys(state).length > 0 ? state : undefined;\r\n}\r\n\r\n/**\r\n * Check if a vnode type is a component (has __setup)\r\n */\r\nfunction isComponent(type: any): type is { __setup: Function; __name?: string; __async?: boolean } {\r\n return typeof type === 'function' && '__setup' in type;\r\n}\r\n\r\n/**\r\n * Creates a simple props accessor for SSR (no reactivity needed)\r\n */\r\nfunction createSSRPropsAccessor<TProps extends Record<string, any>>(rawProps: TProps): PropsAccessor<TProps> {\r\n let defaults: Partial<TProps> = {};\r\n\r\n const handler: ProxyHandler<any> = {\r\n get(_, key: string | symbol) {\r\n if (typeof key === 'symbol') return undefined;\r\n const value = (rawProps as any)[key];\r\n return value != null ? value : (defaults as any)[key];\r\n },\r\n apply(_, __, args: [Partial<TProps>]) {\r\n if (args[0] && typeof args[0] === 'object') {\r\n defaults = { ...defaults, ...args[0] };\r\n }\r\n return proxy;\r\n },\r\n has(_, key: string | symbol) {\r\n if (typeof key === 'symbol') return false;\r\n return key in rawProps || key in defaults;\r\n },\r\n ownKeys() {\r\n return [...new Set([...Object.keys(rawProps), ...Object.keys(defaults)])];\r\n },\r\n getOwnPropertyDescriptor(_, key: string | symbol) {\r\n if (typeof key === 'symbol') return undefined;\r\n if (key in rawProps || key in defaults) {\r\n return { enumerable: true, configurable: true, writable: false };\r\n }\r\n return undefined;\r\n }\r\n };\r\n\r\n const proxy = new Proxy(\r\n function propsAccessor() { } as unknown as PropsAccessor<TProps>,\r\n handler\r\n );\r\n return proxy;\r\n}\r\n\r\n/**\r\n * Detect hydration directive from props\r\n */\r\nfunction getHydrationDirective(props: Record<string, any>): { strategy: HydrationStrategy; 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 * Filter out client directives from props\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 * Serialize props for client hydration (must be JSON-safe)\r\n */\r\nfunction serializeProps(props: Record<string, any>): Record<string, any> {\r\n const serialized: Record<string, any> = {};\r\n for (const key in props) {\r\n const value = props[key];\r\n // Skip functions, symbols, and internal props\r\n if (typeof value === 'function' || typeof value === 'symbol') continue;\r\n if (key === 'children' || key === 'key' || key === 'ref' || key === 'slots') continue;\r\n if (key.startsWith('on')) continue; // Skip event handlers\r\n\r\n try {\r\n // Test if serializable\r\n JSON.stringify(value);\r\n serialized[key] = value;\r\n } catch {\r\n // Skip non-serializable values\r\n }\r\n }\r\n return serialized;\r\n}\r\n\r\n/**\r\n * Render element to string chunks (generator for streaming)\r\n */\r\nasync function* renderToChunks(element: JSXElement, ctx: SSRContext): 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);\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 // Check for hydration directive\r\n const hydration = getHydrationDirective(allProps);\r\n const { children, slots: slotsFromProps, ...propsData } = filterClientDirectives(allProps);\r\n\r\n const id = ctx.nextId();\r\n ctx.pushComponent(id);\r\n\r\n // Emit component start marker\r\n yield `<!--$c:${id}-->`;\r\n\r\n // Track signals for islands that may need state serialization\r\n const signalMap = new Map<string, any>();\r\n const shouldTrackState = !!hydration;\r\n\r\n // If this is an island, register it and add island marker\r\n if (hydration) {\r\n const islandInfo: IslandInfo = {\r\n strategy: hydration.strategy,\r\n media: hydration.media,\r\n props: serializeProps(propsData),\r\n componentId: componentName\r\n };\r\n ctx.registerIsland(id, islandInfo);\r\n\r\n // Add island marker with strategy\r\n yield `<!--$island:${hydration.strategy}:${id}${hydration.media ? `:${hydration.media}` : ''}-->`;\r\n }\r\n\r\n // For client:only, don't render component content (placeholder only)\r\n if (hydration?.strategy === 'only') {\r\n yield `<div data-island=\"${id}\"></div>`;\r\n yield `<!--/$c:${id}-->`;\r\n ctx.popComponent();\r\n return;\r\n }\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 // Use tracking signal for async islands, regular signal otherwise\r\n const signalFn = shouldTrackState ? createTrackingSignal(signalMap) : signal;\r\n\r\n // Track SSR loads for this component\r\n const ssrLoads: Promise<void>[] = [];\r\n let ssrLoadResolved = false;\r\n\r\n // Create SSR helper for async data loading\r\n const ssrHelper = {\r\n load(fn: () => Promise<void>): void {\r\n // Queue the async work - will be processed in parallel\r\n ssrLoads.push(fn());\r\n },\r\n isServer: true,\r\n isHydrating: false\r\n };\r\n\r\n const componentCtx: ComponentSetupContext = {\r\n el: null as any,\r\n signal: signalFn as typeof signal,\r\n props: createSSRPropsAccessor(propsData),\r\n slots: slots,\r\n emit: () => { },\r\n parent: null,\r\n onMount: () => { },\r\n onCleanup: () => { },\r\n expose: () => { },\r\n renderFn: null,\r\n update: () => { },\r\n ssr: ssrHelper,\r\n _signals: shouldTrackState ? signalMap : undefined,\r\n _ssrLoads: ssrLoads\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 && hydration) {\r\n // NON-BLOCKING: Don't await! Render placeholder, stream content later\r\n\r\n // Create a promise that will render the component after data loads\r\n const deferredRender = (async () => {\r\n await Promise.all(ssrLoads);\r\n ssrLoadResolved = true;\r\n\r\n // Now render the component with loaded data\r\n let html = '';\r\n if (renderFn) {\r\n const result = (renderFn as () => any)();\r\n if (result) {\r\n // Render to string (no streaming for deferred content)\r\n html = await renderVNodeToString(result, ctx);\r\n }\r\n }\r\n\r\n // Capture signal state for hydration\r\n if (signalMap.size > 0) {\r\n const state = serializeSignalState(signalMap);\r\n if (state) {\r\n const islandInfo = ctx.getIslands().get(id);\r\n if (islandInfo) {\r\n islandInfo.state = state;\r\n }\r\n }\r\n }\r\n\r\n return html;\r\n })();\r\n\r\n // Register for streaming later\r\n const islandInfo = ctx.getIslands().get(id)!;\r\n ctx.addPendingAsync({\r\n id,\r\n promise: deferredRender,\r\n signalMap,\r\n islandInfo\r\n });\r\n\r\n // Render placeholder immediately\r\n yield `<div data-async-placeholder=\"${id}\" style=\"display:contents;\">`;\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);\r\n }\r\n } else {\r\n yield* renderToChunks(result, ctx);\r\n }\r\n }\r\n }\r\n\r\n yield `</div>`;\r\n } else {\r\n // Synchronous component or no ssr.load() calls - await if needed\r\n if (ssrLoads.length > 0) {\r\n await Promise.all(ssrLoads);\r\n }\r\n\r\n // After async loads complete, capture signal state for hydration\r\n if (shouldTrackState && signalMap.size > 0) {\r\n const state = serializeSignalState(signalMap);\r\n if (state) {\r\n // Update the island info with captured state\r\n const islandInfo = ctx.getIslands().get(id);\r\n if (islandInfo) {\r\n islandInfo.state = state;\r\n }\r\n }\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 yield* renderToChunks(item, ctx);\r\n }\r\n } else {\r\n yield* renderToChunks(result, ctx);\r\n }\r\n }\r\n }\r\n }\r\n } catch (e) {\r\n console.error(`Error rendering component ${componentName}:`, e);\r\n } finally {\r\n setCurrentInstance(prev || null);\r\n }\r\n\r\n // Emit component end 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 // Serialize props\r\n for (const key in vnode.props) {\r\n const value = vnode.props[key];\r\n if (key === 'children' || key === 'key' || key === 'ref') continue;\r\n if (key.startsWith('client:')) continue; // Skip client directives\r\n\r\n if (key === 'style') {\r\n const styleString = typeof value === 'object'\r\n ? Object.entries(value).map(([k, v]) => `${camelToKebab(k)}:${v}`).join(';')\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 const voidElements = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];\r\n if (voidElements.includes(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);\r\n prevWasText = isText;\r\n }\r\n\r\n yield `</${tagName}>`;\r\n }\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 * Helper to render a VNode to string (for deferred async content)\r\n */\r\nasync function renderVNodeToString(element: JSXElement, ctx: SSRContext): Promise<string> {\r\n let result = '';\r\n for await (const chunk of renderToChunks(element, ctx)) {\r\n result += chunk;\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Render JSX element to a ReadableStream with streaming async support\r\n */\r\nexport function renderToStream(element: JSXElement, context?: SSRContext): ReadableStream<string> {\r\n const ctx = context || createSSRContext();\r\n\r\n return new ReadableStream<string>({\r\n async start(controller) {\r\n try {\r\n // Phase 1: Render the main page (async components get placeholders)\r\n for await (const chunk of renderToChunks(element, ctx)) {\r\n controller.enqueue(chunk);\r\n }\r\n\r\n // Phase 2: Stream async component replacements as they resolve\r\n const pendingAsync = ctx.getPendingAsync();\r\n if (pendingAsync.length > 0) {\r\n // Inject the streaming replacement script\r\n controller.enqueue(generateStreamingScript());\r\n\r\n // Wait for all pending async components and stream their content\r\n await Promise.all(\r\n pendingAsync.map(async (pending) => {\r\n try {\r\n const html = await pending.promise;\r\n\r\n // Get the updated state after data loaded\r\n const state = serializeSignalState(pending.signalMap);\r\n if (state) {\r\n pending.islandInfo.state = state;\r\n }\r\n\r\n // Stream the replacement\r\n controller.enqueue(generateReplacementScript(\r\n pending.id,\r\n html,\r\n state\r\n ));\r\n } catch (error) {\r\n console.error(`Error streaming async component ${pending.id}:`, error);\r\n // Stream error fallback\r\n controller.enqueue(generateReplacementScript(\r\n pending.id,\r\n `<div style=\"color:red;\">Error loading component</div>`,\r\n undefined\r\n ));\r\n }\r\n })\r\n );\r\n }\r\n\r\n // Phase 3: Append the hydration data script (with final state)\r\n if (ctx.getIslands().size > 0) {\r\n controller.enqueue(generateHydrationScript(ctx));\r\n }\r\n\r\n // Phase 4: Signal that streaming is complete - client can now hydrate\r\n controller.enqueue(`<script>window.__SIGX_STREAMING_COMPLETE__ = true; window.dispatchEvent(new Event('sigx:ready'));</script>`);\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/**\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\r\n/**\r\n * Render with callbacks for fine-grained streaming control.\r\n * This allows the server to inject scripts between shell and async content.\r\n */\r\nexport async function renderToStreamWithCallbacks(\r\n element: JSXElement,\r\n callbacks: StreamCallbacks,\r\n context?: SSRContext\r\n): Promise<void> {\r\n const ctx = context || createSSRContext();\r\n\r\n try {\r\n // Phase 1: Render the shell (sync content + async placeholders)\r\n let shellHtml = '';\r\n for await (const chunk of renderToChunks(element, ctx)) {\r\n shellHtml += chunk;\r\n }\r\n\r\n // Phase 2: Include sync island data immediately so sync components can hydrate\r\n // At this point, only sync component islands are registered (async have placeholders)\r\n const pendingAsync = ctx.getPendingAsync();\r\n const syncIslandIds = new Set<number>();\r\n\r\n // Get islands that are NOT pending async (they're sync)\r\n const pendingAsyncIds = new Set(pendingAsync.map(p => p.id));\r\n ctx.getIslands().forEach((_, id) => {\r\n if (!pendingAsyncIds.has(id)) {\r\n syncIslandIds.add(id);\r\n }\r\n });\r\n\r\n // Generate sync islands data script\r\n if (syncIslandIds.size > 0) {\r\n shellHtml += generateSyncHydrationScript(ctx, syncIslandIds);\r\n }\r\n\r\n // If there are pending async, set up the async islands container\r\n if (pendingAsync.length > 0) {\r\n shellHtml += `<script>window.__SIGX_PENDING_ISLANDS__ = {};</script>`;\r\n }\r\n\r\n // Signal that sync hydration can start\r\n shellHtml += `<script>window.__SIGX_STREAMING_COMPLETE__ = true; window.dispatchEvent(new Event('sigx:ready'));</script>`;\r\n\r\n // Send the shell immediately - this lets scripts start loading and sync components hydrate!\r\n callbacks.onShellReady(shellHtml);\r\n\r\n // Phase 3: Wait for async components and stream replacements\r\n if (pendingAsync.length > 0) {\r\n // Send the streaming replacement script\r\n callbacks.onAsyncChunk(generateStreamingScript());\r\n\r\n // Wait for all pending async components\r\n await Promise.all(\r\n pendingAsync.map(async (pending) => {\r\n try {\r\n const html = await pending.promise;\r\n\r\n const state = serializeSignalState(pending.signalMap);\r\n if (state) {\r\n pending.islandInfo.state = state;\r\n }\r\n\r\n // Include island data with the replacement\r\n callbacks.onAsyncChunk(generateReplacementScriptWithIsland(pending.id, html, pending.islandInfo));\r\n } catch (error) {\r\n console.error(`Error streaming async component ${pending.id}:`, error);\r\n callbacks.onAsyncChunk(generateReplacementScript(\r\n pending.id,\r\n `<div style=\"color:red;\">Error loading component</div>`,\r\n undefined\r\n ));\r\n }\r\n })\r\n );\r\n\r\n // Signal async streaming complete\r\n callbacks.onAsyncChunk(`<script>window.dispatchEvent(new Event('sigx:async-complete'));</script>`);\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/**\r\n * Render JSX element to string (convenience wrapper around stream)\r\n * For renderToString, we wait for all async components to complete,\r\n * then include the replacement scripts inline so the final HTML is complete.\r\n */\r\nexport async function renderToString(element: JSXElement, context?: SSRContext): Promise<string> {\r\n const ctx = context || createSSRContext();\r\n let result = '';\r\n\r\n // Phase 1: Render main content (async components get placeholders)\r\n for await (const chunk of renderToChunks(element, ctx)) {\r\n result += chunk;\r\n }\r\n\r\n // Phase 2: Wait for pending async components and add replacement scripts\r\n const pendingAsync = ctx.getPendingAsync();\r\n if (pendingAsync.length > 0) {\r\n // Add the streaming replacement script\r\n result += generateStreamingScript();\r\n\r\n // Wait for all pending async components\r\n await Promise.all(\r\n pendingAsync.map(async (pending) => {\r\n try {\r\n const html = await pending.promise;\r\n\r\n // Get the updated state after data loaded\r\n const state = serializeSignalState(pending.signalMap);\r\n if (state) {\r\n pending.islandInfo.state = state;\r\n }\r\n\r\n // Add the replacement script\r\n result += generateReplacementScript(pending.id, html, state);\r\n } catch (error) {\r\n console.error(`Error rendering async component ${pending.id}:`, error);\r\n result += generateReplacementScript(\r\n pending.id,\r\n `<div style=\"color:red;\">Error loading component</div>`,\r\n undefined\r\n );\r\n }\r\n })\r\n );\r\n }\r\n\r\n // Phase 3: Append hydration script with final state\r\n if (ctx.getIslands().size > 0) {\r\n result += generateHydrationScript(ctx);\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Generate the streaming replacement script (injected once before any replacements)\r\n * This script provides the $SIGX_REPLACE function used by replacement chunks\r\n */\r\nfunction generateStreamingScript(): string {\r\n return `\r\n<script>\r\nwindow.$SIGX_REPLACE = function(id, html, state) {\r\n var placeholder = document.querySelector('[data-async-placeholder=\"' + id + '\"]');\r\n if (placeholder) {\r\n // Create a template to parse the HTML\r\n var template = document.createElement('template');\r\n template.innerHTML = html;\r\n \r\n // Replace placeholder content\r\n placeholder.innerHTML = '';\r\n while (template.content.firstChild) {\r\n placeholder.appendChild(template.content.firstChild);\r\n }\r\n \r\n // Update island state in the hydration data\r\n if (state) {\r\n var dataScript = document.getElementById('__SIGX_ISLANDS__');\r\n if (dataScript) {\r\n try {\r\n var data = JSON.parse(dataScript.textContent || '{}');\r\n if (data[id]) {\r\n data[id].state = state;\r\n dataScript.textContent = JSON.stringify(data);\r\n }\r\n } catch(e) {}\r\n }\r\n }\r\n \r\n // Dispatch event for hydration to pick up\r\n placeholder.dispatchEvent(new CustomEvent('sigx:async-ready', { bubbles: true, detail: { id: id, state: state } }));\r\n }\r\n};\r\n</script>`;\r\n}\r\n\r\n/**\r\n * Generate a replacement script for a specific async component\r\n */\r\nfunction generateReplacementScript(id: number, html: string, state?: Record<string, any>): string {\r\n // Escape the HTML for embedding in a script\r\n const escapedHtml = JSON.stringify(html);\r\n const stateJson = state ? JSON.stringify(state) : 'null';\r\n\r\n return `<script>$SIGX_REPLACE(${id}, ${escapedHtml}, ${stateJson});</script>`;\r\n}\r\n\r\n/**\r\n * Generate a replacement script that also includes island data for async component\r\n */\r\nfunction generateReplacementScriptWithIsland(id: number, html: string, islandInfo: IslandInfo): string {\r\n const escapedHtml = JSON.stringify(html);\r\n const islandJson = JSON.stringify(islandInfo);\r\n\r\n return `<script>\r\n(function() {\r\n // Add island data to the existing hydration data\r\n var dataScript = document.getElementById('__SIGX_ISLANDS__');\r\n if (dataScript) {\r\n try {\r\n var data = JSON.parse(dataScript.textContent || '{}');\r\n data[${id}] = ${islandJson};\r\n dataScript.textContent = JSON.stringify(data);\r\n } catch(e) { console.error('Failed to update island data:', e); }\r\n }\r\n // Replace the placeholder content\r\n $SIGX_REPLACE(${id}, ${escapedHtml}, ${islandInfo.state ? JSON.stringify(islandInfo.state) : 'null'});\r\n})();\r\n</script>`;\r\n}\r\n\r\n/**\r\n * Generate hydration script for sync islands only\r\n */\r\nfunction generateSyncHydrationScript(ctx: SSRContext, syncIslandIds: Set<number>): string {\r\n const islands = ctx.getIslands();\r\n const islandData: Record<number, IslandInfo> = {};\r\n\r\n islands.forEach((info, id) => {\r\n if (syncIslandIds.has(id)) {\r\n islandData[id] = info;\r\n }\r\n });\r\n\r\n if (Object.keys(islandData).length === 0) return '';\r\n\r\n return `\r\n<script type=\"application/json\" id=\"__SIGX_ISLANDS__\">${JSON.stringify(islandData)}</script>`;\r\n}\r\n\r\n/**\r\n * Generate the hydration bootstrap script\r\n */\r\nfunction generateHydrationScript(ctx: SSRContext): string {\r\n const islands = ctx.getIslands();\r\n if (islands.size === 0) return '';\r\n\r\n const islandData: Record<number, IslandInfo> = {};\r\n islands.forEach((info, id) => {\r\n islandData[id] = info;\r\n });\r\n\r\n // Only output the JSON data - the client entry is responsible for calling hydrateIslands()\r\n // We don't inject a script tag because the browser can't resolve npm package specifiers\r\n // The client bundle (via Vite/Rollup) handles the import resolution\r\n return `\r\n<script type=\"application/json\" id=\"__SIGX_ISLANDS__\">${JSON.stringify(islandData)}</script>`;\r\n}\r\n\r\n// HTML escaping\r\nconst ESCAPE: Record<string, string> = {\r\n '&': '&amp;',\r\n '<': '&lt;',\r\n '>': '&gt;',\r\n '\"': '&quot;',\r\n \"'\": '&#39;'\r\n};\r\n\r\nfunction escapeHtml(s: string): string {\r\n return s.replace(/[&<>\"']/g, c => ESCAPE[c]);\r\n}\r\n\r\nfunction camelToKebab(str: string): string {\r\n return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();\r\n}\r\n"],"mappings":"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"}