@askrjs/askr 0.0.38 → 0.0.40

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.
Files changed (250) hide show
  1. package/dist/bench/components/benchmark-row.d.ts.map +1 -1
  2. package/dist/bench/components/benchmark-row.js.map +1 -1
  3. package/dist/bench/components/benchmark-table.js.map +1 -1
  4. package/dist/benchmark.d.ts.map +1 -1
  5. package/dist/benchmark.js +2 -2
  6. package/dist/benchmark.js.map +1 -1
  7. package/dist/bin/askr-ssg.d.ts.map +1 -1
  8. package/dist/bin/askr-ssg.js.map +1 -1
  9. package/dist/boot/index.d.ts +9 -6
  10. package/dist/boot/index.d.ts.map +1 -1
  11. package/dist/boot/index.js +210 -75
  12. package/dist/boot/index.js.map +1 -1
  13. package/dist/common/attr-names.js +17 -0
  14. package/dist/common/attr-names.js.map +1 -0
  15. package/dist/common/component.d.ts.map +1 -1
  16. package/dist/common/control.d.ts.map +1 -1
  17. package/dist/common/control.js.map +1 -1
  18. package/dist/common/env.js.map +1 -1
  19. package/dist/common/jsx.d.ts.map +1 -1
  20. package/dist/common/jsx.js.map +1 -1
  21. package/dist/common/promise.js +8 -0
  22. package/dist/common/promise.js.map +1 -0
  23. package/dist/common/props.d.ts +286 -1
  24. package/dist/common/props.d.ts.map +1 -1
  25. package/dist/common/router-internal.js +6 -0
  26. package/dist/common/router-internal.js.map +1 -0
  27. package/dist/common/router.d.ts +45 -36
  28. package/dist/common/router.d.ts.map +1 -1
  29. package/dist/common/ssr-errors.d.ts.map +1 -1
  30. package/dist/common/ssr-errors.js.map +1 -1
  31. package/dist/common/ssr.d.ts +1 -0
  32. package/dist/common/ssr.d.ts.map +1 -1
  33. package/dist/common/ssr.js +6 -0
  34. package/dist/common/ssr.js.map +1 -0
  35. package/dist/common/vnode.d.ts +2 -1
  36. package/dist/common/vnode.d.ts.map +1 -1
  37. package/dist/common/vnode.js.map +1 -1
  38. package/dist/components/error-boundary.d.ts +7 -4
  39. package/dist/components/error-boundary.d.ts.map +1 -1
  40. package/dist/components/error-boundary.js.map +1 -1
  41. package/dist/components/link.d.ts +2 -1
  42. package/dist/components/link.d.ts.map +1 -1
  43. package/dist/components/link.js +10 -1
  44. package/dist/components/link.js.map +1 -1
  45. package/dist/control/case.d.ts +4 -2
  46. package/dist/control/case.d.ts.map +1 -1
  47. package/dist/control/case.js.map +1 -1
  48. package/dist/control/for.d.ts +2 -1
  49. package/dist/control/for.d.ts.map +1 -1
  50. package/dist/control/for.js.map +1 -1
  51. package/dist/control/shared.d.ts +9 -0
  52. package/dist/control/shared.d.ts.map +1 -0
  53. package/dist/control/shared.js.map +1 -1
  54. package/dist/control/show.d.ts +4 -3
  55. package/dist/control/show.d.ts.map +1 -1
  56. package/dist/control/show.js.map +1 -1
  57. package/dist/data/index.d.ts +94 -14
  58. package/dist/data/index.d.ts.map +1 -1
  59. package/dist/data/index.js +65 -14
  60. package/dist/data/index.js.map +1 -1
  61. package/dist/dev/invariant.js.map +1 -1
  62. package/dist/dev/logger.js.map +1 -1
  63. package/dist/foundations/icon/icon.d.ts.map +1 -1
  64. package/dist/foundations/icon/icon.js.map +1 -1
  65. package/dist/foundations/icon/icon.types.d.ts.map +1 -1
  66. package/dist/foundations/interactions/dismissable.d.ts +9 -3
  67. package/dist/foundations/interactions/dismissable.d.ts.map +1 -1
  68. package/dist/foundations/interactions/dismissable.js.map +1 -1
  69. package/dist/foundations/interactions/focusable.d.ts.map +1 -1
  70. package/dist/foundations/interactions/focusable.js.map +1 -1
  71. package/dist/foundations/interactions/hoverable.d.ts.map +1 -1
  72. package/dist/foundations/interactions/hoverable.js.map +1 -1
  73. package/dist/foundations/interactions/interaction-policy.d.ts +1 -1
  74. package/dist/foundations/interactions/interaction-policy.d.ts.map +1 -1
  75. package/dist/foundations/interactions/interaction-policy.js +3 -3
  76. package/dist/foundations/interactions/interaction-policy.js.map +1 -1
  77. package/dist/foundations/interactions/pressable.d.ts.map +1 -1
  78. package/dist/foundations/interactions/pressable.js.map +1 -1
  79. package/dist/foundations/interactions/roving-focus.d.ts.map +1 -1
  80. package/dist/foundations/interactions/roving-focus.js +8 -8
  81. package/dist/foundations/interactions/roving-focus.js.map +1 -1
  82. package/dist/foundations/state/controllable.d.ts.map +1 -1
  83. package/dist/foundations/state/controllable.js.map +1 -1
  84. package/dist/foundations/structures/collection.d.ts.map +1 -1
  85. package/dist/foundations/structures/collection.js.map +1 -1
  86. package/dist/foundations/structures/layer.d.ts.map +1 -1
  87. package/dist/foundations/structures/layer.js.map +1 -1
  88. package/dist/foundations/structures/layout.d.ts +4 -2
  89. package/dist/foundations/structures/layout.d.ts.map +1 -1
  90. package/dist/foundations/structures/layout.js.map +1 -1
  91. package/dist/foundations/structures/portal.d.ts +9 -25
  92. package/dist/foundations/structures/portal.d.ts.map +1 -1
  93. package/dist/foundations/structures/portal.js +98 -17
  94. package/dist/foundations/structures/portal.js.map +1 -1
  95. package/dist/foundations/structures/presence.d.ts +4 -3
  96. package/dist/foundations/structures/presence.d.ts.map +1 -1
  97. package/dist/foundations/structures/presence.js +2 -2
  98. package/dist/foundations/structures/presence.js.map +1 -1
  99. package/dist/foundations/structures/slot.d.ts +2 -1
  100. package/dist/foundations/structures/slot.d.ts.map +1 -1
  101. package/dist/foundations/structures/slot.js.map +1 -1
  102. package/dist/foundations/utilities/aria.d.ts.map +1 -1
  103. package/dist/foundations/utilities/aria.js.map +1 -1
  104. package/dist/foundations/utilities/compose-handlers.d.ts.map +1 -1
  105. package/dist/foundations/utilities/compose-handlers.js.map +1 -1
  106. package/dist/foundations/utilities/compose-ref.d.ts.map +1 -1
  107. package/dist/foundations/utilities/compose-ref.js.map +1 -1
  108. package/dist/foundations/utilities/event-types.d.ts.map +1 -1
  109. package/dist/foundations/utilities/merge-props.js.map +1 -1
  110. package/dist/foundations/utilities/use-id.d.ts.map +1 -1
  111. package/dist/foundations/utilities/use-id.js.map +1 -1
  112. package/dist/fx/fx.d.ts.map +1 -1
  113. package/dist/fx/fx.js.map +1 -1
  114. package/dist/fx/noop.js.map +1 -1
  115. package/dist/fx/timing.d.ts +5 -4
  116. package/dist/fx/timing.d.ts.map +1 -1
  117. package/dist/fx/timing.js +7 -4
  118. package/dist/fx/timing.js.map +1 -1
  119. package/dist/index.js.map +1 -1
  120. package/dist/jsx/types.d.ts +3 -3
  121. package/dist/jsx/types.d.ts.map +1 -1
  122. package/dist/jsx/utils.d.ts.map +1 -1
  123. package/dist/jsx/utils.js.map +1 -1
  124. package/dist/jsx-dev-runtime.d.ts +6 -2
  125. package/dist/jsx-dev-runtime.d.ts.map +1 -1
  126. package/dist/jsx-dev-runtime.js.map +1 -1
  127. package/dist/jsx-runtime.d.ts +16 -6
  128. package/dist/jsx-runtime.d.ts.map +1 -1
  129. package/dist/jsx-runtime.js.map +1 -1
  130. package/dist/renderer/children.js.map +1 -1
  131. package/dist/renderer/cleanup.js +40 -13
  132. package/dist/renderer/cleanup.js.map +1 -1
  133. package/dist/renderer/dom.js +163 -63
  134. package/dist/renderer/dom.js.map +1 -1
  135. package/dist/renderer/evaluate.js +9 -6
  136. package/dist/renderer/evaluate.js.map +1 -1
  137. package/dist/renderer/fastpath.js.map +1 -1
  138. package/dist/renderer/for-commit.js +1 -0
  139. package/dist/renderer/for-commit.js.map +1 -1
  140. package/dist/renderer/index.js.map +1 -1
  141. package/dist/renderer/keyed.js.map +1 -1
  142. package/dist/renderer/reconcile.js +34 -0
  143. package/dist/renderer/reconcile.js.map +1 -1
  144. package/dist/renderer/utils.js +34 -3
  145. package/dist/renderer/utils.js.map +1 -1
  146. package/dist/router/index.d.ts +2 -2
  147. package/dist/router/match.js +16 -2
  148. package/dist/router/match.js.map +1 -1
  149. package/dist/router/navigate.d.ts +9 -1
  150. package/dist/router/navigate.d.ts.map +1 -1
  151. package/dist/router/navigate.js +235 -81
  152. package/dist/router/navigate.js.map +1 -1
  153. package/dist/router/policy.d.ts +7 -7
  154. package/dist/router/policy.d.ts.map +1 -1
  155. package/dist/router/policy.js +5 -7
  156. package/dist/router/policy.js.map +1 -1
  157. package/dist/router/route-context.js.map +1 -1
  158. package/dist/router/route.d.ts +20 -9
  159. package/dist/router/route.d.ts.map +1 -1
  160. package/dist/router/route.js +95 -29
  161. package/dist/router/route.js.map +1 -1
  162. package/dist/runtime/child-scope.d.ts.map +1 -1
  163. package/dist/runtime/child-scope.js +5 -1
  164. package/dist/runtime/child-scope.js.map +1 -1
  165. package/dist/runtime/component.d.ts +5 -2
  166. package/dist/runtime/component.d.ts.map +1 -1
  167. package/dist/runtime/component.js +85 -25
  168. package/dist/runtime/component.js.map +1 -1
  169. package/dist/runtime/context.d.ts +5 -2
  170. package/dist/runtime/context.d.ts.map +1 -1
  171. package/dist/runtime/context.js +5 -2
  172. package/dist/runtime/context.js.map +1 -1
  173. package/dist/runtime/control.d.ts.map +1 -1
  174. package/dist/runtime/control.js.map +1 -1
  175. package/dist/runtime/derive.d.ts.map +1 -1
  176. package/dist/runtime/derive.js +3 -1
  177. package/dist/runtime/derive.js.map +1 -1
  178. package/dist/runtime/dev-namespace.js.map +1 -1
  179. package/dist/runtime/effect.d.ts.map +1 -1
  180. package/dist/runtime/effect.js +17 -11
  181. package/dist/runtime/effect.js.map +1 -1
  182. package/dist/runtime/events.js +43 -1
  183. package/dist/runtime/events.js.map +1 -1
  184. package/dist/runtime/execution-model.js.map +1 -1
  185. package/dist/runtime/fastlane.js +2 -23
  186. package/dist/runtime/fastlane.js.map +1 -1
  187. package/dist/runtime/for-bench.d.ts.map +1 -1
  188. package/dist/runtime/for-bench.js.map +1 -1
  189. package/dist/runtime/for.d.ts +1 -1
  190. package/dist/runtime/for.d.ts.map +1 -1
  191. package/dist/runtime/for.js +36 -12
  192. package/dist/runtime/for.js.map +1 -1
  193. package/dist/runtime/operations.d.ts +3 -13
  194. package/dist/runtime/operations.d.ts.map +1 -1
  195. package/dist/runtime/operations.js +47 -39
  196. package/dist/runtime/operations.js.map +1 -1
  197. package/dist/runtime/perf-metrics.js.map +1 -1
  198. package/dist/runtime/readable.d.ts +1 -0
  199. package/dist/runtime/readable.d.ts.map +1 -1
  200. package/dist/runtime/readable.js +6 -1
  201. package/dist/runtime/readable.js.map +1 -1
  202. package/dist/runtime/resource-cell.js +13 -2
  203. package/dist/runtime/resource-cell.js.map +1 -1
  204. package/dist/runtime/scheduler.d.ts.map +1 -1
  205. package/dist/runtime/scheduler.js +8 -4
  206. package/dist/runtime/scheduler.js.map +1 -1
  207. package/dist/runtime/selector.d.ts.map +1 -1
  208. package/dist/runtime/selector.js +8 -2
  209. package/dist/runtime/selector.js.map +1 -1
  210. package/dist/runtime/snapshot-source.d.ts.map +1 -1
  211. package/dist/runtime/snapshot-source.js.map +1 -1
  212. package/dist/runtime/ssr-bridge.js.map +1 -1
  213. package/dist/runtime/state.d.ts +4 -3
  214. package/dist/runtime/state.d.ts.map +1 -1
  215. package/dist/runtime/state.js.map +1 -1
  216. package/dist/ssg/batch-render.js +4 -8
  217. package/dist/ssg/batch-render.js.map +1 -1
  218. package/dist/ssg/create-static-gen.d.ts +10 -2
  219. package/dist/ssg/create-static-gen.d.ts.map +1 -1
  220. package/dist/ssg/create-static-gen.js +5 -4
  221. package/dist/ssg/create-static-gen.js.map +1 -1
  222. package/dist/ssg/generate-metadata.js.map +1 -1
  223. package/dist/ssg/incremental-manifest.js.map +1 -1
  224. package/dist/ssg/resolve-ssg-data.js +54 -12
  225. package/dist/ssg/resolve-ssg-data.js.map +1 -1
  226. package/dist/ssg/route-utils.js.map +1 -1
  227. package/dist/ssg/types.d.ts +8 -7
  228. package/dist/ssg/types.d.ts.map +1 -1
  229. package/dist/ssg/write-static-files.js.map +1 -1
  230. package/dist/ssr/attrs.js +3 -2
  231. package/dist/ssr/attrs.js.map +1 -1
  232. package/dist/ssr/context.d.ts +1 -0
  233. package/dist/ssr/context.d.ts.map +1 -1
  234. package/dist/ssr/context.js +1 -0
  235. package/dist/ssr/context.js.map +1 -1
  236. package/dist/ssr/errors.d.ts.map +1 -1
  237. package/dist/ssr/escape.js.map +1 -1
  238. package/dist/ssr/index.d.ts +8 -12
  239. package/dist/ssr/index.d.ts.map +1 -1
  240. package/dist/ssr/index.js +55 -30
  241. package/dist/ssr/index.js.map +1 -1
  242. package/dist/ssr/render-keys.js +15 -4
  243. package/dist/ssr/render-keys.js.map +1 -1
  244. package/dist/ssr/render-resolved.js +33 -0
  245. package/dist/ssr/render-resolved.js.map +1 -0
  246. package/dist/ssr/sink.js.map +1 -1
  247. package/dist/ssr/types.d.ts.map +1 -1
  248. package/dist/ssr/verify-hydration.js +5 -6
  249. package/dist/ssr/verify-hydration.js.map +1 -1
  250. package/package.json +13 -5
