@sigx/server-renderer 0.1.26 → 0.2.1

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 +1 @@
1
- {"version":3,"file":"hydrate-core.d.ts","sourceRoot":"","sources":["../../src/client/hydrate-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACH,KAAK,EAIR,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAIvC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CA6BvF;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAgIrF"}
1
+ {"version":3,"file":"hydrate-core.d.ts","sourceRoot":"","sources":["../../src/client/hydrate-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACH,KAAK,EAKR,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAIvC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CA6BvF;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAsJrF"}
@@ -1,2 +1,2 @@
1
- import { a as clearClientPlugins, c as getCurrentAppContext, d as setPendingServerState, i as hydrateComponent, l as registerClientPlugin, n as hydrate, o as createRestoringSignal, r as hydrateNode, s as getClientPlugins, t as ssrClientPlugin, u as setCurrentAppContext } from "../client-DVLUaAq6.js";
1
+ import { a as clearClientPlugins, c as getCurrentAppContext, d as setPendingServerState, i as hydrateComponent, l as registerClientPlugin, n as hydrate, o as createRestoringSignal, r as hydrateNode, s as getClientPlugins, t as ssrClientPlugin, u as setCurrentAppContext } from "../client-CL2CTcTJ.js";
2
2
  export { clearClientPlugins, createRestoringSignal, getClientPlugins, getCurrentAppContext, hydrate, hydrateComponent, hydrateNode, registerClientPlugin, setCurrentAppContext, setPendingServerState, ssrClientPlugin };
@@ -1,26 +1,58 @@
1
- import { n as internals_exports, t as generateSignalKey } from "./types-CkI_lB93.js";
2
- import { Fragment, Text, effect, getCurrentInstance, isComponent, isModel, render, signal } from "sigx";
1
+ import { n as internals_exports, t as generateSignalKey } from "./types-DYlI_C8F.js";
2
+ import { Comment, Fragment, Text, effect, getCurrentInstance, isComponent, isModel, render, signal } from "sigx";
3
+ //#region src/client/hydrate-context.ts
4
+ /**
5
+ * Hydration context and state management
6
+ *
7
+ * Manages server state restoration, app context tracking,
8
+ * client plugin registration, and the SSR context extension for components.
9
+ *
10
+ * Strategy-specific concerns (island data, async hydration) are handled
11
+ * by plugins registered via `registerClientPlugin()`.
12
+ */
3
13
  var _pendingServerState = null;
4
14
  var _currentAppContext = null;
5
15
  var _clientPlugins = [];
16
+ /**
17
+ * Register a client-side SSR plugin.
18
+ * Plugins are called during hydration to intercept component processing,
19
+ * skip default hydration walk, or run post-hydration logic.
20
+ */
6
21
  function registerClientPlugin(plugin) {
7
22
  _clientPlugins.push(plugin);
8
23
  }
24
+ /**
25
+ * Get all registered client-side plugins.
26
+ */
9
27
  function getClientPlugins() {
10
28
  return _clientPlugins;
11
29
  }
30
+ /**
31
+ * Clear all registered client plugins (useful for testing).
32
+ */
12
33
  function clearClientPlugins() {
13
34
  _clientPlugins = [];
14
35
  }
36
+ /**
37
+ * Set server state that should be used for the next component mount.
38
+ * Used internally when mounting async components after streaming.
39
+ */
15
40
  function setPendingServerState(state) {
16
41
  _pendingServerState = state;
17
42
  }
43
+ /** Get the current app context for deferred hydration */
18
44
  function getCurrentAppContext() {
19
45
  return _currentAppContext;
20
46
  }
47
+ /** Set the current app context during hydration */
21
48
  function setCurrentAppContext(ctx) {
22
49
  _currentAppContext = ctx;
23
50
  }
51
+ /**
52
+ * Creates a signal function that restores state from server-captured values.
53
+ * Used during hydration of async components to avoid re-fetching data.
54
+ * Supports both primitive and object signals.
55
+ */
24
56
  function createRestoringSignal(serverState) {
25
57
  let signalIndex = 0;
26
58
  let hasWarnedPositional = false;
@@ -34,6 +66,9 @@ function createRestoringSignal(serverState) {
34
66
  return signal(initial);
35
67
  };
36
68
  }
69
+ /**
70
+ * Normalize any element to VNode
71
+ */
37
72
  function normalizeElement(element) {
38
73
  if (element == null || element === true || element === false) return null;
39
74
  if (typeof element === "string" || typeof element === "number") return {
@@ -46,6 +81,11 @@ function normalizeElement(element) {
46
81
  };
47
82
  return element;
48
83
  }
84
+ /**
85
+ * Register the SSR context extension for all components.
86
+ * This provides the `ssr` object with a no-op `load()` for client-side rendering.
87
+ * Also handles server state restoration for async streamed components.
88
+ */
49
89
  (0, internals_exports.registerContextExtension)((ctx) => {
50
90
  const serverState = _pendingServerState;
51
91
  if (serverState) {
@@ -70,6 +110,28 @@ function normalizeElement(element) {
70
110
  isHydrating: false
71
111
  };
72
112
  });
113
+ //#endregion
114
+ //#region src/client/hydrate-component.ts
115
+ /**
116
+ * Component hydration logic — strategy-agnostic
117
+ *
118
+ * Handles running component setup, creating reactive effects,
119
+ * and restoring server state for hydrated components.
120
+ * Does not depend on islands or any specific SSR strategy.
121
+ */
122
+ /**
123
+ * Hydrate a component - run setup and create reactive effect
124
+ *
125
+ * With trailing markers, the structure is: <content><!--$c:id-->
126
+ * - dom points to start of content
127
+ * - trailingMarker (if provided) is the anchor at the end
128
+ *
129
+ * @param vnode - The VNode to hydrate
130
+ * @param dom - The DOM node to start from (content starts here)
131
+ * @param parent - The parent node
132
+ * @param serverState - Optional state captured from server for async components
133
+ * @param trailingMarker - Optional trailing marker comment (the component anchor)
134
+ */
73
135
  function hydrateComponent(vnode, dom, parent, serverState, trailingMarker) {
74
136
  const componentFactory = vnode.type;
75
137
  const setup = componentFactory.__setup;
@@ -208,6 +270,32 @@ function hydrateComponent(vnode, dom, parent, serverState, trailingMarker) {
208
270
  };
209
271
  return anchor ? anchor.nextSibling : endDom;
210
272
  }
273
+ //#endregion
274
+ //#region src/client/hydrate-core.ts
275
+ /**
276
+ * Core hydration logic — strategy-agnostic
277
+ *
278
+ * Walks existing server-rendered DOM and attaches event handlers,
279
+ * creates reactive effects, and delegates components to the component hydrator.
280
+ *
281
+ * Plugins registered via `registerClientPlugin()` can intercept component
282
+ * hydration (e.g., for deferred/island-based hydration strategies).
283
+ */
284
+ /**
285
+ * Hydrate a server-rendered app.
286
+ *
287
+ * This walks the existing DOM to attach event handlers, runs component
288
+ * setup functions to establish reactivity, then uses runtime-dom for updates.
289
+ *
290
+ * Registered client plugins are called at appropriate points:
291
+ * - `beforeHydrate`: before the DOM walk (return false to skip it entirely)
292
+ * - `hydrateComponent`: for each component (return { next } to handle it)
293
+ * - `afterHydrate`: after the DOM walk completes
294
+ *
295
+ * @param element - The root element/VNode to hydrate
296
+ * @param container - The DOM container with SSR content
297
+ * @param appContext - The app context for DI (provides, etc.)
298
+ */
211
299
  function hydrate(element, container, appContext) {
212
300
  const vnode = normalizeElement(element);
213
301
  if (!vnode) return;
@@ -221,11 +309,17 @@ function hydrate(element, container, appContext) {
221
309
  for (const plugin of plugins) plugin.client?.afterHydrate?.(container);
222
310
  container._vnode = vnode;
223
311
  }
312
+ /**
313
+ * Hydrate a VNode against existing DOM
314
+ * This only attaches event handlers and refs - no DOM creation
315
+ */
224
316
  function hydrateNode(vnode, dom, parent) {
225
317
  if (!vnode) return dom;
226
318
  const isComponentVNode = isComponent(vnode.type);
227
319
  const isTextVNode = vnode.type === Text;
320
+ const isCommentVNode = vnode.type === Comment;
228
321
  while (dom && dom.nodeType === Node.COMMENT_NODE) {
322
+ if (isCommentVNode && dom.data === "") break;
229
323
  if (isComponentVNode) {
230
324
  if (dom.data.startsWith("$c:")) break;
231
325
  }
@@ -237,6 +331,17 @@ function hydrateNode(vnode, dom, parent) {
237
331
  }
238
332
  dom = dom.nextSibling;
239
333
  }
334
+ if (vnode.type === Comment) {
335
+ if (dom && dom.nodeType === Node.COMMENT_NODE) {
336
+ vnode.dom = dom;
337
+ return dom.nextSibling;
338
+ }
339
+ const comment = document.createComment("");
340
+ if (dom) parent.insertBefore(comment, dom);
341
+ else parent.appendChild(comment);
342
+ vnode.dom = comment;
343
+ return dom;
344
+ }
240
345
  if (vnode.type === Text) {
241
346
  if (dom && dom.nodeType === Node.TEXT_NODE) {
242
347
  vnode.dom = dom;
@@ -294,6 +399,10 @@ function hydrateNode(vnode, dom, parent) {
294
399
  }
295
400
  return dom;
296
401
  }
402
+ /**
403
+ * Fix select element value after hydrating children.
404
+ * This is needed because <select>.value only works after <option> children exist in DOM.
405
+ */
297
406
  function fixSelectValue(dom, props) {
298
407
  if (dom.tagName === "SELECT" && "value" in props) {
299
408
  const val = props.value;
@@ -304,7 +413,26 @@ function fixSelectValue(dom, props) {
304
413
  } else dom.value = String(val);
305
414
  }
306
415
  }
307
- const ssrClientPlugin = {
416
+ //#endregion
417
+ //#region src/client/plugin.ts
418
+ /**
419
+ * SSR Client Plugin
420
+ *
421
+ * Adds the hydrate() method to the app instance for client-side hydration.
422
+ * Also registers the SSR context extension for all components.
423
+ *
424
+ * @example
425
+ * ```tsx
426
+ * import { defineApp } from 'sigx';
427
+ * import { ssrClientPlugin } from '@sigx/server-renderer/client';
428
+ *
429
+ * const app = defineApp(<App />);
430
+ * app.use(ssrClientPlugin)
431
+ * .use(router)
432
+ * .hydrate('#app');
433
+ * ```
434
+ */
435
+ var ssrClientPlugin = {
308
436
  name: "@sigx/server-renderer/client",
309
437
  install(app) {
310
438
  app.hydrate = function(container) {
@@ -321,6 +449,7 @@ const ssrClientPlugin = {
321
449
  };
322
450
  }
323
451
  };
452
+ //#endregion
324
453
  export { clearClientPlugins as a, getCurrentAppContext as c, setPendingServerState as d, hydrateComponent as i, registerClientPlugin as l, hydrate as n, createRestoringSignal as o, hydrateNode as r, getClientPlugins as s, ssrClientPlugin as t, setCurrentAppContext as u };
325
454
 
326
- //# sourceMappingURL=client-DVLUaAq6.js.map
455
+ //# sourceMappingURL=client-CL2CTcTJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-CL2CTcTJ.js","names":[],"sources":["../src/client/hydrate-context.ts","../src/client/hydrate-component.ts","../src/client/hydrate-core.ts","../src/client/plugin.ts"],"sourcesContent":["/**\r\n * Hydration context and state management\r\n *\r\n * Manages server state restoration, app context tracking,\r\n * client plugin registration, and the SSR context extension for components.\r\n *\r\n * Strategy-specific concerns (island data, async hydration) are handled\r\n * by plugins registered via `registerClientPlugin()`.\r\n */\r\n\r\nimport {\r\n VNode,\r\n signal,\r\n Text,\r\n} from 'sigx';\r\nimport { registerContextExtension } from 'sigx/internals';\r\nimport type { AppContext } from 'sigx';\r\nimport type { SSRPlugin } from '../plugin';\r\nimport type { SSRSignalFn } from '../server/types';\r\nimport { generateSignalKey } from '../server/types';\r\n\r\n// ============= Internal Types =============\r\n\r\nexport interface InternalVNode extends VNode {\r\n _subTree?: VNode;\r\n _subTreeRef?: { current: VNode | null };\r\n _effect?: any;\r\n _componentProps?: any;\r\n _slots?: any;\r\n}\r\n\r\n// Re-export SSRSignalFn from shared types so existing consumers work\r\nexport type { SSRSignalFn };\r\n\r\n// ============= Module State =============\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// Track current app context during hydration for DI\r\n// Used for deferred hydration callbacks\r\nlet _currentAppContext: AppContext | null = null;\r\n\r\n// Registered client-side SSR plugins\r\nlet _clientPlugins: SSRPlugin[] = [];\r\n\r\n// ============= Client Plugin Registry =============\r\n\r\n/**\r\n * Register a client-side SSR plugin.\r\n * Plugins are called during hydration to intercept component processing,\r\n * skip default hydration walk, or run post-hydration logic.\r\n */\r\nexport function registerClientPlugin(plugin: SSRPlugin): void {\r\n _clientPlugins.push(plugin);\r\n}\r\n\r\n/**\r\n * Get all registered client-side plugins.\r\n */\r\nexport function getClientPlugins(): SSRPlugin[] {\r\n return _clientPlugins;\r\n}\r\n\r\n/**\r\n * Clear all registered client plugins (useful for testing).\r\n */\r\nexport function clearClientPlugins(): void {\r\n _clientPlugins = [];\r\n}\r\n\r\n// ============= State Accessors =============\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\nexport function setPendingServerState(state: Record<string, any> | null): void {\r\n _pendingServerState = state;\r\n}\r\n\r\n/** Get the current app context for deferred hydration */\r\nexport function getCurrentAppContext(): AppContext | null {\r\n return _currentAppContext;\r\n}\r\n\r\n/** Set the current app context during hydration */\r\nexport function setCurrentAppContext(ctx: AppContext | null): void {\r\n _currentAppContext = ctx;\r\n}\r\n\r\n// ============= Signal Restoration =============\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 * Supports both primitive and object signals.\r\n */\r\nexport function createRestoringSignal(serverState: Record<string, any>): SSRSignalFn {\r\n let signalIndex = 0;\r\n let hasWarnedPositional = false;\r\n\r\n return function restoringSignal(initial: any, name?: string): any {\r\n // Generate a stable key for this signal (must match server-side)\r\n const key = generateSignalKey(name, signalIndex++);\r\n\r\n // Dev warning: positional keys are fragile\r\n if (process.env.NODE_ENV !== 'production' && !name && !hasWarnedPositional) {\r\n hasWarnedPositional = true;\r\n console.warn(\r\n `[SSR Hydration] Signal restored without a name — using positional key \"${key}\". ` +\r\n `If signal declaration order differs between server and client builds, ` +\r\n `state will be silently mismatched. Use named signals: signal(value, \"name\")`\r\n );\r\n }\r\n\r\n // Check if we have server state for this signal\r\n if (key in serverState) {\r\n return signal(serverState[key]);\r\n }\r\n\r\n // No server state, use initial value\r\n return signal(initial as any);\r\n } as SSRSignalFn;\r\n}\r\n\r\n// ============= Element Normalization =============\r\n\r\n/**\r\n * Normalize any element to VNode\r\n */\r\nexport function 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// ============= Context Extension Registration =============\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\n * Component hydration logic — strategy-agnostic\r\n *\r\n * Handles running component setup, creating reactive effects,\r\n * and restoring server state for hydrated components.\r\n * Does not depend on islands or any specific SSR strategy.\r\n */\r\n\r\nimport {\r\n VNode,\r\n getCurrentInstance,\r\n signal,\r\n effect,\r\n isModel\r\n} from 'sigx';\r\nimport type { ComponentSetupContext, SlotsObject } from 'sigx';\r\nimport {\r\n setCurrentInstance,\r\n createPropsAccessor,\r\n createSlots,\r\n normalizeSubTree,\r\n patch,\r\n mount,\r\n patchProp,\r\n filterClientDirectives,\r\n createEmit,\r\n provideAppContext,\r\n} from 'sigx/internals';\r\nimport {\r\n InternalVNode,\r\n createRestoringSignal,\r\n getCurrentAppContext\r\n} from './hydrate-context';\r\nimport { hydrateNode } from './hydrate-core';\r\n\r\n/**\r\n * Minimal type for component factories used in hydration.\r\n * Compatible with ComponentFactory from runtime-core.\r\n */\r\nexport interface ComponentFactory {\r\n __setup: Function;\r\n __name?: string;\r\n __async?: boolean;\r\n}\r\n\r\n/**\r\n * Hydrate a component - run setup and create reactive effect\r\n *\r\n * With trailing markers, the structure is: <content><!--$c:id-->\r\n * - dom points to start of content\r\n * - trailingMarker (if provided) is the anchor at the end\r\n *\r\n * @param vnode - The VNode to hydrate\r\n * @param dom - The DOM node to start from (content starts here)\r\n * @param parent - The parent node\r\n * @param serverState - Optional state captured from server for async components\r\n * @param trailingMarker - Optional trailing marker comment (the component anchor)\r\n */\r\nexport function hydrateComponent(vnode: VNode, dom: Node | null, parent: Node, serverState?: Record<string, any>, trailingMarker?: Comment | null): 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 // With trailing markers, find the marker if not provided\r\n let anchor: Comment | null = trailingMarker || null;\r\n let componentId: number | null = null;\r\n\r\n if (!anchor) {\r\n // Find this component's trailing marker by traversing forward.\r\n // SSR emits <!--$c:N--> after each component's content, with parent IDs\r\n // lower than child IDs. When nested components exist, child markers appear\r\n // before the parent marker. We find the correct (outermost) marker by\r\n // looking for the lowest-ID $c: comment in a contiguous sequence.\r\n let current: Node | null = dom;\r\n let bestAnchor: Comment | null = null;\r\n let bestId: number = Infinity;\r\n let foundAnyMarker = false;\r\n\r\n while (current) {\r\n if (current.nodeType === Node.COMMENT_NODE) {\r\n const text = (current as Comment).data;\r\n if (text.startsWith('$c:')) {\r\n const id = parseInt(text.slice(3), 10);\r\n if (id < bestId) {\r\n bestId = id;\r\n bestAnchor = current as Comment;\r\n }\r\n foundAnyMarker = true;\r\n }\r\n } else if (foundAnyMarker) {\r\n // Hit a non-comment node after finding markers — we've passed\r\n // our component's boundary and entered a sibling's content\r\n break;\r\n }\r\n current = current.nextSibling;\r\n }\r\n\r\n if (bestAnchor) {\r\n anchor = bestAnchor;\r\n componentId = bestId;\r\n }\r\n } else {\r\n // Extract component ID from provided marker\r\n const text = anchor.data;\r\n if (text.startsWith('$c:')) {\r\n componentId = parseInt(text.slice(3), 10);\r\n }\r\n }\r\n\r\n const internalVNode = vnode as InternalVNode;\r\n const initialProps = vnode.props || {};\r\n const { children, slots: slotsFromProps, $models: modelsData, ...propsData } = filterClientDirectives(initialProps);\r\n\r\n // Merge Model<T> objects directly into props for unified access: props.model.value\r\n const propsWithModels = { ...propsData };\r\n if (modelsData) {\r\n for (const modelKey in modelsData) {\r\n const modelValue = modelsData[modelKey];\r\n if (isModel(modelValue)) {\r\n propsWithModels[modelKey] = modelValue;\r\n }\r\n }\r\n }\r\n\r\n // Create reactive props\r\n const reactiveProps = signal(propsWithModels);\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 unmountHooks: ((ctx: any) => void)[] = [];\r\n const createdHooks: (() => void)[] = [];\r\n const updatedHooks: (() => 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 },\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: createEmit(reactiveProps),\r\n parent: parentInstance,\r\n onMounted: (fn) => { mountHooks.push(fn); },\r\n onUnmounted: (fn) => { unmountHooks.push(fn); },\r\n onCreated: (fn) => { createdHooks.push(fn); },\r\n onUpdated: (fn) => { updatedHooks.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 // For ROOT component only (no parent), provide the AppContext\r\n if (!parentInstance && getCurrentAppContext()) {\r\n provideAppContext(componentCtx, getCurrentAppContext()!);\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 if (process.env.NODE_ENV !== 'production') {\r\n console.error(`Error hydrating component ${componentName}:`, err);\r\n }\r\n } finally {\r\n setCurrentInstance(prev);\r\n }\r\n\r\n // Track where the component's DOM starts\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 // Shared mutable ref for the current subtree (same pattern as renderer).\r\n // This ensures that when same-type patching replaces the VNode,\r\n // the effect closure and all aliased VNodes share the same subtree reference.\r\n const subTreeRef: { current: VNode | null } = { current: null };\r\n internalVNode._subTreeRef = subTreeRef;\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 const prevSubTree = subTreeRef.current;\r\n\r\n // Handle null/undefined renders (e.g., conditional components like Modal)\r\n if (subTreeResult == null) {\r\n if (isFirstRender) {\r\n // Check if there's SSR content in the DOM (between dom and anchor).\r\n // This happens when a lazy() component returns null on first render\r\n // (chunk not loaded yet) but the server rendered the full content.\r\n // In that case, keep isFirstRender=true so the next render (after the\r\n // lazy component resolves) will hydrate against the existing SSR DOM\r\n // instead of mounting a duplicate.\r\n const hasSSRContent = dom != null && anchor != null && dom !== anchor;\r\n if (!hasSSRContent) {\r\n // Truly null first render — SSR also rendered nothing\r\n isFirstRender = false;\r\n }\r\n // If hasSSRContent, leave isFirstRender=true and SSR DOM visible.\r\n // The component will hydrate when it re-renders with real content.\r\n } else if (prevSubTree && prevSubTree.dom) {\r\n // Had content before, now returning null - unmount the previous subtree\r\n const patchContainer = prevSubTree.dom.parentNode as Element || parent;\r\n const emptyNode = normalizeSubTree(null);\r\n patch(prevSubTree, emptyNode, patchContainer);\r\n subTreeRef.current = emptyNode;\r\n internalVNode._subTree = emptyNode;\r\n }\r\n return;\r\n }\r\n\r\n const subTree = normalizeSubTree(subTreeResult);\r\n\r\n if (isFirstRender) {\r\n isFirstRender = false;\r\n\r\n // Check if SSR actually produced content for this component.\r\n // When dom is null or points directly at the anchor comment,\r\n // SSR rendered nothing (e.g., lazy() returned null on the server).\r\n // In that case, mount fresh instead of trying to hydrate\r\n // against non-existent DOM.\r\n const hasSSRContent = dom != null && dom !== anchor;\r\n if (hasSSRContent) {\r\n // Hydrate against existing SSR DOM\r\n endDom = hydrateNode(subTree, dom, parent);\r\n } else {\r\n // No SSR content — mount fresh before the anchor\r\n mount(subTree, parent as Element, anchor || null);\r\n }\r\n subTreeRef.current = subTree;\r\n internalVNode._subTree = subTree;\r\n } else {\r\n // Subsequent renders - use patch directly like runtime-core does\r\n if (prevSubTree) {\r\n const patchContainer = prevSubTree.dom?.parentNode as Element || parent;\r\n patch(prevSubTree, subTree, patchContainer);\r\n } else {\r\n // No previous subtree - mount fresh using the component's anchor\r\n mount(subTree, parent as Element, anchor || null);\r\n }\r\n subTreeRef.current = subTree;\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 trailing anchor comment as the component's dom reference\r\n vnode.dom = anchor || endDom;\r\n\r\n // Run mount hooks\r\n const mountCtx = { el: parent as Element };\r\n createdHooks.forEach(hook => hook());\r\n mountHooks.forEach(hook => hook(mountCtx));\r\n\r\n // Store cleanup\r\n vnode.cleanup = () => {\r\n unmountHooks.forEach(hook => hook(mountCtx));\r\n };\r\n\r\n // With trailing markers, the anchor IS the end - return next sibling\r\n return anchor ? anchor.nextSibling : endDom;\r\n}\r\n","/**\r\n * Core hydration logic — strategy-agnostic\r\n *\r\n * Walks existing server-rendered DOM and attaches event handlers,\r\n * creates reactive effects, and delegates components to the component hydrator.\r\n *\r\n * Plugins registered via `registerClientPlugin()` can intercept component\r\n * hydration (e.g., for deferred/island-based hydration strategies).\r\n */\r\n\r\nimport {\r\n VNode,\r\n Fragment,\r\n Text,\r\n Comment,\r\n isComponent,\r\n} from 'sigx';\r\nimport { patchProp, patchDirective, onElementMounted } from 'sigx/internals';\r\nimport type { AppContext } from 'sigx';\r\nimport { normalizeElement, setCurrentAppContext, getCurrentAppContext, getClientPlugins } from './hydrate-context';\r\nimport { hydrateComponent } from './hydrate-component';\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\n * Registered client plugins are called at appropriate points:\r\n * - `beforeHydrate`: before the DOM walk (return false to skip it entirely)\r\n * - `hydrateComponent`: for each component (return { next } to handle it)\r\n * - `afterHydrate`: after the DOM walk completes\r\n *\r\n * @param element - The root element/VNode to hydrate\r\n * @param container - The DOM container with SSR content\r\n * @param appContext - The app context for DI (provides, etc.)\r\n */\r\nexport function hydrate(element: any, container: Element, appContext?: AppContext): void {\r\n const vnode = normalizeElement(element);\r\n if (!vnode) return;\r\n\r\n // Store app context for component hydration (DI needs this)\r\n setCurrentAppContext(appContext ?? null);\r\n\r\n const plugins = getClientPlugins();\r\n\r\n // Let plugins intercept before the DOM walk\r\n for (const plugin of plugins) {\r\n const result = plugin.client?.beforeHydrate?.(container);\r\n if (result === false) {\r\n // Plugin opted out of the default DOM walk (e.g., resumable SSR)\r\n (container as any)._vnode = vnode;\r\n return;\r\n }\r\n }\r\n\r\n // Walk existing DOM, attach handlers, and mount components\r\n hydrateNode(vnode, container.firstChild, container);\r\n\r\n // Post-hydration hooks\r\n for (const plugin of plugins) {\r\n plugin.client?.afterHydrate?.(container);\r\n }\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/**\r\n * Hydrate a VNode against existing DOM\r\n * This only attaches event handlers and refs - no DOM creation\r\n */\r\nexport function hydrateNode(vnode: VNode, dom: Node | null, parent: Node): Node | null {\r\n if (!vnode) return dom;\r\n\r\n // Skip comment nodes (<!--t--> text separators and <!--$c:N--> component markers).\r\n // Component markers are only meaningful when the VNode itself is a component —\r\n // for element/text/fragment VNodes, all comments are just SSR artifacts to skip past.\r\n const isComponentVNode = isComponent(vnode.type);\r\n const isTextVNode = vnode.type === Text;\r\n const isCommentVNode = vnode.type === Comment;\r\n while (dom && dom.nodeType === Node.COMMENT_NODE) {\r\n // Comment VNodes match empty comment placeholders emitted by SSR\r\n if (isCommentVNode && (dom as globalThis.Comment).data === '') {\r\n break;\r\n }\r\n if (isComponentVNode) {\r\n const commentText = (dom as globalThis.Comment).data;\r\n // Stop at component markers — the component hydrator needs them for boundaries\r\n if (commentText.startsWith('$c:')) {\r\n break;\r\n }\r\n }\r\n // When a text VNode hits a <!--t--> separator, the SSR may have omitted the\r\n // preceding empty text (e.g. \"\" + \" · Logout\" → <!--t--> · Logout).\r\n // Replace the comment with an empty text node so this VNode can attach to it,\r\n // preserving the boundary for the next text VNode.\r\n if (isTextVNode && (dom as globalThis.Comment).data === 't') {\r\n const emptyText = document.createTextNode('');\r\n parent.replaceChild(emptyText, dom);\r\n dom = emptyText;\r\n break;\r\n }\r\n dom = dom.nextSibling;\r\n }\r\n\r\n if (vnode.type === Comment) {\r\n // SSR emits <!---> for falsy children — attach to the comment node\r\n if (dom && dom.nodeType === Node.COMMENT_NODE) {\r\n vnode.dom = dom;\r\n return dom.nextSibling;\r\n }\r\n // Fallback: create a comment node if SSR didn't emit one (mismatch recovery)\r\n const comment = document.createComment('');\r\n if (dom) {\r\n parent.insertBefore(comment, dom);\r\n } else {\r\n parent.appendChild(comment);\r\n }\r\n vnode.dom = comment;\r\n return dom;\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 // Hydration mismatch: expected a text node but got something else.\r\n // Create a fresh text node and insert it so the VNode has a valid DOM ref.\r\n const textNode = document.createTextNode(String(vnode.text ?? ''));\r\n if (dom) {\r\n parent.insertBefore(textNode, dom);\r\n } else {\r\n parent.appendChild(textNode);\r\n }\r\n vnode.dom = textNode;\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 // Let plugins intercept component hydration (e.g., islands scheduling)\r\n const plugins = getClientPlugins();\r\n for (const plugin of plugins) {\r\n const result = plugin.client?.hydrateComponent?.(vnode, dom, parent);\r\n if (result !== undefined) {\r\n // Plugin handled this component — return the next DOM node\r\n return result;\r\n }\r\n }\r\n\r\n // No plugin handled it — 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 if (process.env.NODE_ENV !== 'production') {\r\n const cls = vnode.props?.class || '';\r\n console.warn('[Hydrate] Expected element but got:', dom, '| tag:', vnode.type, '| class:', cls, '| parent:', parent?.nodeName);\r\n }\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 let hasDirectives = false;\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 if (key.charCodeAt(0) === 117 /* 'u' */ && key.startsWith('use:')) {\r\n // Route use:* directive props through patchDirective\r\n patchDirective(el, key.slice(4), null, vnode.props[key], getCurrentAppContext());\r\n hasDirectives = true;\r\n } else {\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 // Fire mounted hooks for directives (element is already in DOM during hydration)\r\n if (hasDirectives) {\r\n onElementMounted(el);\r\n }\r\n\r\n // Handle ref - patchProp skips refs, so we handle them here\r\n if (vnode.props.ref) {\r\n if (typeof vnode.props.ref === 'function') {\r\n vnode.props.ref(el);\r\n } else if (typeof vnode.props.ref === 'object') {\r\n vnode.props.ref.current = el;\r\n }\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 * 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 * SSR Client Plugin\r\n * \r\n * Provides app.hydrate() method for client-side hydration of server-rendered HTML.\r\n * This plugin follows the same pattern as the router plugin.\r\n */\r\n\r\nimport type { Plugin, App, AppContext } from '@sigx/runtime-core';\r\nimport { render } from 'sigx';\r\nimport { hydrate as hydrateImpl } from './hydrate-core';\r\n\r\n// ============================================================================\r\n// Type Augmentation\r\n// ============================================================================\r\n\r\n/**\r\n * Hydrate function signature - matches MountFn pattern\r\n */\r\nexport type HydrateFn<TContainer = any> = (\r\n element: any,\r\n container: TContainer,\r\n appContext: AppContext\r\n) => (() => void) | void;\r\n\r\ndeclare module '@sigx/runtime-core' {\r\n interface App<TContainer = any> {\r\n /**\r\n * Hydrate the app from server-rendered HTML.\r\n * \r\n * Unlike mount() which creates new DOM, hydrate() attaches to existing\r\n * server-rendered DOM, adding event handlers and establishing reactivity.\r\n * \r\n * @example\r\n * ```tsx\r\n * import { defineApp } from 'sigx';\r\n * import { ssrClientPlugin } from '@sigx/server-renderer/client';\r\n * \r\n * const app = defineApp(<App />);\r\n * app.use(router)\r\n * .use(ssrClientPlugin)\r\n * .hydrate(document.getElementById('app')!);\r\n * ```\r\n */\r\n hydrate(container: TContainer): App<TContainer>;\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Plugin Implementation\r\n// ============================================================================\r\n\r\n/**\r\n * SSR Client Plugin\r\n * \r\n * Adds the hydrate() method to the app instance for client-side hydration.\r\n * Also registers the SSR context extension for all components.\r\n * \r\n * @example\r\n * ```tsx\r\n * import { defineApp } from 'sigx';\r\n * import { ssrClientPlugin } from '@sigx/server-renderer/client';\r\n * \r\n * const app = defineApp(<App />);\r\n * app.use(ssrClientPlugin)\r\n * .use(router)\r\n * .hydrate('#app');\r\n * ```\r\n */\r\nexport const ssrClientPlugin: Plugin = {\r\n name: '@sigx/server-renderer/client',\r\n\r\n install(app: App) {\r\n // Add hydrate method to the app instance\r\n (app as any).hydrate = function(container: Element | string): App {\r\n // Resolve container if string selector\r\n const resolvedContainer = typeof container === 'string'\r\n ? document.querySelector(container)\r\n : container;\r\n\r\n if (!resolvedContainer) {\r\n throw new Error(\r\n `[ssrClientPlugin] Cannot find container: ${container}. ` +\r\n 'Make sure the element exists in the DOM before calling hydrate().'\r\n );\r\n }\r\n\r\n // Get the root component from the app\r\n const rootComponent = (app as any)._rootComponent;\r\n \r\n if (!rootComponent) {\r\n throw new Error(\r\n '[ssrClientPlugin] No root component found on app. ' +\r\n 'Make sure you created the app with defineApp(<Component />).'\r\n );\r\n }\r\n\r\n // Check if there's actual SSR content to hydrate\r\n // If container is empty or only has comments, fall back to client-side render\r\n const hasSSRContent = resolvedContainer.firstElementChild !== null ||\r\n (resolvedContainer.firstChild !== null && \r\n resolvedContainer.firstChild.nodeType !== Node.COMMENT_NODE);\r\n\r\n // Get app context for passing to render (needed for inject() to work)\r\n const appContext = (app as any)._context;\r\n\r\n if (hasSSRContent) {\r\n // Perform hydration with app context for DI\r\n hydrateImpl(rootComponent, resolvedContainer, appContext);\r\n } else {\r\n // No SSR content - fall back to client-side render (dev mode)\r\n render(rootComponent, resolvedContainer, appContext);\r\n }\r\n\r\n // Store container on the vnode for potential unmount\r\n (resolvedContainer as any)._app = app;\r\n\r\n return app;\r\n };\r\n }\r\n};\r\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
- import { t as generateSignalKey } from "./types-CkI_lB93.js";
2
- import { a as createSSR, c as renderHeadToString, h as initDirectivesForSSR, i as renderToString, l as useHead, m as createSSRContext, n as renderToStream, o as collectSSRHead, p as renderVNodeToString, s as enableSSRHead } from "./server-pSrHP504.js";
3
- import { t as ssrClientPlugin } from "./client-DVLUaAq6.js";
1
+ import { t as generateSignalKey } from "./types-DYlI_C8F.js";
2
+ import { a as createSSR, c as renderHeadToString, h as initDirectivesForSSR, i as renderToString, l as useHead, m as createSSRContext, n as renderToStream, o as collectSSRHead, p as renderVNodeToString, s as enableSSRHead } from "./server-CiZxVKV9.js";
3
+ import { t as ssrClientPlugin } from "./client-CL2CTcTJ.js";
4
+ //#region src/index.ts
4
5
  initDirectivesForSSR();
6
+ //#endregion
5
7
  export { collectSSRHead, createSSR, createSSRContext, enableSSRHead, generateSignalKey, renderHeadToString, renderToStream, renderToString, renderVNodeToString, ssrClientPlugin, useHead };
6
8
 
7
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\r\n * @sigx/server-renderer\r\n * \r\n * Server-side rendering and client-side hydration for SigX applications.\r\n * Strategy-agnostic core — extend with plugins for islands, resumable SSR, etc.\r\n * \r\n * ## Server Usage\r\n * ```ts\r\n * import { renderToStream, renderToString } from '@sigx/server-renderer/server';\r\n * \r\n * // Streaming (recommended)\r\n * const stream = renderToStream(<App />);\r\n * \r\n * // Or string\r\n * const html = await renderToString(<App />);\r\n * ```\r\n * \r\n * ## Plugin-driven rendering (recommended for islands, async, etc.)\r\n * ```ts\r\n * import { createSSR } from '@sigx/server-renderer';\r\n * import { islandsPlugin } from '@sigx/ssr-islands';\r\n * \r\n * const ssr = createSSR().use(islandsPlugin());\r\n * const html = await ssr.render(<App />);\r\n * ```\r\n * \r\n * ## Client Usage\r\n * ```ts\r\n * import { defineApp } from 'sigx';\r\n * import { ssrClientPlugin } from '@sigx/server-renderer/client';\r\n * \r\n * defineApp(<App />)\r\n * .use(ssrClientPlugin)\r\n * .hydrate('#root');\r\n * ```\r\n * \r\n * @module\r\n */\r\n\r\n// SSR directive type augmentation — adds getSSRProps to DirectiveDefinition\r\nimport './directive-ssr-types.js';\r\n\r\n// Patch getSSRProps onto built-in directives (show, etc.)\r\nimport { initDirectivesForSSR } from './builtin-ssr-directives.js';\r\ninitDirectivesForSSR();\r\n\r\n// Plugin system\r\nexport { createSSR } from './ssr.js';\r\nexport type { SSRInstance } from './ssr.js';\r\nexport type { SSRPlugin } from './plugin.js';\r\n\r\n// Re-export from server (convenience)\r\nexport { renderToStream, renderToString, renderVNodeToString } from './server/index.js';\r\nexport { createSSRContext } from './server/context.js';\r\nexport type { SSRContext, SSRContextOptions, RenderOptions, CorePendingAsync } from './server/context.js';\r\n\r\n// Re-export from client (convenience)\r\nexport { ssrClientPlugin } from './client/index.js';\r\n\r\n// SSR types (shared across server-renderer and plugins)\r\nexport type { SSRHelper } from './client-directives.js';\r\nexport type { SSRSignalFn } from './server/types.js';\r\nexport { generateSignalKey } from './server/types.js';\r\n\r\n// Head management\r\nexport { useHead, renderHeadToString, enableSSRHead, collectSSRHead } from './head.js';\r\nexport type { HeadConfig, HeadMeta, HeadLink, HeadScript } from './head.js';\r\n"],"mappings":";;;AA4CA,sBAAsB"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\r\n * @sigx/server-renderer\r\n * \r\n * Server-side rendering and client-side hydration for SigX applications.\r\n * Strategy-agnostic core — extend with plugins for islands, resumable SSR, etc.\r\n * \r\n * ## Server Usage\r\n * ```ts\r\n * import { renderToStream, renderToString } from '@sigx/server-renderer/server';\r\n * \r\n * // Streaming (recommended)\r\n * const stream = renderToStream(<App />);\r\n * \r\n * // Or string\r\n * const html = await renderToString(<App />);\r\n * ```\r\n * \r\n * ## Plugin-driven rendering (recommended for islands, async, etc.)\r\n * ```ts\r\n * import { createSSR } from '@sigx/server-renderer';\r\n * import { islandsPlugin } from '@sigx/ssr-islands';\r\n * \r\n * const ssr = createSSR().use(islandsPlugin());\r\n * const html = await ssr.render(<App />);\r\n * ```\r\n * \r\n * ## Client Usage\r\n * ```ts\r\n * import { defineApp } from 'sigx';\r\n * import { ssrClientPlugin } from '@sigx/server-renderer/client';\r\n * \r\n * defineApp(<App />)\r\n * .use(ssrClientPlugin)\r\n * .hydrate('#root');\r\n * ```\r\n * \r\n * @module\r\n */\r\n\r\n// SSR directive type augmentation — adds getSSRProps to DirectiveDefinition\r\nimport './directive-ssr-types.js';\r\n\r\n// Patch getSSRProps onto built-in directives (show, etc.)\r\nimport { initDirectivesForSSR } from './builtin-ssr-directives.js';\r\ninitDirectivesForSSR();\r\n\r\n// Plugin system\r\nexport { createSSR } from './ssr.js';\r\nexport type { SSRInstance } from './ssr.js';\r\nexport type { SSRPlugin } from './plugin.js';\r\n\r\n// Re-export from server (convenience)\r\nexport { renderToStream, renderToString, renderVNodeToString } from './server/index.js';\r\nexport { createSSRContext } from './server/context.js';\r\nexport type { SSRContext, SSRContextOptions, RenderOptions, CorePendingAsync } from './server/context.js';\r\n\r\n// Re-export from client (convenience)\r\nexport { ssrClientPlugin } from './client/index.js';\r\n\r\n// SSR types (shared across server-renderer and plugins)\r\nexport type { SSRHelper } from './client-directives.js';\r\nexport type { SSRSignalFn } from './server/types.js';\r\nexport { generateSignalKey } from './server/types.js';\r\n\r\n// Head management\r\nexport { useHead, renderHeadToString, enableSSRHead, collectSSRHead } from './head.js';\r\nexport type { HeadConfig, HeadMeta, HeadLink, HeadScript } from './head.js';\r\n"],"mappings":";;;;AA4CA,sBAAsB"}
@@ -1,3 +1,3 @@
1
- import { t as generateSignalKey } from "../types-CkI_lB93.js";
2
- import { d as generateReplacementScript, f as generateStreamingScript, i as renderToString, m as createSSRContext, n as renderToStream, p as renderVNodeToString, r as renderToStreamWithCallbacks, t as renderToNodeStream, u as escapeJsonForScript } from "../server-pSrHP504.js";
1
+ import { t as generateSignalKey } from "../types-DYlI_C8F.js";
2
+ import { d as generateReplacementScript, f as generateStreamingScript, i as renderToString, m as createSSRContext, n as renderToStream, p as renderVNodeToString, r as renderToStreamWithCallbacks, t as renderToNodeStream, u as escapeJsonForScript } from "../server-CiZxVKV9.js";
3
3
  export { createSSRContext, escapeJsonForScript, generateReplacementScript, generateSignalKey, generateStreamingScript, renderToNodeStream, renderToStream, renderToStreamWithCallbacks, renderToString, renderVNodeToString };
@@ -1 +1 @@
1
- {"version":3,"file":"render-core.d.ts","sourceRoot":"","sources":["../../src/server/render-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAoC,UAAU,EAAE,MAAM,MAAM,CAAC;AAO5G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAY5C,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5C;AAQD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIhD;AAiBD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcxE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CASjE;AAwCD;;;;;;GAMG;AACH,wBAAuB,cAAc,CACjC,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,UAAU,EACf,SAAS,GAAE,qBAAqB,GAAG,IAAW,EAC9C,UAAU,GAAE,UAAU,GAAG,IAAW,GACrC,cAAc,CAAC,MAAM,CAAC,CAoXxB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,GAAE,UAAU,GAAG,IAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAMrI;AAID;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAC9B,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,qBAAqB,GAAG,IAAI,EACvC,UAAU,EAAE,UAAU,GAAG,IAAI,EAC7B,GAAG,EAAE,MAAM,EAAE,GACd,OAAO,CAmQT"}
1
+ {"version":3,"file":"render-core.d.ts","sourceRoot":"","sources":["../../src/server/render-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAoC,UAAU,EAAE,MAAM,MAAM,CAAC;AAO5G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAY5C,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5C;AAQD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIhD;AAiBD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcxE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CASjE;AAwCD;;;;;;GAMG;AACH,wBAAuB,cAAc,CACjC,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,UAAU,EACf,SAAS,GAAE,qBAAqB,GAAG,IAAW,EAC9C,UAAU,GAAE,UAAU,GAAG,IAAW,GACrC,cAAc,CAAC,MAAM,CAAC,CA0XxB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,GAAE,UAAU,GAAG,IAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAMrI;AAID;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAC9B,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,qBAAqB,GAAG,IAAI,EACvC,UAAU,EAAE,UAAU,GAAG,IAAI,EAC7B,GAAG,EAAE,MAAM,EAAE,GACd,OAAO,CAyQT"}