@@ -1 +1 @@
1
- {"version":3,"file":"attrs.js","names":[],"sources":["../../src/ssr/attrs.ts"],"sourcesContent":["/**\n * HTML attribute rendering for SSR\n */\n\nimport type { Props } from '../common/props';\nimport type { RenderSink } from './sink';\nimport { escapeAttr, needsEscapeAttr, styleObjToCss } from './escape';\n\n/** Result of renderAttrs including any raw HTML from dangerouslySetInnerHTML */\nexport type AttrsResult = {\n attrs: string;\n dangerousHtml?: string;\n};\n\nconst ESCAPED_ATTR_VALUE_CACHE_LIMIT = 512;\nconst escapedAttrValueCache = new Map<string, string>();\n\n// Fast check for event handler pattern (on + uppercase letter)\nfunction isEventHandler(key: string): boolean {\n return (\n key.length >= 3 &&\n key.charCodeAt(0) === 111 && // 'o'\n key.charCodeAt(1) === 110 && // 'n'\n key.charCodeAt(2) >= 65 &&\n key.charCodeAt(2) <= 90 // 'A'-'Z'\n );\n}\n\nfunction getEscapedAttrValue(value: string): string {\n if (value.length > 64) {\n return escapeAttr(value);\n }\n\n const cached = escapedAttrValueCache.get(value);\n if (cached !== undefined) {\n return cached;\n }\n\n const escaped = escapeAttr(value);\n // Skip caching strings that required no escaping — same reference returned,\n // no point occupying cache slots with identity entries.\n if (escaped === value) return value;\n if (escapedAttrValueCache.size >= ESCAPED_ATTR_VALUE_CACHE_LIMIT) {\n escapedAttrValueCache.clear();\n }\n escapedAttrValueCache.set(value, escaped);\n return escaped;\n}\n\n/**\n * Render attributes directly to a sink without intermediate string allocations.\n * This is the hot path for streaming SSR.\n */\nexport function renderAttrsDirect(\n props: Props | undefined,\n sink: Pick<RenderSink, 'write'>\n): void {\n if (!props || typeof props !== 'object') return;\n\n const propsObj = props as Record<string, unknown>;\n for (const key in propsObj) {\n const value = propsObj[key];\n\n // Skip special props\n if (\n key === 'children' ||\n key === 'key' ||\n key === 'ref' ||\n key === 'dangerouslySetInnerHTML'\n )\n continue;\n\n // Skip event handlers\n if (isEventHandler(key)) continue;\n\n // Skip internal props\n if (key.charCodeAt(0) === 95) continue; // '_'\n\n // Normalize class attribute\n const attrName = key === 'className' ? 'class' : key;\n\n // Handle style objects\n if (attrName === 'style') {\n const css = typeof value === 'string' ? value : styleObjToCss(value);\n if (!css) continue;\n sink.write(' style=\"');\n // Escape inline - most style values don't need escaping\n if (needsEscapeAttr(css)) {\n sink.write(getEscapedAttrValue(css));\n } else {\n sink.write(css);\n }\n sink.write('\"');\n continue;\n }\n\n // Boolean attributes\n if (value === true) {\n sink.write(' ');\n sink.write(attrName);\n continue;\n }\n\n if (value === false || value === null || value === undefined) continue;\n\n // Regular attributes\n const strValue = String(value);\n sink.write(' ');\n sink.write(attrName);\n sink.write('=\"');\n // escapeAttr returns the original string when nothing needs escaping,\n // so no separate needsEscapeAttr pre-scan is required.\n sink.write(getEscapedAttrValue(strValue));\n sink.write('\"');\n }\n}\n\n/**\n * Render attributes to HTML string, excluding event handlers\n * Optimized for minimal allocations using push-based approach\n *\n * Returns both the attribute string and any dangerouslySetInnerHTML content.\n */\nexport function renderAttrs(props?: Props): string;\nexport function renderAttrs(\n props: Props | undefined,\n opts: { returnDangerousHtml: true }\n): AttrsResult;\nexport function renderAttrs(\n props?: Props,\n opts?: { returnDangerousHtml?: boolean }\n): string | AttrsResult {\n if (!props || typeof props !== 'object') {\n return opts?.returnDangerousHtml ? { attrs: '' } : '';\n }\n\n const attrParts: string[] = [];\n let dangerousHtml: string | undefined;\n\n const propsObj = props as Record<string, unknown>;\n for (const key in propsObj) {\n const value = propsObj[key];\n\n // Skip children in attrs\n if (key === 'children') continue;\n\n // Skip internal identity refs (framework-only)\n if (key === 'key' || key === 'ref') continue;\n\n // Handle dangerouslySetInnerHTML\n if (key === 'dangerouslySetInnerHTML') {\n if (value && typeof value === 'object' && '__html' in (value as object)) {\n dangerousHtml = String((value as { __html: unknown }).__html);\n }\n continue;\n }\n\n // Skip event handlers\n if (isEventHandler(key)) continue;\n\n // Skip internal props\n if (key.charCodeAt(0) === 95) continue; // '_'\n\n // Normalize class attribute (`class` preferred, accept `className` for compatibility)\n const attrName = key === 'class' || key === 'className' ? 'class' : key;\n\n // Handle style objects\n if (attrName === 'style') {\n const css = typeof value === 'string' ? value : styleObjToCss(value);\n if (css === null || css === '') continue;\n attrParts.push(` style=\"${getEscapedAttrValue(css)}\"`);\n continue;\n }\n\n // Boolean attributes\n if (value === true) {\n attrParts.push(` ${attrName}`);\n } else if (value === false || value === null || value === undefined) {\n continue;\n } else {\n const strValue = String(value);\n attrParts.push(` ${attrName}=\"${getEscapedAttrValue(strValue)}\"`);\n }\n }\n\n const result = attrParts.join('');\n\n if (opts?.returnDangerousHtml) {\n return { attrs: result, dangerousHtml };\n }\n return result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"attrs.js","names":[],"sources":["../../src/ssr/attrs.ts"],"sourcesContent":["/**\n * HTML attribute rendering for SSR\n */\n\nimport type { Props } from '../common/props';\nimport { getPublicAttributeName } from '../common/attr-names';\nimport type { RenderSink } from './sink';\nimport { escapeAttr, needsEscapeAttr, styleObjToCss } from './escape';\n\n/** Result of renderAttrs including any raw HTML from dangerouslySetInnerHTML */\nexport type AttrsResult = {\n attrs: string;\n dangerousHtml?: string;\n};\n\nconst ESCAPED_ATTR_VALUE_CACHE_LIMIT = 512;\nconst escapedAttrValueCache = new Map<string, string>();\n\n// Fast check for event handler pattern (on + uppercase letter)\nfunction isEventHandler(key: string): boolean {\n return (\n key.length >= 3 &&\n key.charCodeAt(0) === 111 && // 'o'\n key.charCodeAt(1) === 110 && // 'n'\n key.charCodeAt(2) >= 65 &&\n key.charCodeAt(2) <= 90 // 'A'-'Z'\n );\n}\n\nfunction getEscapedAttrValue(value: string): string {\n if (value.length > 64) {\n return escapeAttr(value);\n }\n\n const cached = escapedAttrValueCache.get(value);\n if (cached !== undefined) {\n return cached;\n }\n\n const escaped = escapeAttr(value);\n // Skip caching strings that required no escaping — same reference returned,\n // no point occupying cache slots with identity entries.\n if (escaped === value) return value;\n if (escapedAttrValueCache.size >= ESCAPED_ATTR_VALUE_CACHE_LIMIT) {\n escapedAttrValueCache.clear();\n }\n escapedAttrValueCache.set(value, escaped);\n return escaped;\n}\n\n/**\n * Render attributes directly to a sink without intermediate string allocations.\n * This is the hot path for streaming SSR.\n */\nexport function renderAttrsDirect(\n props: Props | undefined,\n sink: Pick<RenderSink, 'write'>\n): void {\n if (!props || typeof props !== 'object') return;\n\n const propsObj = props as Record<string, unknown>;\n for (const key in propsObj) {\n const value = propsObj[key];\n\n // Skip special props\n if (\n key === 'children' ||\n key === 'key' ||\n key === 'ref' ||\n key === 'dangerouslySetInnerHTML'\n )\n continue;\n\n // Skip event handlers\n if (isEventHandler(key)) continue;\n\n // Skip internal props\n if (key.charCodeAt(0) === 95) continue; // '_'\n\n // Normalize public JSX prop names to their rendered HTML attribute names.\n const attrName = getPublicAttributeName(key);\n\n // Handle style objects\n if (attrName === 'style') {\n const css = typeof value === 'string' ? value : styleObjToCss(value);\n if (!css) continue;\n sink.write(' style=\"');\n // Escape inline - most style values don't need escaping\n if (needsEscapeAttr(css)) {\n sink.write(getEscapedAttrValue(css));\n } else {\n sink.write(css);\n }\n sink.write('\"');\n continue;\n }\n\n // Boolean attributes\n if (value === true) {\n sink.write(' ');\n sink.write(attrName);\n continue;\n }\n\n if (value === false || value === null || value === undefined) continue;\n\n // Regular attributes\n const strValue = String(value);\n sink.write(' ');\n sink.write(attrName);\n sink.write('=\"');\n // escapeAttr returns the original string when nothing needs escaping,\n // so no separate needsEscapeAttr pre-scan is required.\n sink.write(getEscapedAttrValue(strValue));\n sink.write('\"');\n }\n}\n\n/**\n * Render attributes to HTML string, excluding event handlers\n * Optimized for minimal allocations using push-based approach\n *\n * Returns both the attribute string and any dangerouslySetInnerHTML content.\n */\nexport function renderAttrs(props?: Props): string;\nexport function renderAttrs(\n props: Props | undefined,\n opts: { returnDangerousHtml: true }\n): AttrsResult;\nexport function renderAttrs(\n props?: Props,\n opts?: { returnDangerousHtml?: boolean }\n): string | AttrsResult {\n if (!props || typeof props !== 'object') {\n return opts?.returnDangerousHtml ? { attrs: '' } : '';\n }\n\n const attrParts: string[] = [];\n let dangerousHtml: string | undefined;\n\n const propsObj = props as Record<string, unknown>;\n for (const key in propsObj) {\n const value = propsObj[key];\n\n // Skip children in attrs\n if (key === 'children') continue;\n\n // Skip internal identity refs (framework-only)\n if (key === 'key' || key === 'ref') continue;\n\n // Handle dangerouslySetInnerHTML\n if (key === 'dangerouslySetInnerHTML') {\n if (value && typeof value === 'object' && '__html' in (value as object)) {\n dangerousHtml = String((value as { __html: unknown }).__html);\n }\n continue;\n }\n\n // Skip event handlers\n if (isEventHandler(key)) continue;\n\n // Skip internal props\n if (key.charCodeAt(0) === 95) continue; // '_'\n\n // Normalize public JSX prop names to their rendered HTML attribute names.\n const attrName = key === 'class' ? 'class' : getPublicAttributeName(key);\n\n // Handle style objects\n if (attrName === 'style') {\n const css = typeof value === 'string' ? value : styleObjToCss(value);\n if (css === null || css === '') continue;\n attrParts.push(` style=\"${getEscapedAttrValue(css)}\"`);\n continue;\n }\n\n // Boolean attributes\n if (value === true) {\n attrParts.push(` ${attrName}`);\n } else if (value === false || value === null || value === undefined) {\n continue;\n } else {\n const strValue = String(value);\n attrParts.push(` ${attrName}=\"${getEscapedAttrValue(strValue)}\"`);\n }\n }\n\n const result = attrParts.join('');\n\n if (opts?.returnDangerousHtml) {\n return { attrs: result, dangerousHtml };\n }\n return result;\n}\n"],"mappings":"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"}
@@ -12,6 +12,7 @@ interface RenderContext {
12
12
  routeAuth?: RouteAuthOptions;
13
13
  signal?: AbortSignal;
14
14
  queryCache?: Map<string, unknown>;
15
+ ssrCleanupFns: Array<() => void>;
15
16
  keyCounter: number;
16
17
  renderData: Record<string, unknown> | null;
17
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","names":[],"sources":["../../src/ssr/context.ts"],"mappings":";;;;;UAgBiB,aAAA;EACf,GAAA;EACA,IAAA;EACA,IAAA,GAAO,OAAA;EACP,MAAA,GAAS,MAAA;EACT,MAAA,YAAkB,KAAA;EAClB,SAAA,GAAY,gBAAA;EACZ,MAAA,GAAS,WAAA;EACT,UAAA,GAAa,GAAA;EAEb,UAAA;EACA,UAAA,EAAY,MAAA;AAAA;AAAA,KAIF,UAAA,GAAa,aAAA;AAAA,iBAoDT,mBAAA,CACd,IAAA,WACA,IAAA;EACE,GAAA;EACA,IAAA,GAAO,OAAA;EACP,MAAA,GAAS,MAAA;EACT,MAAA,YAAkB,KAAA;EAClB,SAAA,GAAY,gBAAA;EACZ,MAAA,GAAS,WAAA;AAAA,IAEV,aAAA;;;;;iBAqBa,iBAAA,GAAA,CAAqB,GAAA,EAAK,aAAA,EAAe,EAAA,QAAU,CAAA,GAAI,CAAA;AAnFvE;;;;AAAA,iBAsGgB,gBAAA,CAAA,GAAoB,aAAA;AAAA,cASvB,aAAA,SAAa,gBAAA;AAAA,cACb,cAAA,SAAc,iBAAA;AAAA,cACd,oBAAA,SAAoB,gBAAA;AAAA,iBAEjB,iBAAA,GAAA,CAAqB,GAAA,EAAK,aAAA,EAAe,EAAA,QAAU,CAAA,GAAI,CAAA;;;;;iBASvD,mBAAA,CAAA"}
1
+ {"version":3,"file":"context.d.ts","names":[],"sources":["../../src/ssr/context.ts"],"mappings":";;;;;UAgBiB,aAAA;EACf,GAAA;EACA,IAAA;EACA,IAAA,GAAO,OAAA;EACP,MAAA,GAAS,MAAA;EACT,MAAA,YAAkB,KAAA;EAClB,SAAA,GAAY,gBAAA;EACZ,MAAA,GAAS,WAAA;EACT,UAAA,GAAa,GAAA;EACb,aAAA,EAAe,KAAA;EAEf,UAAA;EACA,UAAA,EAAY,MAAA;AAAA;AAAA,KAIF,UAAA,GAAa,aAAa;AAAA,iBAoDtB,mBAAA,CACd,IAAA,WACA,IAAA;EACE,GAAA;EACA,IAAA,GAAO,OAAA;EACP,MAAA,GAAS,MAAA;EACT,MAAA,YAAkB,KAAA;EAClB,SAAA,GAAY,gBAAA;EACZ,MAAA,GAAS,WAAA;AAAA,IAEV,aAAA;;;;;iBAsBa,iBAAA,GAAA,CAAqB,GAAA,EAAK,aAAA,EAAe,EAAA,QAAU,CAAA,GAAI,CAAA;;AAxFnD;AAIpB;;iBAuGgB,gBAAA,CAAA,GAAoB,aAAa;AAAA,cASpC,aAAA,SAAa,gBAAmB;AAAA,cAChC,cAAA,SAAc,iBAAoB;AAAA,cAClC,oBAAA,SAAoB,gBAAmB;AAAA,iBAEpC,iBAAA,GAAA,CAAqB,GAAA,EAAK,aAAA,EAAe,EAAA,QAAU,CAAA,GAAI,CAAA;;;;;iBASvD,mBAAA,CAAA"}
@@ -42,6 +42,7 @@ function createRenderContext(seed = 12345, opts = {}) {
42
42
  routeAuth: opts.routeAuth,
43
43
  signal: opts.signal,
44
44
  queryCache: /* @__PURE__ */ new Map(),
45
+ ssrCleanupFns: [],
45
46
  keyCounter: 0,
46
47
  renderData: null
47
48
  };
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../../src/ssr/context.ts"],"sourcesContent":["/**\n * SSR Context Management\n *\n * Provides render-context storage for server-side rendering.\n * Node SSR lazily installs AsyncLocalStorage on first use; browser builds use\n * the fallback stack.\n */\n\nimport { SSRDataMissingError } from './errors';\nimport { clearEscapeCache } from './escape';\n\nexport type { SSRData } from '../common/ssr';\nimport type { SSRData } from '../common/ssr';\nimport type { Route, RouteAuthOptions } from '../common/router';\n\n// Unified per-render context combining SSRContext and RenderContext\nexport interface RenderContext {\n url: string;\n seed: number;\n data?: SSRData;\n params?: Record<string, string>;\n routes?: readonly Route[];\n routeAuth?: RouteAuthOptions;\n signal?: AbortSignal;\n queryCache?: Map<string, unknown>;\n // Per-render key state (moved from render-keys.ts globals)\n keyCounter: number;\n renderData: Record<string, unknown> | null;\n}\n\n// Legacy alias for compatibility\nexport type SSRContext = RenderContext;\n\ntype RenderContextAccessor = {\n getStore(): RenderContext | undefined;\n run<R>(store: RenderContext, fn: () => R): R;\n};\n\ntype AsyncHooksModule = {\n AsyncLocalStorage?: new () => RenderContextAccessor;\n};\n\nlet renderContextAccessor: RenderContextAccessor | null = null;\nlet renderContextAccessorInitialized = false;\n\n// Fallback stack for non-Node environments\nlet fallbackStack: RenderContext | null = null;\n\nfunction ensureRenderContextAccessor(): void {\n if (renderContextAccessorInitialized) {\n return;\n }\n\n renderContextAccessorInitialized = true;\n\n if (typeof process === 'undefined' || !process.versions?.node) {\n return;\n }\n\n try {\n // Hide the Node builtin from browser dependency scanners.\n // eslint-disable-next-line @typescript-eslint/no-implied-eval\n const loadAsyncHooks = new Function(\n 'return require(' + JSON.stringify('async_hooks') + ');'\n ) as () => AsyncHooksModule;\n const asyncHooks = loadAsyncHooks();\n\n if (asyncHooks.AsyncLocalStorage) {\n const asyncLocalStorage = new asyncHooks.AsyncLocalStorage();\n renderContextAccessor = {\n getStore() {\n return asyncLocalStorage.getStore();\n },\n run<R>(store: RenderContext, fn: () => R): R {\n return asyncLocalStorage.run(store, fn);\n },\n };\n }\n } catch {\n // Keep the fallback stack when async_hooks is unavailable.\n }\n}\n\nexport function createRenderContext(\n seed = 12345,\n opts: {\n url?: string;\n data?: SSRData;\n params?: Record<string, string>;\n routes?: readonly Route[];\n routeAuth?: RouteAuthOptions;\n signal?: AbortSignal;\n } = {}\n): RenderContext {\n clearEscapeCache();\n\n return {\n url: opts.url ?? '',\n seed,\n data: opts.data,\n params: opts.params,\n routes: opts.routes,\n routeAuth: opts.routeAuth,\n signal: opts.signal,\n queryCache: new Map<string, unknown>(),\n keyCounter: 0,\n renderData: null,\n };\n}\n\n/**\n * Run a function with the given render context.\n * Concurrency-safe in Node.js via AsyncLocalStorage.\n */\nexport function withRenderContext<T>(ctx: RenderContext, fn: () => T): T {\n ensureRenderContextAccessor();\n if (renderContextAccessor) {\n return renderContextAccessor.run(ctx, fn);\n }\n // Fallback: stack-based (not concurrency-safe)\n const prev = fallbackStack;\n fallbackStack = ctx;\n try {\n return fn();\n } finally {\n fallbackStack = prev;\n }\n}\n\n/**\n * Get the current render context.\n * Returns null if not inside a render.\n */\nexport function getRenderContext(): RenderContext | null {\n ensureRenderContextAccessor();\n if (renderContextAccessor) {\n return renderContextAccessor.getStore() ?? null;\n }\n return fallbackStack;\n}\n\n// Legacy API aliases (deprecated, for backwards compatibility)\nexport const getSSRContext = getRenderContext;\nexport const withSSRContext = withRenderContext;\nexport const getCurrentSSRContext = getRenderContext;\n\nexport function runWithSSRContext<T>(ctx: RenderContext, fn: () => T): T {\n // This was a separate path for sync detection; now unified\n return withRenderContext(ctx, fn);\n}\n\n/**\n * Centralized SSR enforcement helper — throws a consistent error when async\n * data is encountered during synchronous SSR.\n */\nexport function throwSSRDataMissing(): never {\n throw new SSRDataMissingError();\n}\n\nexport { SSRDataMissingError };\n"],"mappings":";;;;;;;;;;;AA0CA,IAAI,wBAAsD;AAC1D,IAAI,mCAAmC;AAGvC,IAAI,gBAAsC;AAE1C,SAAS,8BAAoC;CAC3C,IAAI,kCACF;CAGF,mCAAmC;CAEnC,IAAI,OAAO,YAAY,eAAe,CAAC,QAAQ,UAAU,MACvD;CAGF,IAAI;EAMF,MAAM,aAAa,IAHQ,SACzB,oBAAoB,KAAK,UAAU,cAAc,GAAG,KAEnC,EAAgB;EAEnC,IAAI,WAAW,mBAAmB;GAChC,MAAM,oBAAoB,IAAI,WAAW,mBAAmB;GAC5D,wBAAwB;IACtB,WAAW;KACT,OAAO,kBAAkB,UAAU;;IAErC,IAAO,OAAsB,IAAgB;KAC3C,OAAO,kBAAkB,IAAI,OAAO,GAAG;;IAE1C;;SAEG;;AAKV,SAAgB,oBACd,OAAO,OACP,OAOI,EAAE,EACS;CACf,kBAAkB;CAElB,OAAO;EACL,KAAK,KAAK,OAAO;EACjB;EACA,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,QAAQ,KAAK;EACb,WAAW,KAAK;EAChB,QAAQ,KAAK;EACb,4BAAY,IAAI,KAAsB;EACtC,YAAY;EACZ,YAAY;EACb;;;;;;AAOH,SAAgB,kBAAqB,KAAoB,IAAgB;CACvE,6BAA6B;CAC7B,IAAI,uBACF,OAAO,sBAAsB,IAAI,KAAK,GAAG;CAG3C,MAAM,OAAO;CACb,gBAAgB;CAChB,IAAI;EACF,OAAO,IAAI;WACH;EACR,gBAAgB;;;;;;;AAQpB,SAAgB,mBAAyC;CACvD,6BAA6B;CAC7B,IAAI,uBACF,OAAO,sBAAsB,UAAU,IAAI;CAE7C,OAAO;;;;;;AAiBT,SAAgB,sBAA6B;CAC3C,MAAM,IAAI,qBAAqB"}
1
+ {"version":3,"file":"context.js","names":[],"sources":["../../src/ssr/context.ts"],"sourcesContent":["/**\n * SSR Context Management\n *\n * Provides render-context storage for server-side rendering.\n * Node SSR lazily installs AsyncLocalStorage on first use; browser builds use\n * the fallback stack.\n */\n\nimport { SSRDataMissingError } from './errors';\nimport { clearEscapeCache } from './escape';\n\nexport type { SSRData } from '../common/ssr';\nimport type { SSRData } from '../common/ssr';\nimport type { Route, RouteAuthOptions } from '../common/router';\n\n// Unified per-render context combining SSRContext and RenderContext\nexport interface RenderContext {\n url: string;\n seed: number;\n data?: SSRData;\n params?: Record<string, string>;\n routes?: readonly Route[];\n routeAuth?: RouteAuthOptions;\n signal?: AbortSignal;\n queryCache?: Map<string, unknown>;\n ssrCleanupFns: Array<() => void>;\n // Per-render key state (moved from render-keys.ts globals)\n keyCounter: number;\n renderData: Record<string, unknown> | null;\n}\n\n// Legacy alias for compatibility\nexport type SSRContext = RenderContext;\n\ntype RenderContextAccessor = {\n getStore(): RenderContext | undefined;\n run<R>(store: RenderContext, fn: () => R): R;\n};\n\ntype AsyncHooksModule = {\n AsyncLocalStorage?: new () => RenderContextAccessor;\n};\n\nlet renderContextAccessor: RenderContextAccessor | null = null;\nlet renderContextAccessorInitialized = false;\n\n// Fallback stack for non-Node environments\nlet fallbackStack: RenderContext | null = null;\n\nfunction ensureRenderContextAccessor(): void {\n if (renderContextAccessorInitialized) {\n return;\n }\n\n renderContextAccessorInitialized = true;\n\n if (typeof process === 'undefined' || !process.versions?.node) {\n return;\n }\n\n try {\n // Hide the Node builtin from browser dependency scanners.\n // eslint-disable-next-line @typescript-eslint/no-implied-eval\n const loadAsyncHooks = new Function(\n 'return require(' + JSON.stringify('async_hooks') + ');'\n ) as () => AsyncHooksModule;\n const asyncHooks = loadAsyncHooks();\n\n if (asyncHooks.AsyncLocalStorage) {\n const asyncLocalStorage = new asyncHooks.AsyncLocalStorage();\n renderContextAccessor = {\n getStore() {\n return asyncLocalStorage.getStore();\n },\n run<R>(store: RenderContext, fn: () => R): R {\n return asyncLocalStorage.run(store, fn);\n },\n };\n }\n } catch {\n // Keep the fallback stack when async_hooks is unavailable.\n }\n}\n\nexport function createRenderContext(\n seed = 12345,\n opts: {\n url?: string;\n data?: SSRData;\n params?: Record<string, string>;\n routes?: readonly Route[];\n routeAuth?: RouteAuthOptions;\n signal?: AbortSignal;\n } = {}\n): RenderContext {\n clearEscapeCache();\n\n return {\n url: opts.url ?? '',\n seed,\n data: opts.data,\n params: opts.params,\n routes: opts.routes,\n routeAuth: opts.routeAuth,\n signal: opts.signal,\n queryCache: new Map<string, unknown>(),\n ssrCleanupFns: [],\n keyCounter: 0,\n renderData: null,\n };\n}\n\n/**\n * Run a function with the given render context.\n * Concurrency-safe in Node.js via AsyncLocalStorage.\n */\nexport function withRenderContext<T>(ctx: RenderContext, fn: () => T): T {\n ensureRenderContextAccessor();\n if (renderContextAccessor) {\n return renderContextAccessor.run(ctx, fn);\n }\n // Fallback: stack-based (not concurrency-safe)\n const prev = fallbackStack;\n fallbackStack = ctx;\n try {\n return fn();\n } finally {\n fallbackStack = prev;\n }\n}\n\n/**\n * Get the current render context.\n * Returns null if not inside a render.\n */\nexport function getRenderContext(): RenderContext | null {\n ensureRenderContextAccessor();\n if (renderContextAccessor) {\n return renderContextAccessor.getStore() ?? null;\n }\n return fallbackStack;\n}\n\n// Legacy API aliases (deprecated, for backwards compatibility)\nexport const getSSRContext = getRenderContext;\nexport const withSSRContext = withRenderContext;\nexport const getCurrentSSRContext = getRenderContext;\n\nexport function runWithSSRContext<T>(ctx: RenderContext, fn: () => T): T {\n // This was a separate path for sync detection; now unified\n return withRenderContext(ctx, fn);\n}\n\n/**\n * Centralized SSR enforcement helper — throws a consistent error when async\n * data is encountered during synchronous SSR.\n */\nexport function throwSSRDataMissing(): never {\n throw new SSRDataMissingError();\n}\n\nexport { SSRDataMissingError };\n"],"mappings":";;;;;;;;;;;AA2CA,IAAI,wBAAsD;AAC1D,IAAI,mCAAmC;AAGvC,IAAI,gBAAsC;AAE1C,SAAS,8BAAoC;CAC3C,IAAI,kCACF;CAGF,mCAAmC;CAEnC,IAAI,OAAO,YAAY,eAAe,CAAC,QAAQ,UAAU,MACvD;CAGF,IAAI;EAMF,MAAM,aAAa,IAHQ,SACzB,oBAAoB,KAAK,UAAU,aAAa,IAAI,IAEnC,EAAe;EAElC,IAAI,WAAW,mBAAmB;GAChC,MAAM,oBAAoB,IAAI,WAAW,kBAAkB;GAC3D,wBAAwB;IACtB,WAAW;KACT,OAAO,kBAAkB,SAAS;IACpC;IACA,IAAO,OAAsB,IAAgB;KAC3C,OAAO,kBAAkB,IAAI,OAAO,EAAE;IACxC;GACF;EACF;CACF,QAAQ,CAER;AACF;AAEA,SAAgB,oBACd,OAAO,OACP,OAOI,CAAC,GACU;CACf,iBAAiB;CAEjB,OAAO;EACL,KAAK,KAAK,OAAO;EACjB;EACA,MAAM,KAAK;EACX,QAAQ,KAAK;EACb,QAAQ,KAAK;EACb,WAAW,KAAK;EAChB,QAAQ,KAAK;EACb,4BAAY,IAAI,IAAqB;EACrC,eAAe,CAAC;EAChB,YAAY;EACZ,YAAY;CACd;AACF;;;;;AAMA,SAAgB,kBAAqB,KAAoB,IAAgB;CACvE,4BAA4B;CAC5B,IAAI,uBACF,OAAO,sBAAsB,IAAI,KAAK,EAAE;CAG1C,MAAM,OAAO;CACb,gBAAgB;CAChB,IAAI;EACF,OAAO,GAAG;CACZ,UAAU;EACR,gBAAgB;CAClB;AACF;;;;;AAMA,SAAgB,mBAAyC;CACvD,4BAA4B;CAC5B,IAAI,uBACF,OAAO,sBAAsB,SAAS,KAAK;CAE7C,OAAO;AACT;;;;;AAgBA,SAAgB,sBAA6B;CAC3C,MAAM,IAAI,oBAAoB;AAChC"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/ssr/errors.ts"],"mappings":";;;cAEa,iBAAA,SAA0B,KAAA;EAAA,SAC5B,IAAA;cACG,OAAA;AAAA"}
1
+ {"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/ssr/errors.ts"],"mappings":";;;cAEa,iBAAA,SAA0B,KAAK;EAAA,SACjC,IAAA;cACG,OAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"escape.js","names":[],"sources":["../../src/ssr/escape.ts"],"sourcesContent":["/**\n * HTML escaping utilities for SSR\n *\n * Centralizes text and attribute escaping to avoid duplication\n * between sync and streaming SSR renderers.\n */\n\n// HTML5 void elements that don't have closing tags\nexport const VOID_ELEMENTS = new Set([\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr',\n]);\n\n// Escape cache for common values (bounded and clearable for long-running servers)\nconst escapeCache = new Map<string, string>();\nconst MAX_CACHE_SIZE = 256;\n\nconst _TEXT_ESCAPE_RE = /[&<>]/g;\n\nconst CSS_UNSAFE_RE = /[{}<>\\\\]/g;\nconst CSS_URI_SCHEME_RE = /(?:^|[\\s(,])([a-z][a-z0-9+.-]*):/i;\nconst CSS_FUNCTION_NAME_RE = /([a-z-][a-z0-9-]*)\\s*\\(/gi;\n\nconst CSS_ALLOWED_FUNCTIONS = new Set([\n 'var',\n 'calc',\n 'min',\n 'max',\n 'clamp',\n 'rgb',\n 'rgba',\n 'hsl',\n 'hsla',\n 'lab',\n 'lch',\n 'oklab',\n 'oklch',\n 'color',\n 'color-mix',\n 'translate',\n 'translatex',\n 'translatey',\n 'translatez',\n 'scale',\n 'scalex',\n 'scaley',\n 'scalez',\n 'rotate',\n 'rotatex',\n 'rotatey',\n 'rotatez',\n 'skew',\n 'skewx',\n 'skewy',\n 'matrix',\n 'matrix3d',\n 'linear-gradient',\n 'radial-gradient',\n 'conic-gradient',\n 'repeating-linear-gradient',\n 'repeating-radial-gradient',\n 'repeating-conic-gradient',\n 'cubic-bezier',\n 'steps',\n]);\n\nconst STYLE_PROP_CACHE = new Map<string, string>();\nconst MAX_STYLE_PROP_CACHE_SIZE = 512;\n\n// Pre-compute escape map functions for faster replacement\nconst _textEscapeMap = (ch: string): string => {\n const code = ch.charCodeAt(0);\n if (code === 38) return '&amp;'; // &\n if (code === 60) return '&lt;'; // <\n if (code === 62) return '&gt;'; // >\n return ch;\n};\n\nfunction toKebabCached(prop: string): string {\n const cached = STYLE_PROP_CACHE.get(prop);\n if (cached !== undefined) return cached;\n const kebab = prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);\n if (STYLE_PROP_CACHE.size < MAX_STYLE_PROP_CACHE_SIZE) {\n STYLE_PROP_CACHE.set(prop, kebab);\n }\n return kebab;\n}\n\n/**\n * Clear the escape cache. Call between SSR requests in long-running servers\n * to prevent memory buildup from unique strings.\n */\nexport function clearEscapeCache(): void {\n escapeCache.clear();\n}\n\n/**\n * Fast check if a string needs text escaping.\n * Used to skip the full escape call when not needed.\n */\nexport function needsEscapeText(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n const ch = text.charCodeAt(i);\n if (ch === 38 || ch === 60 || ch === 62) return true;\n }\n return false;\n}\n\n/**\n * Fast check if a string needs attribute escaping.\n * Checks for & \" ' < > characters.\n */\nexport function needsEscapeAttr(value: string): boolean {\n for (let i = 0; i < value.length; i++) {\n const ch = value.charCodeAt(i);\n // & \" ' < >\n if (ch === 38 || ch === 34 || ch === 39 || ch === 60 || ch === 62) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Escape HTML special characters in text content\n * Fast-path: cache first, then scan-then-escape for new strings\n */\nexport function escapeText(text: string): string {\n // Only use cache for short strings (likely to be repeated)\n const useCache = text.length <= 64;\n\n if (useCache) {\n const cached = escapeCache.get(text);\n if (cached !== undefined) return cached;\n }\n\n // Fast path: check if escaping needed\n if (!needsEscapeText(text)) {\n if (useCache && escapeCache.size < MAX_CACHE_SIZE) {\n escapeCache.set(text, text);\n }\n return text;\n }\n\n // Single-pass scan-and-build: avoids 3 separate regex passes\n let escaped = '';\n let lastIndex = 0;\n for (let i = 0; i < text.length; i++) {\n const ch = text.charCodeAt(i);\n let entity: string;\n if (ch === 38) entity = '&amp;';\n else if (ch === 60) entity = '&lt;';\n else if (ch === 62) entity = '&gt;';\n else continue;\n escaped += text.slice(lastIndex, i) + entity;\n lastIndex = i + 1;\n }\n const result = escaped + text.slice(lastIndex);\n\n if (useCache && escapeCache.size < MAX_CACHE_SIZE) {\n escapeCache.set(text, result);\n }\n return result;\n}\n\n/**\n * Escape HTML special characters in attribute values.\n * Single-pass scan: returns the original string unchanged when no special\n * characters are found, so callers can drop separate needsEscapeAttr checks.\n */\nexport function escapeAttr(value: string): string {\n let escaped = '';\n let lastIndex = 0;\n for (let i = 0; i < value.length; i++) {\n const ch = value.charCodeAt(i);\n let entity: string;\n if (ch === 38) entity = '&amp;';\n else if (ch === 34) entity = '&quot;';\n else if (ch === 39) entity = '&#x27;';\n else if (ch === 60) entity = '&lt;';\n else if (ch === 62) entity = '&gt;';\n else continue;\n escaped += value.slice(lastIndex, i) + entity;\n lastIndex = i + 1;\n }\n // Return original reference when nothing needed escaping (no allocation)\n return lastIndex === 0 ? value : escaped + value.slice(lastIndex);\n}\n\n/**\n * Escape CSS value to prevent injection attacks.\n * Removes characters that could break out of CSS context.\n */\nfunction escapeCssValue(value: string): string {\n const str = String(value);\n\n if (CSS_URI_SCHEME_RE.test(str)) {\n return '';\n }\n\n if (str.includes('(')) {\n for (const match of str.matchAll(CSS_FUNCTION_NAME_RE)) {\n if (!CSS_ALLOWED_FUNCTIONS.has(match[1].toLowerCase())) {\n return '';\n }\n }\n }\n\n // Remove characters that could break out of CSS value context\n return str.replace(CSS_UNSAFE_RE, '');\n}\n\n/**\n * Convert style object to CSS string with value escaping\n * Optimized to avoid Object.entries allocation\n */\nexport function styleObjToCss(value: unknown): string | null {\n if (!value || typeof value !== 'object') return null;\n\n const styleObj = value as Record<string, unknown>;\n let result = '';\n\n for (const k in styleObj) {\n const v = styleObj[k];\n if (v === null || v === undefined || v === false) continue;\n\n const prop = toKebabCached(k);\n const safeValue = escapeCssValue(String(v));\n if (safeValue) {\n result += `${prop}:${safeValue};`;\n }\n }\n\n return result || null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"escape.js","names":[],"sources":["../../src/ssr/escape.ts"],"sourcesContent":["/**\n * HTML escaping utilities for SSR\n *\n * Centralizes text and attribute escaping to avoid duplication\n * between sync and streaming SSR renderers.\n */\n\n// HTML5 void elements that don't have closing tags\nexport const VOID_ELEMENTS = new Set([\n 'area',\n 'base',\n 'br',\n 'col',\n 'embed',\n 'hr',\n 'img',\n 'input',\n 'link',\n 'meta',\n 'param',\n 'source',\n 'track',\n 'wbr',\n]);\n\n// Escape cache for common values (bounded and clearable for long-running servers)\nconst escapeCache = new Map<string, string>();\nconst MAX_CACHE_SIZE = 256;\n\nconst _TEXT_ESCAPE_RE = /[&<>]/g;\n\nconst CSS_UNSAFE_RE = /[{}<>\\\\]/g;\nconst CSS_URI_SCHEME_RE = /(?:^|[\\s(,])([a-z][a-z0-9+.-]*):/i;\nconst CSS_FUNCTION_NAME_RE = /([a-z-][a-z0-9-]*)\\s*\\(/gi;\n\nconst CSS_ALLOWED_FUNCTIONS = new Set([\n 'var',\n 'calc',\n 'min',\n 'max',\n 'clamp',\n 'rgb',\n 'rgba',\n 'hsl',\n 'hsla',\n 'lab',\n 'lch',\n 'oklab',\n 'oklch',\n 'color',\n 'color-mix',\n 'translate',\n 'translatex',\n 'translatey',\n 'translatez',\n 'scale',\n 'scalex',\n 'scaley',\n 'scalez',\n 'rotate',\n 'rotatex',\n 'rotatey',\n 'rotatez',\n 'skew',\n 'skewx',\n 'skewy',\n 'matrix',\n 'matrix3d',\n 'linear-gradient',\n 'radial-gradient',\n 'conic-gradient',\n 'repeating-linear-gradient',\n 'repeating-radial-gradient',\n 'repeating-conic-gradient',\n 'cubic-bezier',\n 'steps',\n]);\n\nconst STYLE_PROP_CACHE = new Map<string, string>();\nconst MAX_STYLE_PROP_CACHE_SIZE = 512;\n\n// Pre-compute escape map functions for faster replacement\nconst _textEscapeMap = (ch: string): string => {\n const code = ch.charCodeAt(0);\n if (code === 38) return '&amp;'; // &\n if (code === 60) return '&lt;'; // <\n if (code === 62) return '&gt;'; // >\n return ch;\n};\n\nfunction toKebabCached(prop: string): string {\n const cached = STYLE_PROP_CACHE.get(prop);\n if (cached !== undefined) return cached;\n const kebab = prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);\n if (STYLE_PROP_CACHE.size < MAX_STYLE_PROP_CACHE_SIZE) {\n STYLE_PROP_CACHE.set(prop, kebab);\n }\n return kebab;\n}\n\n/**\n * Clear the escape cache. Call between SSR requests in long-running servers\n * to prevent memory buildup from unique strings.\n */\nexport function clearEscapeCache(): void {\n escapeCache.clear();\n}\n\n/**\n * Fast check if a string needs text escaping.\n * Used to skip the full escape call when not needed.\n */\nexport function needsEscapeText(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n const ch = text.charCodeAt(i);\n if (ch === 38 || ch === 60 || ch === 62) return true;\n }\n return false;\n}\n\n/**\n * Fast check if a string needs attribute escaping.\n * Checks for & \" ' < > characters.\n */\nexport function needsEscapeAttr(value: string): boolean {\n for (let i = 0; i < value.length; i++) {\n const ch = value.charCodeAt(i);\n // & \" ' < >\n if (ch === 38 || ch === 34 || ch === 39 || ch === 60 || ch === 62) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Escape HTML special characters in text content\n * Fast-path: cache first, then scan-then-escape for new strings\n */\nexport function escapeText(text: string): string {\n // Only use cache for short strings (likely to be repeated)\n const useCache = text.length <= 64;\n\n if (useCache) {\n const cached = escapeCache.get(text);\n if (cached !== undefined) return cached;\n }\n\n // Fast path: check if escaping needed\n if (!needsEscapeText(text)) {\n if (useCache && escapeCache.size < MAX_CACHE_SIZE) {\n escapeCache.set(text, text);\n }\n return text;\n }\n\n // Single-pass scan-and-build: avoids 3 separate regex passes\n let escaped = '';\n let lastIndex = 0;\n for (let i = 0; i < text.length; i++) {\n const ch = text.charCodeAt(i);\n let entity: string;\n if (ch === 38) entity = '&amp;';\n else if (ch === 60) entity = '&lt;';\n else if (ch === 62) entity = '&gt;';\n else continue;\n escaped += text.slice(lastIndex, i) + entity;\n lastIndex = i + 1;\n }\n const result = escaped + text.slice(lastIndex);\n\n if (useCache && escapeCache.size < MAX_CACHE_SIZE) {\n escapeCache.set(text, result);\n }\n return result;\n}\n\n/**\n * Escape HTML special characters in attribute values.\n * Single-pass scan: returns the original string unchanged when no special\n * characters are found, so callers can drop separate needsEscapeAttr checks.\n */\nexport function escapeAttr(value: string): string {\n let escaped = '';\n let lastIndex = 0;\n for (let i = 0; i < value.length; i++) {\n const ch = value.charCodeAt(i);\n let entity: string;\n if (ch === 38) entity = '&amp;';\n else if (ch === 34) entity = '&quot;';\n else if (ch === 39) entity = '&#x27;';\n else if (ch === 60) entity = '&lt;';\n else if (ch === 62) entity = '&gt;';\n else continue;\n escaped += value.slice(lastIndex, i) + entity;\n lastIndex = i + 1;\n }\n // Return original reference when nothing needed escaping (no allocation)\n return lastIndex === 0 ? value : escaped + value.slice(lastIndex);\n}\n\n/**\n * Escape CSS value to prevent injection attacks.\n * Removes characters that could break out of CSS context.\n */\nfunction escapeCssValue(value: string): string {\n const str = String(value);\n\n if (CSS_URI_SCHEME_RE.test(str)) {\n return '';\n }\n\n if (str.includes('(')) {\n for (const match of str.matchAll(CSS_FUNCTION_NAME_RE)) {\n if (!CSS_ALLOWED_FUNCTIONS.has(match[1].toLowerCase())) {\n return '';\n }\n }\n }\n\n // Remove characters that could break out of CSS value context\n return str.replace(CSS_UNSAFE_RE, '');\n}\n\n/**\n * Convert style object to CSS string with value escaping\n * Optimized to avoid Object.entries allocation\n */\nexport function styleObjToCss(value: unknown): string | null {\n if (!value || typeof value !== 'object') return null;\n\n const styleObj = value as Record<string, unknown>;\n let result = '';\n\n for (const k in styleObj) {\n const v = styleObj[k];\n if (v === null || v === undefined || v === false) continue;\n\n const prop = toKebabCached(k);\n const safeValue = escapeCssValue(String(v));\n if (safeValue) {\n result += `${prop}:${safeValue};`;\n }\n }\n\n return result || null;\n}\n"],"mappings":"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"}
@@ -14,24 +14,20 @@ declare function renderToStringSync(component: (props?: Record<string, unknown>)
14
14
  seed?: number;
15
15
  data?: SSRData;
16
16
  }): string;
17
- declare function renderResolvedToStringSync(opts: {
17
+ declare function resolveRequest(opts: {
18
18
  url: string;
19
- routes: Array<{
19
+ manifest: RouteManifest;
20
+ routes?: Array<{
20
21
  path: string;
21
22
  handler: RouteHandler;
22
23
  namespace?: string;
23
24
  }>;
24
- handler: RouteHandler;
25
- params?: Record<string, string>;
26
- options?: {
27
- seed?: number;
28
- data?: SSRData;
29
- };
30
- }): string;
31
- declare function resolveRequest(opts: {
25
+ auth?: RouteAuthOptions;
26
+ signal?: AbortSignal;
27
+ } | {
32
28
  url: string;
33
29
  manifest?: RouteManifest;
34
- routes?: Array<{
30
+ routes: Array<{
35
31
  path: string;
36
32
  handler: RouteHandler;
37
33
  namespace?: string;
@@ -60,5 +56,5 @@ declare function renderToStream(opts: {
60
56
  onComplete(): void;
61
57
  }): void;
62
58
  //#endregion
63
- export { type SSRComponent, SSRDataMissingError, SSRRoute, type VNode, renderResolvedToStringSync, renderToStream, renderToString, renderToStringSync, resolveRequest };
59
+ export { type SSRComponent, SSRDataMissingError, SSRRoute, type VNode, renderToStream, renderToString, renderToStringSync, resolveRequest };
64
60
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/ssr/index.ts"],"mappings":";;;;;;;;;;;;iBA4nCgB,kBAAA,CACd,SAAA,GACE,KAAA,GAAQ,MAAA,sBACL,KAAA,GAAQ,UAAA,iDACb,KAAA,GAAQ,MAAA,mBACR,OAAA;EAAY,IAAA;EAAe,IAAA,GAAO,OAAA;AAAA;AAAA,iBA2BpB,0BAAA,CAA2B,IAAA;EACzC,GAAA;EACA,MAAA,EAAQ,KAAA;IAAQ,IAAA;IAAc,OAAA,EAAS,YAAA;IAAc,SAAA;EAAA;EACrD,OAAA,EAAS,YAAA;EACT,MAAA,GAAS,MAAA;EACT,OAAA;IAAY,IAAA;IAAe,IAAA,GAAO,OAAA;EAAA;AAAA;AAAA,iBA6Bd,cAAA,CAAe,IAAA;EACnC,GAAA;EACA,QAAA,GAAW,aAAA;EACX,MAAA,GAAS,KAAA;IAAQ,IAAA;IAAc,OAAA,EAAS,YAAA;IAAc,SAAA;EAAA;EACtD,IAAA,GAAO,gBAAA;EACP,MAAA,GAAS,WAAA;AAAA,IACP,OAAA,CAAQ,kBAAA;AAAA,KAqCA,QAAA;EACV,IAAA;EACA,OAAA,EAAS,YAAA;EACT,SAAA;AAAA;AAAA,iBAGc,cAAA,CACd,SAAA,GACE,KAAA,GAAQ,MAAA,sBACL,KAAA,GAAQ,UAAA;AAAA,iBAEC,cAAA,CAAe,IAAA;EAC7B,GAAA;EACA,MAAA,EAAQ,QAAA;EACR,IAAA;EACA,IAAA,GAAO,OAAA;AAAA;AAAA,iBAuBO,cAAA,CAAe,IAAA;EAC7B,GAAA;EACA,MAAA,EAAQ,QAAA;EACR,IAAA;EACA,IAAA,GAAO,OAAA;EACP,OAAA,CAAQ,IAAA;EACR,UAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/ssr/index.ts"],"mappings":";;;;;;;;;;;;iBAqsCgB,kBAAA,CACd,SAAA,GACE,KAAA,GAAQ,MAAA,sBACL,KAAA,GAAQ,UAAA,iDACb,KAAA,GAAQ,MAAA,mBACR,OAAA;EAAY,IAAA;EAAe,IAAA,GAAO,OAAA;AAAA;AAAA,iBAgCd,cAAA,CACpB,IAAA;EAEM,GAAA;EACA,QAAA,EAAU,aAAA;EACV,MAAA,GAAS,KAAA;IACP,IAAA;IACA,OAAA,EAAS,YAAA;IACT,SAAA;EAAA;EAEF,IAAA,GAAO,gBAAA;EACP,MAAA,GAAS,WAAA;AAAA;EAGT,GAAA;EACA,QAAA,GAAW,aAAA;EACX,MAAA,EAAQ,KAAA;IACN,IAAA;IACA,OAAA,EAAS,YAAA;IACT,SAAA;EAAA;EAEF,IAAA,GAAO,gBAAA;EACP,MAAA,GAAS,WAAA;AAAA,IAEd,OAAA,CAAQ,kBAAA;AAAA,KAqCC,QAAA;EACV,IAAA;EACA,OAAA,EAAS,YAAY;EACrB,SAAA;AAAA;AAAA,iBAGc,cAAA,CACd,SAAA,GACE,KAAA,GAAQ,MAAA,sBACL,KAAA,GAAQ,UAAA;AAAA,iBAEC,cAAA,CAAe,IAAA;EAC7B,GAAA;EACA,MAAA,EAAQ,QAAA;EACR,IAAA;EACA,IAAA,GAAO,OAAO;AAAA;AAAA,iBAuBA,cAAA,CAAe,IAAA;EAC7B,GAAA;EACA,MAAA,EAAQ,QAAA;EACR,IAAA;EACA,IAAA,GAAO,OAAO;EACd,OAAA,CAAQ,IAAA;EACR,UAAA;AAAA"}
package/dist/ssr/index.js CHANGED
@@ -2,7 +2,9 @@ import { __CONTROL_BOUNDARY__ } from "../common/control.js";
2
2
  import { __ERROR_BOUNDARY__ } from "../common/vnode.js";
3
3
  import { logger } from "../dev/logger.js";
4
4
  import { ELEMENT_TYPE, Fragment } from "../common/jsx.js";
5
- import { createComponentInstance, getCurrentComponentInstance, setCurrentComponentInstance } from "../runtime/component.js";
5
+ import { isPromiseLike } from "../common/promise.js";
6
+ import { getPublicAttributeName } from "../common/attr-names.js";
7
+ import { cleanupComponent, createComponentInstance, getCurrentComponentInstance, setCurrentComponentInstance } from "../runtime/component.js";
6
8
  import { evaluateForState } from "../runtime/for.js";
7
9
  import { evaluateCaseState, evaluateShowState } from "../runtime/control.js";
8
10
  import { SSRDataMissingError } from "../common/ssr-errors.js";
@@ -10,9 +12,10 @@ import { VOID_ELEMENTS, escapeText, styleObjToCss } from "./escape.js";
10
12
  import { createRenderContext, getRenderContext, throwSSRDataMissing, withRenderContext } from "./context.js";
11
13
  import { resolveRouteFromRoutes, resolveRouteRequest } from "../router/route.js";
12
14
  import "../jsx/index.js";
13
- import { DefaultPortal } from "../foundations/structures/portal.js";
14
- import { installSSRBridge } from "../runtime/ssr-bridge.js";
15
+ import { DefaultPortal, disposeDefaultPortalScope } from "../foundations/structures/portal.js";
16
+ import { SSR_RENDER_DATA_ATTR } from "../common/ssr.js";
15
17
  import { getCurrentRenderData, getNextKey, startRenderPhase, stopRenderPhase } from "./render-keys.js";
18
+ import { installSSRBridge } from "../runtime/ssr-bridge.js";
16
19
  import { renderAttrs, renderAttrsDirect } from "./attrs.js";
17
20
  import { StreamSink, StringSink } from "./sink.js";
18
21
  //#region src/ssr/index.ts
@@ -80,6 +83,10 @@ function renderChildSyncToSink(child, sink, ctx) {
80
83
  }
81
84
  if (child && typeof child === "object" && "type" in child) renderNodeSyncToSink(child, sink, ctx);
82
85
  }
86
+ function serializeHydrationRenderData(data) {
87
+ if (!data || Object.keys(data).length === 0) return "";
88
+ return `<script type="application/json" ${SSR_RENDER_DATA_ATTR}="true">${JSON.stringify(data).replace(/</g, "\\u003C").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029")}<\/script>`;
89
+ }
83
90
  function renderChildrenSync(children, ctx) {
84
91
  if (!children || !Array.isArray(children) || children.length === 0) return "";
85
92
  if (children.length === 1) return renderChildSync(children[0], ctx);
@@ -232,7 +239,7 @@ function renderNodeSync(node, ctx) {
232
239
  } catch {}
233
240
  if (typeof type === "function") {
234
241
  const result = executeComponentSync(type, props, ctx);
235
- if (result instanceof Promise) throwSSRDataMissing();
242
+ if (isPromiseLike(result)) throwSSRDataMissing();
236
243
  return renderNodeSync(result, ctx);
237
244
  }
238
245
  if (typeof type === "symbol") {
@@ -395,10 +402,26 @@ function executeComponentSync(component, props, ctx) {
395
402
  const prev = getCurrentComponentInstance();
396
403
  const temp = createComponentInstance("ssr-temp", component, props || {}, null);
397
404
  temp.ssr = true;
405
+ temp.portalScope = temp;
406
+ ctx.ssrCleanupFns.push(() => {
407
+ let cleanupError = null;
408
+ try {
409
+ cleanupComponent(temp);
410
+ } catch (error) {
411
+ cleanupError = error;
412
+ }
413
+ try {
414
+ disposeDefaultPortalScope(temp);
415
+ } catch (error) {
416
+ if (cleanupError) throw new AggregateError([cleanupError, error], "SSR temporary owner cleanup failed");
417
+ throw error;
418
+ }
419
+ if (cleanupError) throw cleanupError;
420
+ });
398
421
  setCurrentComponentInstance(temp);
399
422
  try {
400
423
  const result = component(props || {}, { ssr: ctx });
401
- if (result instanceof Promise) throwSSRDataMissing();
424
+ if (isPromiseLike(result)) throwSSRDataMissing();
402
425
  if (typeof result === "string" || typeof result === "number" || typeof result === "boolean" || result === null || result === void 0) {
403
426
  const inner = result === null || result === void 0 || result === false ? "" : String(result);
404
427
  return {
@@ -415,7 +438,20 @@ function executeComponentSync(component, props, ctx) {
415
438
  popSSRStrictPurityGuard();
416
439
  }
417
440
  }
441
+ function disposeSSRTemporaryOwners(ctx) {
442
+ const cleanupFns = ctx.ssrCleanupFns;
443
+ ctx.ssrCleanupFns = [];
444
+ const cleanupErrors = [];
445
+ for (let index = cleanupFns.length - 1; index >= 0; index -= 1) try {
446
+ cleanupFns[index]();
447
+ } catch (error) {
448
+ cleanupErrors.push(error);
449
+ }
450
+ if (cleanupErrors.length === 1) throw cleanupErrors[0];
451
+ if (cleanupErrors.length > 1) throw new AggregateError(cleanupErrors, "SSR temporary owner cleanup failed");
452
+ }
418
453
  function wrapWithDefaultPortal(out) {
454
+ if (isPromiseLike(out)) throwSSRDataMissing();
419
455
  const portalVNode = {
420
456
  $$typeof: ELEMENT_TYPE,
421
457
  type: DefaultPortal,
@@ -465,7 +501,7 @@ function verifyRenderedAttrs(element, props) {
465
501
  continue;
466
502
  }
467
503
  if (isSSRAttrEventHandler(key) || key.charCodeAt(0) === 95) continue;
468
- const attrName = key === "class" || key === "className" ? "class" : key;
504
+ const attrName = key === "class" ? "class" : getPublicAttributeName(key);
469
505
  if (attrName === "style") {
470
506
  const css = typeof value === "string" ? value : styleObjToCss(value);
471
507
  if (!css) continue;
@@ -589,31 +625,15 @@ function renderToStringSync(component, props, options) {
589
625
  if (!node) throw new Error("renderToStringSync: wrapped component returned empty");
590
626
  const sink = new StringSink();
591
627
  renderNodeSyncToSink(node, sink, ctx);
628
+ sink.write(serializeHydrationRenderData(options?.data));
592
629
  sink.end();
593
630
  return sink.toString();
594
631
  } finally {
595
- stopRenderPhase();
596
- }
597
- });
598
- }
599
- function renderResolvedToStringSync(opts) {
600
- const { url, routes, handler, params, options } = opts;
601
- const ctx = createRenderContext(options?.seed ?? 12345, {
602
- url,
603
- data: options?.data,
604
- params,
605
- routes
606
- });
607
- return withRenderContext(ctx, () => {
608
- startRenderPhase(options?.data ?? null);
609
- try {
610
- const node = renderSyncComponentRoot(handler, params || {}, ctx);
611
- const sink = new StringSink();
612
- renderNodeSyncToSink(node, sink, ctx);
613
- sink.end();
614
- return sink.toString();
615
- } finally {
616
- stopRenderPhase();
632
+ try {
633
+ stopRenderPhase();
634
+ } finally {
635
+ disposeSSRTemporaryOwners(ctx);
636
+ }
617
637
  }
618
638
  });
619
639
  }
@@ -668,12 +688,17 @@ function renderToSinkInternal(opts) {
668
688
  startRenderPhase(data || null);
669
689
  try {
670
690
  renderNodeSyncToSink(executeComponentSync(resolved.handler, resolved.params, ctx), sink, ctx);
691
+ sink.write(serializeHydrationRenderData(data));
671
692
  } finally {
672
- stopRenderPhase();
693
+ try {
694
+ stopRenderPhase();
695
+ } finally {
696
+ disposeSSRTemporaryOwners(ctx);
697
+ }
673
698
  }
674
699
  });
675
700
  }
676
701
  //#endregion
677
- export { SSRDataMissingError, renderResolvedToStringSync, renderToStream, renderToString, renderToStringSync, resolveRequest };
702
+ export { SSRDataMissingError, renderToStream, renderToString, renderToStringSync, resolveRequest };
678
703
 
679
704
  //# sourceMappingURL=index.js.map