@lomray/vite-ssr-boost 7.1.0 → 8.0.0-beta.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.
Files changed (130) hide show
  1. package/README.md +17 -1
  2. package/adapters/body.d.ts +5 -0
  3. package/adapters/body.js +2 -0
  4. package/adapters/body.js.map +1 -0
  5. package/adapters/compression.d.ts +11 -0
  6. package/adapters/compression.js +2 -0
  7. package/adapters/compression.js.map +1 -0
  8. package/adapters/edge.d.ts +11 -0
  9. package/adapters/edge.js +2 -0
  10. package/adapters/edge.js.map +1 -0
  11. package/adapters/express/create-request.d.ts +11 -0
  12. package/adapters/express/create-request.js +2 -0
  13. package/adapters/express/create-request.js.map +1 -0
  14. package/{node → adapters/express}/entry.d.ts +8 -14
  15. package/adapters/express/entry.js.map +1 -0
  16. package/{services → adapters/express}/prepare-server.d.ts +14 -21
  17. package/adapters/express/prepare-server.js +2 -0
  18. package/adapters/express/prepare-server.js.map +1 -0
  19. package/{node → adapters/express}/render.d.ts +8 -8
  20. package/adapters/express/render.js +2 -0
  21. package/adapters/express/render.js.map +1 -0
  22. package/{node → adapters/express}/server.d.ts +1 -1
  23. package/adapters/express/server.js +2 -0
  24. package/adapters/express/server.js.map +1 -0
  25. package/adapters/express.d.ts +11 -0
  26. package/adapters/express.js +2 -0
  27. package/adapters/express.js.map +1 -0
  28. package/adapters/fastify.d.ts +23 -0
  29. package/adapters/fastify.js +2 -0
  30. package/adapters/fastify.js.map +1 -0
  31. package/adapters/hono.d.ts +13 -0
  32. package/adapters/hono.js +2 -0
  33. package/adapters/hono.js.map +1 -0
  34. package/adapters/node.d.ts +18 -0
  35. package/adapters/node.js +2 -0
  36. package/adapters/node.js.map +1 -0
  37. package/cli/run-dev.js +1 -1
  38. package/cli/run-dev.js.map +1 -1
  39. package/cli/run-prod.d.ts +1 -1
  40. package/cli/run-prod.js +1 -1
  41. package/cli/run-prod.js.map +1 -1
  42. package/cli/run-serverless.d.ts +2 -2
  43. package/cli/run-serverless.js +1 -1
  44. package/cli/run-serverless.js.map +1 -1
  45. package/components/response-status.js +1 -1
  46. package/components/response-status.js.map +1 -1
  47. package/core/compose-html.d.ts +5 -0
  48. package/core/compose-html.js +2 -0
  49. package/core/compose-html.js.map +1 -0
  50. package/core/early-hints.d.ts +6 -0
  51. package/core/early-hints.js +2 -0
  52. package/core/early-hints.js.map +1 -0
  53. package/core/handler.d.ts +30 -0
  54. package/core/handler.js +2 -0
  55. package/core/handler.js.map +1 -0
  56. package/core/head-response.d.ts +5 -0
  57. package/core/head-response.js +2 -0
  58. package/core/head-response.js.map +1 -0
  59. package/core/headers.d.ts +13 -0
  60. package/core/headers.js +2 -0
  61. package/core/headers.js.map +1 -0
  62. package/core/render.d.ts +101 -0
  63. package/core/render.js +2 -0
  64. package/core/render.js.map +1 -0
  65. package/core/transform-html.d.ts +7 -0
  66. package/core/transform-html.js +2 -0
  67. package/core/transform-html.js.map +1 -0
  68. package/core/types.d.ts +8 -0
  69. package/core/types.js +2 -0
  70. package/core/types.js.map +1 -0
  71. package/edge/render-to-stream.d.ts +6 -0
  72. package/edge/render-to-stream.js +2 -0
  73. package/edge/render-to-stream.js.map +1 -0
  74. package/helpers/build-custom-state.d.ts +1 -2
  75. package/helpers/build-custom-state.js +1 -1
  76. package/helpers/build-custom-state.js.map +1 -1
  77. package/helpers/obtain-stream-error.js +1 -1
  78. package/helpers/obtain-stream-error.js.map +1 -1
  79. package/interfaces/fc-route.d.ts +1 -1
  80. package/interfaces/fc-route.js.map +1 -1
  81. package/node/compress-response.d.ts +6 -0
  82. package/node/compress-response.js +2 -0
  83. package/node/compress-response.js.map +1 -0
  84. package/node/create-headers.d.ts +6 -0
  85. package/node/create-headers.js +2 -0
  86. package/node/create-headers.js.map +1 -0
  87. package/node/create-request.d.ts +13 -0
  88. package/node/create-request.js +2 -0
  89. package/node/create-request.js.map +1 -0
  90. package/node/http.d.ts +6 -0
  91. package/node/http.js +2 -0
  92. package/node/http.js.map +1 -0
  93. package/node/render-to-stream.d.ts +6 -0
  94. package/node/render-to-stream.js +2 -0
  95. package/node/render-to-stream.js.map +1 -0
  96. package/node/request-signal.d.ts +10 -0
  97. package/node/request-signal.js +2 -0
  98. package/node/request-signal.js.map +1 -0
  99. package/node/split-link-header.d.ts +5 -0
  100. package/node/split-link-header.js +2 -0
  101. package/node/split-link-header.js.map +1 -0
  102. package/node/write-early-hints.d.ts +6 -0
  103. package/node/write-early-hints.js +2 -0
  104. package/node/write-early-hints.js.map +1 -0
  105. package/node/write-fetch-response.d.ts +10 -0
  106. package/node/write-fetch-response.js +2 -0
  107. package/node/write-fetch-response.js.map +1 -0
  108. package/package.json +45 -3
  109. package/services/server-config.d.ts +3 -4
  110. package/services/server-config.js.map +1 -1
  111. package/services/ssr-manifest.d.ts +25 -8
  112. package/services/ssr-manifest.js +1 -1
  113. package/services/ssr-manifest.js.map +1 -1
  114. package/helpers/handle-response.d.ts +0 -7
  115. package/helpers/handle-response.js +0 -2
  116. package/helpers/handle-response.js.map +0 -1
  117. package/node/create-fetch-request.d.ts +0 -7
  118. package/node/create-fetch-request.js +0 -2
  119. package/node/create-fetch-request.js.map +0 -1
  120. package/node/entry.js.map +0 -1
  121. package/node/render.js +0 -2
  122. package/node/render.js.map +0 -1
  123. package/node/server.js +0 -2
  124. package/node/server.js.map +0 -1
  125. package/node/write-response.d.ts +0 -14
  126. package/node/write-response.js +0 -2
  127. package/node/write-response.js.map +0 -1
  128. package/services/prepare-server.js +0 -2
  129. package/services/prepare-server.js.map +0 -1
  130. /package/{node → adapters/express}/entry.js +0 -0
package/cli/run-prod.d.ts CHANGED
@@ -7,7 +7,7 @@ interface IRunProdParams {
7
7
  port?: number;
8
8
  isHost?: boolean;
9
9
  isPrintInfo?: boolean;
10
- focusOnly?: IBuildParams['focusOnly'];
10
+ focusOnly?: NonNullable<IBuildParams['focusOnly']>;
11
11
  mode?: string;
12
12
  modulePreload?: boolean;
13
13
  buildDir?: string;
package/cli/run-prod.js CHANGED
@@ -1,2 +1,2 @@
1
- import{performance as o}from"node:perf_hooks";import r from"../helpers/create-focus-only.js";import i from"../node/server.js";import e from"../services/server-config.js";async function s({version:s,isHost:t,isPrintInfo:n,port:l,buildDir:f,focusOnly:a,modulePreload:m=!1}){global.viteBoostStartTime||(global.viteBoostStartTime=o.now());const c=e.init({isHost:t,isProd:!0,isOnlyClient:r(a).isOnlyClient(),isModulePreload:m},{port:l,root:f}),{run:d}=await i(c);return{server:d({version:s,isPrintInfo:n}),config:c}}export{s as default};
1
+ import{performance as o}from"node:perf_hooks";import r from"../adapters/express/server.js";import e from"../helpers/create-focus-only.js";import s from"../services/server-config.js";async function i({version:i,isHost:t,isPrintInfo:n,port:l,buildDir:a,focusOnly:f,modulePreload:m=!1}){global.viteBoostStartTime||(global.viteBoostStartTime=o.now());const p=s.init({isHost:t,isProd:!0,isOnlyClient:e(f).isOnlyClient(),isModulePreload:m},{port:l,root:a}),{run:c}=await r(p);return{server:c({version:i,isPrintInfo:n}),config:p}}export{i as default};
2
2
  //# sourceMappingURL=run-prod.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-prod.js","sources":["../../src/cli/run-prod.ts"],"sourcesContent":["import type { Server } from 'node:net';\nimport { performance } from 'node:perf_hooks';\nimport createFocusOnly from '@helpers/create-focus-only';\nimport createServer from '@node/server';\nimport type { IBuildParams } from '@services/build';\nimport ServerConfig from '@services/server-config';\n\ninterface IRunProdParams {\n version: string;\n port?: number;\n isHost?: boolean;\n isPrintInfo?: boolean;\n focusOnly?: IBuildParams['focusOnly'];\n mode?: string;\n modulePreload?: boolean;\n buildDir?: string;\n}\n\ninterface IRunProdOut {\n server: Server;\n config: ServerConfig;\n}\n\n/**\n * Run production server\n */\nasync function runProd({\n version,\n isHost,\n isPrintInfo,\n port,\n buildDir,\n focusOnly,\n modulePreload = false,\n}: IRunProdParams): Promise<IRunProdOut> {\n if (!global.viteBoostStartTime) {\n global.viteBoostStartTime = performance.now();\n }\n\n const config = ServerConfig.init(\n {\n isHost,\n isProd: true,\n isOnlyClient: createFocusOnly(focusOnly).isOnlyClient(),\n isModulePreload: modulePreload,\n },\n { port, root: buildDir },\n );\n const { run } = await createServer(config);\n\n return {\n server: run({ version, isPrintInfo }),\n config,\n };\n}\n\nexport default runProd;\n"],"names":["async","runProd","version","isHost","isPrintInfo","port","buildDir","focusOnly","modulePreload","global","viteBoostStartTime","performance","now","config","ServerConfig","init","isProd","isOnlyClient","createFocusOnly","isModulePreload","root","run","createServer","server"],"mappings":"0KA0BAA,eAAeC,GAAQC,QACrBA,EAAOC,OACPA,EAAMC,YACNA,EAAWC,KACXA,EAAIC,SACJA,EAAQC,UACRA,EAASC,cACTA,GAAgB,IAEXC,OAAOC,qBACVD,OAAOC,mBAAqBC,EAAYC,OAG1C,MAAMC,EAASC,EAAaC,KAC1B,CACEZ,SACAa,QAAQ,EACRC,aAAcC,EAAgBX,GAAWU,eACzCE,gBAAiBX,GAEnB,CAAEH,OAAMe,KAAMd,KAEVe,IAAEA,SAAcC,EAAaT,GAEnC,MAAO,CACLU,OAAQF,EAAI,CAAEnB,UAASE,gBACvBS,SAEJ"}
1
+ {"version":3,"file":"run-prod.js","sources":["../../src/cli/run-prod.ts"],"sourcesContent":["import type { Server } from 'node:net';\nimport { performance } from 'node:perf_hooks';\nimport createServer from '@adapters/express/server';\nimport createFocusOnly from '@helpers/create-focus-only';\nimport type { IBuildParams } from '@services/build';\nimport ServerConfig from '@services/server-config';\n\ninterface IRunProdParams {\n version: string;\n port?: number;\n isHost?: boolean;\n isPrintInfo?: boolean;\n focusOnly?: NonNullable<IBuildParams['focusOnly']>;\n mode?: string;\n modulePreload?: boolean;\n buildDir?: string;\n}\n\ninterface IRunProdOut {\n server: Server;\n config: ServerConfig;\n}\n\n/**\n * Run production server\n */\nasync function runProd({\n version,\n isHost,\n isPrintInfo,\n port,\n buildDir,\n focusOnly,\n modulePreload = false,\n}: IRunProdParams): Promise<IRunProdOut> {\n if (!global.viteBoostStartTime) {\n global.viteBoostStartTime = performance.now();\n }\n\n const config = ServerConfig.init(\n {\n isHost,\n isProd: true,\n isOnlyClient: createFocusOnly(focusOnly).isOnlyClient(),\n isModulePreload: modulePreload,\n },\n { port, root: buildDir },\n );\n const { run } = await createServer(config);\n\n return {\n server: run({ version, isPrintInfo }),\n config,\n };\n}\n\nexport default runProd;\n"],"names":["async","runProd","version","isHost","isPrintInfo","port","buildDir","focusOnly","modulePreload","global","viteBoostStartTime","performance","now","config","ServerConfig","init","isProd","isOnlyClient","createFocusOnly","isModulePreload","root","run","createServer","server"],"mappings":"sLA0BAA,eAAeC,GAAQC,QACrBA,EAAOC,OACPA,EAAMC,YACNA,EAAWC,KACXA,EAAIC,SACJA,EAAQC,UACRA,EAASC,cACTA,GAAgB,IAEXC,OAAOC,qBACVD,OAAOC,mBAAqBC,EAAYC,OAG1C,MAAMC,EAASC,EAAaC,KAC1B,CACEZ,SACAa,QAAQ,EACRC,aAAcC,EAAgBX,GAAWU,eACzCE,gBAAiBX,GAEnB,CAAEH,OAAMe,KAAMd,KAEVe,IAAEA,SAAcC,EAAaT,GAEnC,MAAO,CACLU,OAAQF,EAAI,CAAEnB,UAASE,gBACvBS,SAEJ"}
@@ -1,4 +1,4 @@
1
- import { Express } from 'express';
1
+ import { ICreateServerOut } from "../adapters/express/server.js";
2
2
  interface IRunServerlessParams {
3
3
  version: string;
4
4
  modulePreload?: boolean;
@@ -6,5 +6,5 @@ interface IRunServerlessParams {
6
6
  /**
7
7
  * Run serverless
8
8
  */
9
- declare function runServerless({ version, modulePreload, }: IRunServerlessParams): Promise<Express>;
9
+ declare function runServerless({ version, modulePreload, }: IRunServerlessParams): Promise<ICreateServerOut['app']>;
10
10
  export { runServerless as default };
@@ -1,2 +1,2 @@
1
- import{performance as o}from"node:perf_hooks";import r from"../helpers/print-server-info.js";import e from"../node/server.js";import i from"../services/server-config.js";async function t({version:t,modulePreload:s=!1}){global.viteBoostStartTime||(global.viteBoostStartTime=o.now());const n=i.init({isProd:!0,isOnlyClient:!1,isModulePreload:s}),{app:a}=await e(n);return r(n,{version:t}),a}export{t as default};
1
+ import{performance as o}from"node:perf_hooks";import r from"../adapters/express/server.js";import e from"../helpers/print-server-info.js";import s from"../services/server-config.js";async function i({version:i,modulePreload:t=!1}){global.viteBoostStartTime||(global.viteBoostStartTime=o.now());const n=s.init({isProd:!0,isOnlyClient:!1,isModulePreload:t}),{app:a}=await r(n);return e(n,{version:i}),a}export{i as default};
2
2
  //# sourceMappingURL=run-serverless.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"run-serverless.js","sources":["../../src/cli/run-serverless.ts"],"sourcesContent":["import { performance } from 'node:perf_hooks';\nimport type { Express } from 'express';\nimport printServerInfo from '@helpers/print-server-info';\nimport createServer from '@node/server';\nimport ServerConfig from '@services/server-config';\n\ninterface IRunServerlessParams {\n version: string;\n modulePreload?: boolean;\n}\n\n/**\n * Run serverless\n */\nasync function runServerless({\n version,\n modulePreload = false,\n}: IRunServerlessParams): Promise<Express> {\n if (!global.viteBoostStartTime) {\n global.viteBoostStartTime = performance.now();\n }\n\n const config = ServerConfig.init({\n isProd: true,\n isOnlyClient: false,\n isModulePreload: modulePreload,\n });\n\n const { app } = await createServer(config);\n\n void printServerInfo(config, { version });\n\n return app;\n}\n\nexport default runServerless;\n"],"names":["async","runServerless","version","modulePreload","global","viteBoostStartTime","performance","now","config","ServerConfig","init","isProd","isOnlyClient","isModulePreload","app","createServer","printServerInfo"],"mappings":"0KAcAA,eAAeC,GAAcC,QAC3BA,EAAOC,cACPA,GAAgB,IAEXC,OAAOC,qBACVD,OAAOC,mBAAqBC,EAAYC,OAG1C,MAAMC,EAASC,EAAaC,KAAK,CAC/BC,QAAQ,EACRC,cAAc,EACdC,gBAAiBV,KAGbW,IAAEA,SAAcC,EAAaP,GAInC,OAFKQ,EAAgBR,EAAQ,CAAEN,YAExBY,CACT"}
1
+ {"version":3,"file":"run-serverless.js","sources":["../../src/cli/run-serverless.ts"],"sourcesContent":["import { performance } from 'node:perf_hooks';\nimport type { ICreateServerOut } from '@adapters/express/server';\nimport createServer from '@adapters/express/server';\nimport printServerInfo from '@helpers/print-server-info';\nimport ServerConfig from '@services/server-config';\n\ninterface IRunServerlessParams {\n version: string;\n modulePreload?: boolean;\n}\n\n/**\n * Run serverless\n */\nasync function runServerless({\n version,\n modulePreload = false,\n}: IRunServerlessParams): Promise<ICreateServerOut['app']> {\n if (!global.viteBoostStartTime) {\n global.viteBoostStartTime = performance.now();\n }\n\n const config = ServerConfig.init({\n isProd: true,\n isOnlyClient: false,\n isModulePreload: modulePreload,\n });\n\n const { app } = await createServer(config);\n\n void printServerInfo(config, { version });\n\n return app;\n}\n\nexport default runServerless;\n"],"names":["async","runServerless","version","modulePreload","global","viteBoostStartTime","performance","now","config","ServerConfig","init","isProd","isOnlyClient","isModulePreload","app","createServer","printServerInfo"],"mappings":"sLAcAA,eAAeC,GAAcC,QAC3BA,EAAOC,cACPA,GAAgB,IAEXC,OAAOC,qBACVD,OAAOC,mBAAqBC,EAAYC,OAG1C,MAAMC,EAASC,EAAaC,KAAK,CAC/BC,QAAQ,EACRC,cAAc,EACdC,gBAAiBV,KAGbW,IAAEA,SAAcC,EAAaP,GAInC,OAFKQ,EAAgBR,EAAQ,CAAEN,YAExBY,CACT"}
@@ -1,2 +1,2 @@
1
- import{useServerContext as s}from"../context/server.js";const e=({status:e})=>{const t=s();return t.isServer&&(t.response=new Response("",{status:e})),null};export{e as default};
1
+ import{useServerContext as s}from"../context/server.js";const e=({status:e})=>{const t=s();return t.isServer&&(t.response=new Response(null,{status:e})),null};export{e as default};
2
2
  //# sourceMappingURL=response-status.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"response-status.js","sources":["../../src/components/response-status.ts"],"sourcesContent":["import type { FC } from 'react';\nimport { useServerContext } from '@context/server';\n\ninterface IStatusGate {\n status: number;\n}\n\n/**\n * Change server response status\n * Example: <ResponseStatus status={400} />\n * @constructor\n */\nconst ResponseStatus: FC<IStatusGate> = ({ status }) => {\n const context = useServerContext();\n\n if (context.isServer) {\n context.response = new Response('', { status });\n }\n\n return null;\n};\n\nexport default ResponseStatus;\n"],"names":["ResponseStatus","status","context","useServerContext","isServer","response","Response"],"mappings":"wDAYA,MAAMA,EAAkC,EAAGC,aACzC,MAAMC,EAAUC,IAMhB,OAJID,EAAQE,WACVF,EAAQG,SAAW,IAAIC,SAAS,GAAI,CAAEL,YAGjC,IAAI"}
1
+ {"version":3,"file":"response-status.js","sources":["../../src/components/response-status.ts"],"sourcesContent":["import type { FC } from 'react';\nimport { useServerContext } from '@context/server';\n\ninterface IStatusGate {\n status: number;\n}\n\n/**\n * Change server response status\n * Example: <ResponseStatus status={400} />\n * @constructor\n */\nconst ResponseStatus: FC<IStatusGate> = ({ status }) => {\n const context = useServerContext();\n\n if (context.isServer) {\n context.response = new Response(null, { status });\n }\n\n return null;\n};\n\nexport default ResponseStatus;\n"],"names":["ResponseStatus","status","context","useServerContext","isServer","response","Response"],"mappings":"wDAYA,MAAMA,EAAkC,EAAGC,aACzC,MAAMC,EAAUC,IAMhB,OAJID,EAAQE,WACVF,EAAQG,SAAW,IAAIC,SAAS,KAAM,CAAEL,YAGnC,IAAI"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Stream the shell, React body and hydration footer under downstream backpressure.
3
+ */
4
+ declare const composeHtml: (header: string, body: ReadableStream<Uint8Array>, footer: string, abort?: ((reason?: unknown) => void) | undefined, onComplete?: () => void) => ReadableStream<Uint8Array>;
5
+ export { composeHtml as default };
@@ -0,0 +1,2 @@
1
+ const e=(e,a,r,o,n)=>{const t=new TextEncoder,c=a.getReader();let l="header";return new ReadableStream({cancel:async e=>{if(o?.(e),"footer"!==l)try{await c.cancel(e)}finally{c.releaseLock(),n?.()}},async pull(a){if("header"===l&&(l="body",e))a.enqueue(t.encode(e));else{if("body"===l){let e;try{e=await c.read()}catch(e){return o?.(e),c.releaseLock(),n?.(),void a.error(e)}if(!e.done)return void a.enqueue(e.value);l="footer",c.releaseLock(),n?.()}r&&a.enqueue(t.encode(r)),a.close()}}})};export{e as default};
2
+ //# sourceMappingURL=compose-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-html.js","sources":["../../src/core/compose-html.ts"],"sourcesContent":["/**\n * Stream the shell, React body and hydration footer under downstream backpressure.\n */\nconst composeHtml = (\n header: string,\n body: ReadableStream<Uint8Array>,\n footer: string,\n abort?: (reason?: unknown) => void,\n onComplete?: () => void,\n): ReadableStream<Uint8Array> => {\n const encoder = new TextEncoder();\n const reader = body.getReader();\n let phase: 'body' | 'footer' | 'header' = 'header';\n\n return new ReadableStream<Uint8Array>({\n /**\n * Release the React reader when the response consumer cancels.\n */\n cancel: async (reason) => {\n abort?.(reason);\n\n if (phase === 'footer') {\n return;\n }\n\n try {\n await reader.cancel(reason);\n } finally {\n reader.releaseLock();\n onComplete?.();\n }\n },\n\n /**\n * Emit one available shell or React chunk per downstream pull.\n */\n async pull(controller) {\n if (phase === 'header') {\n phase = 'body';\n\n if (header) {\n controller.enqueue(encoder.encode(header));\n\n return;\n }\n }\n\n if (phase === 'body') {\n let chunk: ReadableStreamReadResult<Uint8Array>;\n\n try {\n chunk = await reader.read();\n } catch (error) {\n abort?.(error);\n reader.releaseLock();\n onComplete?.();\n controller.error(error);\n\n return;\n }\n\n if (!chunk.done) {\n controller.enqueue(chunk.value);\n\n return;\n }\n\n phase = 'footer';\n reader.releaseLock();\n onComplete?.();\n }\n\n if (footer) {\n controller.enqueue(encoder.encode(footer));\n }\n\n controller.close();\n },\n });\n};\n\nexport default composeHtml;\n"],"names":["composeHtml","header","body","footer","abort","onComplete","encoder","TextEncoder","reader","getReader","phase","ReadableStream","cancel","async","reason","releaseLock","controller","enqueue","encode","chunk","read","error","done","value","close"],"mappings":"AAGA,MAAMA,EAAc,CAClBC,EACAC,EACAC,EACAC,EACAC,KAEA,MAAMC,EAAU,IAAIC,YACdC,EAASN,EAAKO,YACpB,IAAIC,EAAsC,SAE1C,OAAO,IAAIC,eAA2B,CAIpCC,OAAQC,MAAOC,IAGb,GAFAV,IAAQU,GAEM,WAAVJ,EAIJ,UACQF,EAAOI,OAAOE,EACtB,SACEN,EAAOO,cACPV,KACF,GAMFQ,WAAWG,GACT,GAAc,WAAVN,IACFA,EAAQ,OAEJT,GACFe,EAAWC,QAAQX,EAAQY,OAAOjB,QAJtC,CAUA,GAAc,SAAVS,EAAkB,CACpB,IAAIS,EAEJ,IACEA,QAAcX,EAAOY,MACvB,CAAE,MAAOC,GAMP,OALAjB,IAAQiB,GACRb,EAAOO,cACPV,WACAW,EAAWK,MAAMA,EAGnB,CAEA,IAAKF,EAAMG,KAGT,YAFAN,EAAWC,QAAQE,EAAMI,OAK3Bb,EAAQ,SACRF,EAAOO,cACPV,KACF,CAEIF,GACFa,EAAWC,QAAQX,EAAQY,OAAOf,IAGpCa,EAAWQ,OA/BX,CAgCF,GACA"}
@@ -0,0 +1,6 @@
1
+ import { ISsrExecutionContext } from "./types.js";
2
+ /**
3
+ * Emit informational headers when the runtime supports early hints.
4
+ */
5
+ declare const emitEarlyHints: (context: ISsrExecutionContext | undefined, headers: Headers) => Promise<void>;
6
+ export { emitEarlyHints as default };
@@ -0,0 +1,2 @@
1
+ const a=async(a,t)=>{await(a?.onEarlyHints?.(t))};export{a as default};
2
+ //# sourceMappingURL=early-hints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"early-hints.js","sources":["../../src/core/early-hints.ts"],"sourcesContent":["import type { ISsrExecutionContext } from '@core/types';\n\n/**\n * Emit informational headers when the runtime supports early hints.\n */\nconst emitEarlyHints = async (\n context: ISsrExecutionContext | undefined,\n headers: Headers,\n): Promise<void> => {\n await context?.onEarlyHints?.(headers);\n};\n\nexport default emitEarlyHints;\n"],"names":["emitEarlyHints","async","context","headers","onEarlyHints"],"mappings":"AAKA,MAAMA,EAAiBC,MACrBC,EACAC,WAEMD,GAASE,eAAeD,GAAQ"}
@@ -0,0 +1,30 @@
1
+ import { ICoreRenderOptions, ICoreRenderParams } from "./render.js";
2
+ import { ISsrExecutionContext, TSsrHandler } from "./types.js";
3
+ interface IHtmlShell {
4
+ footer: string;
5
+ header: string;
6
+ }
7
+ interface IRequestInit<TAppProps> {
8
+ appProps?: TAppProps;
9
+ headers?: HeadersInit;
10
+ status?: number;
11
+ }
12
+ interface ICreateHandlerOptions<TAppProps> extends ICoreRenderOptions<TAppProps> {
13
+ /**
14
+ * Supply the document shell, including the application's browser entry script.
15
+ */
16
+ getHtml: (request: Request) => IHtmlShell | Promise<IHtmlShell>;
17
+ /**
18
+ * Initialize request metadata or return a Response to bypass rendering.
19
+ */
20
+ onRequest?: (params: {
21
+ executionContext?: ISsrExecutionContext;
22
+ request: Request;
23
+ }) => IRequestInit<TAppProps> | Promise<IRequestInit<TAppProps> | Response> | Response;
24
+ }
25
+ /**
26
+ * Create isolated request state and allow request hooks to bypass rendering.
27
+ */
28
+ declare const createHandler: <TAppProps = Record<string, any>>(params: ICoreRenderParams<TAppProps>, { getHtml, onRequest, ...options }: ICreateHandlerOptions<TAppProps>) => TSsrHandler;
29
+ export type { ICreateHandlerOptions, IHtmlShell, IRequestInit };
30
+ export { createHandler as default };
@@ -0,0 +1,2 @@
1
+ import e from"./head-response.js";import s from"./render.js";const t=(t,{getHtml:r,onRequest:o,...a})=>async(n,p)=>{const u=await(o?.({executionContext:p,request:n}));if(u instanceof Response)return e(n,u);const i={appProps:u?.appProps??{},html:await r(n),request:n,response:{headers:new Headers(u?.headers),status:u?.status}};return s(t,i,a,p)};export{t as default};
2
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sources":["../../src/core/handler.ts"],"sourcesContent":["import headResponse from '@core/head-response';\nimport render from '@core/render';\nimport type { ICoreRenderOptions, ICoreRenderParams, ISsrRequestContext } from '@core/render';\nimport type { ISsrExecutionContext, TSsrHandler } from '@core/types';\n\ninterface IHtmlShell {\n footer: string;\n header: string;\n}\n\ninterface IRequestInit<TAppProps> {\n appProps?: TAppProps;\n headers?: HeadersInit;\n status?: number;\n}\n\ninterface ICreateHandlerOptions<TAppProps> extends ICoreRenderOptions<TAppProps> {\n /**\n * Supply the document shell, including the application's browser entry script.\n */\n getHtml: (request: Request) => IHtmlShell | Promise<IHtmlShell>;\n\n /**\n * Initialize request metadata or return a Response to bypass rendering.\n */\n onRequest?: (params: {\n executionContext?: ISsrExecutionContext;\n request: Request;\n }) => IRequestInit<TAppProps> | Promise<IRequestInit<TAppProps> | Response> | Response;\n}\n\n/**\n * Create isolated request state and allow request hooks to bypass rendering.\n */\nconst createHandler = <TAppProps = Record<string, any>>(\n params: ICoreRenderParams<TAppProps>,\n { getHtml, onRequest, ...options }: ICreateHandlerOptions<TAppProps>,\n): TSsrHandler => {\n /**\n * Build fresh context for this request before invoking the renderer.\n */\n return async (request, executionContext) => {\n const requestInit = await onRequest?.({ executionContext, request });\n\n if (requestInit instanceof Response) {\n return headResponse(request, requestInit);\n }\n\n const context: ISsrRequestContext<TAppProps> = {\n appProps: (requestInit?.appProps ?? {}) as NonNullable<TAppProps>,\n html: await getHtml(request),\n request,\n response: {\n headers: new Headers(requestInit?.headers),\n status: requestInit?.status,\n },\n };\n\n return render(params, context, options, executionContext);\n };\n};\n\nexport type { ICreateHandlerOptions, IHtmlShell, IRequestInit };\n\nexport default createHandler;\n"],"names":["createHandler","params","getHtml","onRequest","options","async","request","executionContext","requestInit","Response","headResponse","context","appProps","html","response","headers","Headers","status","render"],"mappings":"6DAkCA,MAAMA,EAAgB,CACpBC,GACEC,UAASC,eAAcC,KAKlBC,MAAOC,EAASC,KACrB,MAAMC,QAAoBL,IAAY,CAAEI,mBAAkBD,aAE1D,GAAIE,aAAuBC,SACzB,OAAOC,EAAaJ,EAASE,GAG/B,MAAMG,EAAyC,CAC7CC,SAAWJ,GAAaI,UAAY,GACpCC,WAAYX,EAAQI,GACpBA,UACAQ,SAAU,CACRC,QAAS,IAAIC,QAAQR,GAAaO,SAClCE,OAAQT,GAAaS,SAIzB,OAAOC,EAAOjB,EAAQU,EAASP,EAASG,EAAiB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Preserve GET metadata while releasing a body that cannot be sent for HEAD.
3
+ */
4
+ declare const headResponse: (request: Request, response: Response) => Promise<Response>;
5
+ export { headResponse as default };
@@ -0,0 +1,2 @@
1
+ const s=async(s,t)=>"HEAD"===s.method&&t.body?(await t.body.cancel(),new Response(null,{headers:t.headers,status:t.status,statusText:t.statusText})):t;export{s as default};
2
+ //# sourceMappingURL=head-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"head-response.js","sources":["../../src/core/head-response.ts"],"sourcesContent":["/**\n * Preserve GET metadata while releasing a body that cannot be sent for HEAD.\n */\nconst headResponse = async (request: Request, response: Response): Promise<Response> => {\n if (request.method !== 'HEAD' || !response.body) {\n return response;\n }\n\n await response.body.cancel();\n\n return new Response(null, {\n headers: response.headers,\n status: response.status,\n statusText: response.statusText,\n });\n};\n\nexport default headResponse;\n"],"names":["headResponse","async","request","response","method","body","cancel","Response","headers","status","statusText"],"mappings":"AAGA,MAAMA,EAAeC,MAAOC,EAAkBC,IACrB,SAAnBD,EAAQE,QAAsBD,EAASE,YAIrCF,EAASE,KAAKC,SAEb,IAAIC,SAAS,KAAM,CACxBC,QAASL,EAASK,QAClBC,OAAQN,EAASM,OACjBC,WAAYP,EAASO,cARdP"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Enumerate ordinary headers without combining independent Set-Cookie values.
3
+ */
4
+ declare const getHeaderEntries: (headers: Headers) => [string, string][];
5
+ /**
6
+ * Read cookies without splitting commas inside Expires attributes.
7
+ */
8
+ declare const getSetCookieHeaders: (headers: Headers) => string[];
9
+ /**
10
+ * Response headers override hook headers, while every Set-Cookie remains independent.
11
+ */
12
+ declare const mergeResponseHeaders: (response: Response, base: Headers) => Response;
13
+ export { getHeaderEntries, getSetCookieHeaders, mergeResponseHeaders };
@@ -0,0 +1,2 @@
1
+ const e=e=>{const t=[];return e.forEach(((e,s)=>{"set-cookie"!==s.toLowerCase()&&t.push([s,e])})),t},t=e=>e.getSetCookie(),s=(s,o)=>{const a=new Headers(o);return e(s.headers).forEach((([e,t])=>a.set(e,t))),t(s.headers).forEach((e=>a.append("Set-Cookie",e))),new Response(s.body,{headers:a,status:s.status,statusText:s.statusText})};export{e as getHeaderEntries,t as getSetCookieHeaders,s as mergeResponseHeaders};
2
+ //# sourceMappingURL=headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headers.js","sources":["../../src/core/headers.ts"],"sourcesContent":["/**\n * Enumerate ordinary headers without combining independent Set-Cookie values.\n */\nexport const getHeaderEntries = (headers: Headers): [string, string][] => {\n const entries: [string, string][] = [];\n\n headers.forEach((value, name) => {\n if (name.toLowerCase() !== 'set-cookie') {\n entries.push([name, value]);\n }\n });\n\n return entries;\n};\n\n/**\n * Read cookies without splitting commas inside Expires attributes.\n */\nexport const getSetCookieHeaders = (headers: Headers): string[] => headers.getSetCookie();\n\n/**\n * Response headers override hook headers, while every Set-Cookie remains independent.\n */\nexport const mergeResponseHeaders = (response: Response, base: Headers): Response => {\n const headers = new Headers(base);\n\n getHeaderEntries(response.headers).forEach(([name, value]) => headers.set(name, value));\n getSetCookieHeaders(response.headers).forEach((cookie) => headers.append('Set-Cookie', cookie));\n\n return new Response(response.body, {\n headers,\n status: response.status,\n statusText: response.statusText,\n });\n};\n"],"names":["getHeaderEntries","headers","entries","forEach","value","name","toLowerCase","push","getSetCookieHeaders","getSetCookie","mergeResponseHeaders","response","base","Headers","set","cookie","append","Response","body","status","statusText"],"mappings":"AAGO,MAAMA,EAAoBC,IAC/B,MAAMC,EAA8B,GAQpC,OANAD,EAAQE,SAAQ,CAACC,EAAOC,KACK,eAAvBA,EAAKC,eACPJ,EAAQK,KAAK,CAACF,EAAMD,GACtB,IAGKF,CAAO,EAMHM,EAAuBP,GAA+BA,EAAQQ,eAK9DC,EAAuB,CAACC,EAAoBC,KACvD,MAAMX,EAAU,IAAIY,QAAQD,GAK5B,OAHAZ,EAAiBW,EAASV,SAASE,SAAQ,EAAEE,EAAMD,KAAWH,EAAQa,IAAIT,EAAMD,KAChFI,EAAoBG,EAASV,SAASE,SAASY,GAAWd,EAAQe,OAAO,aAAcD,KAEhF,IAAIE,SAASN,EAASO,KAAM,CACjCjB,UACAkB,OAAQR,EAASQ,OACjBC,WAAYT,EAASS,YACrB"}
@@ -0,0 +1,101 @@
1
+ import { ReactNode } from 'react';
2
+ import { StaticHandler, StaticHandlerContext } from 'react-router';
3
+ import StreamError from "../constants/stream-error.js";
4
+ import { IServerContext } from "../context/server.js";
5
+ import { ISsrExecutionContext } from "./types.js";
6
+ import { IObtainStreamErrorOut } from "../helpers/obtain-stream-error.js";
7
+ interface ISsrRequestContext<TAppProps = Record<string, any>> {
8
+ appProps: NonNullable<TAppProps>;
9
+ /**
10
+ * First render failure, or the explicit timeout/cancellation classification.
11
+ */
12
+ didError?: StreamError;
13
+ html: {
14
+ footer: string;
15
+ header: string;
16
+ };
17
+ isStream?: boolean;
18
+ request: Request;
19
+ /**
20
+ * Mutable response metadata shared with request and render hooks.
21
+ */
22
+ response: {
23
+ headers: Headers;
24
+ status?: number;
25
+ };
26
+ routerContext?: StaticHandlerContext;
27
+ serverContext?: IServerContext;
28
+ }
29
+ interface IRenderStreamOptions {
30
+ onError: (error: unknown) => void;
31
+ signal: AbortSignal;
32
+ }
33
+ interface IRenderStream {
34
+ /**
35
+ * Settles after all suspended content completes or rendering is aborted.
36
+ */
37
+ allReady: Promise<void>;
38
+ /**
39
+ * Stop rendering and settle pending readiness promises.
40
+ */
41
+ abort: (reason?: unknown) => void;
42
+ /**
43
+ * Settles when the first shell is available or cannot be produced.
44
+ */
45
+ shellReady: Promise<void>;
46
+ /**
47
+ * Begin piping after the core commits its response metadata.
48
+ */
49
+ start: () => void;
50
+ stream: ReadableStream<Uint8Array>;
51
+ }
52
+ type TRenderToStream = (node: ReactNode, options: IRenderStreamOptions) => IRenderStream | Promise<IRenderStream>;
53
+ interface ICoreRenderParams<TAppProps = Record<string, any>> {
54
+ createApp: (children: ReactNode, context: ISsrRequestContext<TAppProps>) => ReactNode;
55
+ handler: StaticHandler;
56
+ renderToStream: TRenderToStream;
57
+ }
58
+ interface ICoreRenderOptions<TAppProps = Record<string, any>> {
59
+ /**
60
+ * React rendering deadline in milliseconds, starting after router preparation.
61
+ */
62
+ abortDelay?: number;
63
+ getState?: (params: {
64
+ context: ISsrRequestContext<TAppProps>;
65
+ }) => Record<string, Record<string, any>> | undefined | void;
66
+ onError?: (params: {
67
+ context: ISsrRequestContext<TAppProps>;
68
+ error: IObtainStreamErrorOut;
69
+ }) => void;
70
+ onResponse?: (params: {
71
+ context: ISsrRequestContext<TAppProps>;
72
+ html: string;
73
+ }) => string | undefined | void;
74
+ onRouterReady?: (params: {
75
+ context: ISsrRequestContext<TAppProps>;
76
+ }) => Promise<{
77
+ isStream?: boolean;
78
+ }> | {
79
+ isStream?: boolean;
80
+ };
81
+ onShellError?: (params: {
82
+ context: ISsrRequestContext<TAppProps>;
83
+ error: Error;
84
+ }) => string | undefined | void;
85
+ onShellReady?: (params: {
86
+ context: ISsrRequestContext<TAppProps>;
87
+ }) => {
88
+ footer?: string;
89
+ header?: string;
90
+ };
91
+ prepare?: (params: {
92
+ context: ISsrRequestContext<TAppProps>;
93
+ executionContext?: ISsrExecutionContext;
94
+ }) => Promise<void> | void;
95
+ routerRequestContext?: unknown;
96
+ }
97
+ /**
98
+ * Coordinate router queries, React rendering and the final Fetch response.
99
+ */
100
+ declare const render: <TAppProps>({ createApp, handler, renderToStream }: ICoreRenderParams<TAppProps>, context: ISsrRequestContext<TAppProps>, { abortDelay, getState, onError, onResponse, onRouterReady, onShellError, onShellReady, prepare, routerRequestContext, }: ICoreRenderOptions<TAppProps>, executionContext?: ISsrExecutionContext) => Promise<Response>;
101
+ export { render as default, ISsrRequestContext, IRenderStreamOptions, IRenderStream, TRenderToStream, ICoreRenderParams, ICoreRenderOptions };
package/core/render.js ADDED
@@ -0,0 +1,2 @@
1
+ import e from"react";import{createStaticRouter as t,StaticRouterProvider as r}from"react-router";import o from"../constants/stream-error.js";import{ServerProvider as s}from"../context/server.js";import a from"./compose-html.js";import n from"./head-response.js";import{mergeResponseHeaders as c}from"./headers.js";import d from"./transform-html.js";import l from"../helpers/build-custom-state.js";import u from"../helpers/build-router-state.js";import i from"../helpers/obtain-stream-error.js";const m="text/html",h="Content-Type",p=async({createApp:p,handler:x,renderToStream:f},R,{abortDelay:C=15e3,getState:w,onError:E,onResponse:y,onRouterReady:b,onShellError:q,onShellReady:S,prepare:g,routerRequestContext:v},j)=>{const T=await x.query(R.request,{requestContext:v??R});if(T instanceof Response)return n(R.request,c(T,R.response.headers));R.routerContext=T,await(g?.({context:R,executionContext:j}));const{isStream:A=!0}=await(b?.({context:R}))??{};R.isStream=A,R.serverContext={basename:R.routerContext.basename,isServer:!0,response:null};const H=t(x.dataRoutes,R.routerContext),D=e.createElement(s,{context:R.serverContext},p(e.createElement(r,{router:H,context:R.routerContext,hydrate:!1}),R)),L=new AbortController;let k,B,I=!1;const z=()=>J(R.request.signal.reason),F=()=>clearTimeout(K),G=()=>{F(),R.request.signal.removeEventListener("abort",z)},J=e=>{I||(I=!0,k=e,G(),R.didError??=o.RenderCancel,L.abort(e),B?.abort(e))},K=setTimeout((()=>{R.didError=o.RenderTimeout,J()}),C);R.request.signal.aborted?J(R.request.signal.reason):R.request.signal.addEventListener("abort",z,{once:!0});try{B=await f(D,{onError:e=>{const t=i(e);!I||t.code!==o.RenderAborted&&e!==L.signal.reason||(t.code=R.didError===o.RenderTimeout?o.RenderTimeout:o.RenderCancel);const{code:r}=t;R.didError??=r,E?.({context:R,error:t})},signal:L.signal}),I&&B.abort(k),B.allReady.then(F,F),await(A?B.shellReady:B.allReady)}catch(e){return J(e),await(B?.stream.cancel(e).catch((()=>{}))),((e,t,r)=>{const o=t instanceof Error?t:new Error(String(t)),s=r?.({context:e,error:o})||"<!doctype html><p>Internal Server Error</p>",a=new Headers(e.response.headers);return a.set(h,m),new Response("HEAD"===e.request.method?null:s,{headers:a,status:500})})(R,e,q)}try{const e=R.serverContext.response;if(e&&(M=e,Boolean(M&&M.status>=300&&M.status<400&&M.headers.has("Location"))))return J(),await B.stream.cancel().catch((()=>{})),n(R.request,c(e,R.response.headers));const{header:t,footer:r,headers:o,status:s}=((e,{getState:t,onShellReady:r})=>{const o=e.serverContext.response;e.response.status=o?.status??e.response.status??e.routerContext.statusCode??200,e.response.headers.has(h)||e.response.headers.set(h,m);const s=r?.({context:e})??{},a=u(e.routerContext),n=l(t?.({context:e}));return{header:s.header||e.html.header,footer:a+n+(s.footer||e.html.footer),headers:new Headers(e.response.headers),status:e.response.status}})(R,{getState:w,onShellReady:S});if("HEAD"===R.request.method||[204,205,304].includes(s))return J(),await B.stream.cancel().catch((()=>{})),new Response(null,{headers:o,status:s});const i=a(t,B.stream,r,J,G),p=d(i,y?e=>y({context:R,html:e}):void 0);return B.start(),new Response(p,{headers:o,status:s})}catch(e){throw J(e),B.stream.locked||await B.stream.cancel(e).catch((()=>{})),e}var M};export{p as default};
2
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sources":["../../src/core/render.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport React from 'react';\nimport type { StaticHandler, StaticHandlerContext } from 'react-router';\nimport { createStaticRouter, StaticRouterProvider } from 'react-router';\nimport StreamError from '@constants/stream-error';\nimport { ServerProvider } from '@context/server';\nimport type { IServerContext } from '@context/server';\nimport composeHtml from '@core/compose-html';\nimport headResponse from '@core/head-response';\nimport { mergeResponseHeaders } from '@core/headers';\nimport transformHtml from '@core/transform-html';\nimport type { ISsrExecutionContext } from '@core/types';\nimport buildCustomState from '@helpers/build-custom-state';\nimport buildRouterState from '@helpers/build-router-state';\nimport type { IObtainStreamErrorOut } from '@helpers/obtain-stream-error';\nimport obtainStreamError from '@helpers/obtain-stream-error';\n\nexport interface ISsrRequestContext<TAppProps = Record<string, any>> {\n appProps: NonNullable<TAppProps>;\n\n /**\n * First render failure, or the explicit timeout/cancellation classification.\n */\n didError?: StreamError;\n html: { footer: string; header: string };\n isStream?: boolean;\n request: Request;\n\n /**\n * Mutable response metadata shared with request and render hooks.\n */\n response: {\n headers: Headers;\n status?: number;\n };\n routerContext?: StaticHandlerContext;\n serverContext?: IServerContext;\n}\n\nexport interface IRenderStreamOptions {\n onError: (error: unknown) => void;\n signal: AbortSignal;\n}\n\nexport interface IRenderStream {\n /**\n * Settles after all suspended content completes or rendering is aborted.\n */\n allReady: Promise<void>;\n\n /**\n * Stop rendering and settle pending readiness promises.\n */\n abort: (reason?: unknown) => void;\n\n /**\n * Settles when the first shell is available or cannot be produced.\n */\n shellReady: Promise<void>;\n\n /**\n * Begin piping after the core commits its response metadata.\n */\n start: () => void;\n stream: ReadableStream<Uint8Array>;\n}\n\nexport type TRenderToStream = (\n node: ReactNode,\n options: IRenderStreamOptions,\n) => IRenderStream | Promise<IRenderStream>;\n\nexport interface ICoreRenderParams<TAppProps = Record<string, any>> {\n createApp: (children: ReactNode, context: ISsrRequestContext<TAppProps>) => ReactNode;\n handler: StaticHandler;\n renderToStream: TRenderToStream;\n}\n\nexport interface ICoreRenderOptions<TAppProps = Record<string, any>> {\n /**\n * React rendering deadline in milliseconds, starting after router preparation.\n */\n abortDelay?: number;\n getState?: (params: {\n context: ISsrRequestContext<TAppProps>;\n }) => Record<string, Record<string, any>> | undefined | void;\n onError?: (params: {\n context: ISsrRequestContext<TAppProps>;\n error: IObtainStreamErrorOut;\n }) => void;\n onResponse?: (params: {\n context: ISsrRequestContext<TAppProps>;\n html: string;\n }) => string | undefined | void;\n onRouterReady?: (params: {\n context: ISsrRequestContext<TAppProps>;\n }) => Promise<{ isStream?: boolean }> | { isStream?: boolean };\n onShellError?: (params: {\n context: ISsrRequestContext<TAppProps>;\n error: Error;\n }) => string | undefined | void;\n onShellReady?: (params: { context: ISsrRequestContext<TAppProps> }) => {\n footer?: string;\n header?: string;\n };\n prepare?: (params: {\n context: ISsrRequestContext<TAppProps>;\n executionContext?: ISsrExecutionContext;\n }) => Promise<void> | void;\n routerRequestContext?: unknown;\n}\n\ninterface IHtmlResponse {\n header: string;\n footer: string;\n headers: Headers;\n status: number;\n}\n\nconst HTML_CONTENT_TYPE = 'text/html';\nconst CONTENT_TYPE = 'Content-Type';\n\n/**\n * Distinguish navigational redirects from bodyless statuses such as 304.\n */\nconst isRedirect = (response?: Response | null): boolean =>\n Boolean(\n response && response.status >= 300 && response.status < 400 && response.headers.has('Location'),\n );\n\n/**\n * Build a bodyless HEAD or generic 500 response when React cannot produce a shell.\n */\nconst createShellErrorResponse = <TAppProps,>(\n context: ISsrRequestContext<TAppProps>,\n error: unknown,\n onShellError: ICoreRenderOptions<TAppProps>['onShellError'],\n): Response => {\n const shellError = error instanceof Error ? error : new Error(String(error));\n const html =\n onShellError?.({ context, error: shellError }) || '<!doctype html><p>Internal Server Error</p>';\n const headers = new Headers(context.response.headers);\n\n headers.set(CONTENT_TYPE, HTML_CONTENT_TYPE);\n\n return new Response(context.request.method === 'HEAD' ? null : html, {\n headers,\n status: 500,\n });\n};\n\n/**\n * Prepare the document shell and let hooks finalize response metadata synchronously.\n */\nconst prepareHtmlResponse = <TAppProps,>(\n context: ISsrRequestContext<TAppProps>,\n { getState, onShellReady }: ICoreRenderOptions<TAppProps>,\n): IHtmlResponse => {\n const serverResponse = context.serverContext!.response;\n\n context.response.status =\n serverResponse?.status ?? context.response.status ?? context.routerContext!.statusCode ?? 200;\n\n if (!context.response.headers.has(CONTENT_TYPE)) {\n context.response.headers.set(CONTENT_TYPE, HTML_CONTENT_TYPE);\n }\n\n const shell = onShellReady?.({ context }) ?? {};\n const routerState = buildRouterState(context.routerContext!);\n const customState = buildCustomState(getState?.({ context }));\n const header = shell.header || context.html.header;\n const footer = routerState + customState + (shell.footer || context.html.footer);\n const headers = new Headers(context.response.headers);\n\n return { header, footer, headers, status: context.response.status };\n};\n\n/**\n * Coordinate router queries, React rendering and the final Fetch response.\n */\nconst render = async <TAppProps,>(\n { createApp, handler, renderToStream }: ICoreRenderParams<TAppProps>,\n context: ISsrRequestContext<TAppProps>,\n {\n abortDelay = 15_000,\n getState,\n onError,\n onResponse,\n onRouterReady,\n onShellError,\n onShellReady,\n prepare,\n routerRequestContext,\n }: ICoreRenderOptions<TAppProps>,\n executionContext?: ISsrExecutionContext,\n): Promise<Response> => {\n const queried = await handler.query(context.request, {\n requestContext: routerRequestContext ?? context,\n });\n\n if (queried instanceof Response) {\n return headResponse(context.request, mergeResponseHeaders(queried, context.response.headers));\n }\n\n context.routerContext = queried;\n\n await prepare?.({ context, executionContext });\n\n const { isStream = true } = (await onRouterReady?.({ context })) ?? {};\n\n context.isStream = isStream;\n context.serverContext = {\n basename: context.routerContext.basename,\n isServer: true,\n response: null,\n };\n\n const router = createStaticRouter(handler.dataRoutes, context.routerContext);\n const node = (\n <ServerProvider context={context.serverContext}>\n {createApp(\n <StaticRouterProvider router={router} context={context.routerContext} hydrate={false} />,\n context,\n )}\n </ServerProvider>\n );\n const renderController = new AbortController();\n let abortReason: unknown;\n\n /**\n * Assigned after abort is defined so an async renderer can be cancelled while it initializes.\n */\n let output: IRenderStream | undefined;\n let hasAborted = false;\n\n /**\n * Propagate the request cancellation reason into the renderer.\n */\n const onRequestAbort = (): void => abort(context.request.signal.reason);\n\n /**\n * Clear the deadline when React finishes or the response is cancelled.\n */\n const clearAbortTimer = (): void => clearTimeout(abortTimer);\n\n /**\n * Release the render deadline and request disconnect listener.\n */\n const cleanup = (): void => {\n clearAbortTimer();\n context.request.signal.removeEventListener('abort', onRequestAbort);\n };\n\n /**\n * Cancel rendering once while preserving the reason reported by lifecycle hooks.\n */\n const abort = (reason?: unknown): void => {\n if (hasAborted) {\n return;\n }\n\n hasAborted = true;\n abortReason = reason;\n cleanup();\n context.didError ??= StreamError.RenderCancel;\n renderController.abort(reason);\n output?.abort(reason);\n };\n\n /**\n * Apply the render deadline after routing and preparation finish.\n */\n const abortTimer = setTimeout(() => {\n context.didError = StreamError.RenderTimeout;\n abort();\n }, abortDelay);\n\n if (context.request.signal.aborted) {\n abort(context.request.signal.reason);\n } else {\n context.request.signal.addEventListener('abort', onRequestAbort, { once: true });\n }\n\n try {\n output = await renderToStream(node, {\n /**\n * Classify expected cancellation separately from unexpected React errors.\n */\n onError: (error) => {\n const streamError = obtainStreamError(error);\n\n if (\n hasAborted &&\n (streamError.code === StreamError.RenderAborted ||\n error === renderController.signal.reason)\n ) {\n streamError.code =\n context.didError === StreamError.RenderTimeout\n ? StreamError.RenderTimeout\n : StreamError.RenderCancel;\n }\n\n const { code } = streamError;\n\n context.didError ??= code;\n onError?.({ context, error: streamError });\n },\n signal: renderController.signal,\n });\n\n if (hasAborted) {\n output.abort(abortReason);\n }\n\n void output.allReady.then(clearAbortTimer, clearAbortTimer);\n\n await (isStream ? output.shellReady : output.allReady);\n } catch (error) {\n abort(error);\n await output?.stream.cancel(error).catch(() => undefined);\n\n return createShellErrorResponse(context, error, onShellError);\n }\n\n try {\n const serverResponse = context.serverContext.response;\n\n if (serverResponse && isRedirect(serverResponse)) {\n abort();\n await output.stream.cancel().catch(() => undefined);\n\n return headResponse(\n context.request,\n mergeResponseHeaders(serverResponse, context.response.headers),\n );\n }\n\n const { header, footer, headers, status } = prepareHtmlResponse(context, {\n getState,\n onShellReady,\n });\n\n if (context.request.method === 'HEAD' || [204, 205, 304].includes(status)) {\n abort();\n await output.stream.cancel().catch(() => undefined);\n\n return new Response(null, {\n headers,\n status,\n });\n }\n\n const body = composeHtml(header, output.stream, footer, abort, cleanup);\n const transformed = transformHtml(\n body,\n onResponse ? (html) => onResponse({ context, html }) : undefined,\n );\n\n output.start();\n\n return new Response(transformed, {\n headers,\n status,\n });\n } catch (error) {\n abort(error);\n\n if (!output.stream.locked) {\n await output.stream.cancel(error).catch(() => undefined);\n }\n\n throw error;\n }\n};\n\nexport default render;\n"],"names":["HTML_CONTENT_TYPE","CONTENT_TYPE","render","async","createApp","handler","renderToStream","context","abortDelay","getState","onError","onResponse","onRouterReady","onShellError","onShellReady","prepare","routerRequestContext","executionContext","queried","query","request","requestContext","Response","headResponse","mergeResponseHeaders","response","headers","routerContext","isStream","serverContext","basename","isServer","router","createStaticRouter","dataRoutes","node","React","createElement","ServerProvider","StaticRouterProvider","hydrate","renderController","AbortController","abortReason","output","hasAborted","onRequestAbort","abort","signal","reason","clearAbortTimer","clearTimeout","abortTimer","cleanup","removeEventListener","didError","StreamError","RenderCancel","setTimeout","RenderTimeout","aborted","addEventListener","once","error","streamError","obtainStreamError","code","RenderAborted","allReady","then","shellReady","stream","cancel","catch","shellError","Error","String","html","Headers","set","method","status","createShellErrorResponse","serverResponse","Boolean","has","header","footer","statusCode","shell","routerState","buildRouterState","customState","buildCustomState","prepareHtmlResponse","includes","body","composeHtml","transformed","transformHtml","undefined","start","locked"],"mappings":"8eAuHA,MAAMA,EAAoB,YACpBC,EAAe,eA4DfC,EAASC,OACXC,YAAWC,UAASC,kBACtBC,GAEEC,aAAa,KACbC,WACAC,UACAC,aACAC,gBACAC,eACAC,eACAC,UACAC,wBAEFC,KAEA,MAAMC,QAAgBb,EAAQc,MAAMZ,EAAQa,QAAS,CACnDC,eAAgBL,GAAwBT,IAG1C,GAAIW,aAAmBI,SACrB,OAAOC,EAAahB,EAAQa,QAASI,EAAqBN,EAASX,EAAQkB,SAASC,UAGtFnB,EAAQoB,cAAgBT,QAElBH,IAAU,CAAER,UAASU,sBAE3B,MAAMW,SAAEA,GAAW,SAAgBhB,IAAgB,CAAEL,cAAe,CAAA,EAEpEA,EAAQqB,SAAWA,EACnBrB,EAAQsB,cAAgB,CACtBC,SAAUvB,EAAQoB,cAAcG,SAChCC,UAAU,EACVN,SAAU,MAGZ,MAAMO,EAASC,EAAmB5B,EAAQ6B,WAAY3B,EAAQoB,eACxDQ,EACJC,EAAAC,cAACC,GAAe/B,QAASA,EAAQsB,eAC9BzB,EACCgC,EAAAC,cAACE,EAAoB,CAACP,OAAQA,EAAQzB,QAASA,EAAQoB,cAAea,SAAS,IAC/EjC,IAIAkC,EAAmB,IAAIC,gBAC7B,IAAIC,EAKAC,EACAC,GAAa,EAKjB,MAAMC,EAAiB,IAAYC,EAAMxC,EAAQa,QAAQ4B,OAAOC,QAK1DC,EAAkB,IAAYC,aAAaC,GAK3CC,EAAU,KACdH,IACA3C,EAAQa,QAAQ4B,OAAOM,oBAAoB,QAASR,EAAe,EAM/DC,EAASE,IACTJ,IAIJA,GAAa,EACbF,EAAcM,EACdI,IACA9C,EAAQgD,WAAaC,EAAYC,aACjChB,EAAiBM,MAAME,GACvBL,GAAQG,MAAME,GAAO,EAMjBG,EAAaM,YAAW,KAC5BnD,EAAQgD,SAAWC,EAAYG,cAC/BZ,GAAO,GACNvC,GAECD,EAAQa,QAAQ4B,OAAOY,QACzBb,EAAMxC,EAAQa,QAAQ4B,OAAOC,QAE7B1C,EAAQa,QAAQ4B,OAAOa,iBAAiB,QAASf,EAAgB,CAAEgB,MAAM,IAG3E,IACElB,QAAetC,EAAe6B,EAAM,CAIlCzB,QAAUqD,IACR,MAAMC,EAAcC,EAAkBF,IAGpClB,GACCmB,EAAYE,OAASV,EAAYW,eAChCJ,IAAUtB,EAAiBO,OAAOC,SAEpCe,EAAYE,KACV3D,EAAQgD,WAAaC,EAAYG,cAC7BH,EAAYG,cACZH,EAAYC,cAGpB,MAAMS,KAAEA,GAASF,EAEjBzD,EAAQgD,WAAaW,EACrBxD,IAAU,CAAEH,UAASwD,MAAOC,GAAc,EAE5ChB,OAAQP,EAAiBO,SAGvBH,GACFD,EAAOG,MAAMJ,GAGVC,EAAOwB,SAASC,KAAKnB,EAAiBA,SAEpCtB,EAAWgB,EAAO0B,WAAa1B,EAAOwB,SAC/C,CAAE,MAAOL,GAIP,OAHAhB,EAAMgB,SACAnB,GAAQ2B,OAAOC,OAAOT,GAAOU,OAAM,KAAe,KA1L3B,EAC/BlE,EACAwD,EACAlD,KAEA,MAAM6D,EAAaX,aAAiBY,MAAQZ,EAAQ,IAAIY,MAAMC,OAAOb,IAC/Dc,EACJhE,IAAe,CAAEN,UAASwD,MAAOW,KAAiB,8CAC9ChD,EAAU,IAAIoD,QAAQvE,EAAQkB,SAASC,SAI7C,OAFAA,EAAQqD,IAAI9E,EAAcD,GAEnB,IAAIsB,SAAoC,SAA3Bf,EAAQa,QAAQ4D,OAAoB,KAAOH,EAAM,CACnEnD,UACAuD,OAAQ,KACR,EA6KOC,CAAyB3E,EAASwD,EAAOlD,EAClD,CAEA,IACE,MAAMsE,EAAiB5E,EAAQsB,cAAcJ,SAE7C,GAAI0D,IA1MY1D,EA0MiB0D,EAzMnCC,QACE3D,GAAYA,EAASwD,QAAU,KAAOxD,EAASwD,OAAS,KAAOxD,EAASC,QAAQ2D,IAAI,cA4MlF,OAHAtC,UACMH,EAAO2B,OAAOC,SAASC,OAAM,KAAe,IAE3ClD,EACLhB,EAAQa,QACRI,EAAqB2D,EAAgB5E,EAAQkB,SAASC,UAI1D,MAAM4D,OAAEA,EAAMC,OAAEA,EAAM7D,QAAEA,EAAOuD,OAAEA,GAvLT,EAC1B1E,GACEE,WAAUK,mBAEZ,MAAMqE,EAAiB5E,EAAQsB,cAAeJ,SAE9ClB,EAAQkB,SAASwD,OACfE,GAAgBF,QAAU1E,EAAQkB,SAASwD,QAAU1E,EAAQoB,cAAe6D,YAAc,IAEvFjF,EAAQkB,SAASC,QAAQ2D,IAAIpF,IAChCM,EAAQkB,SAASC,QAAQqD,IAAI9E,EAAcD,GAG7C,MAAMyF,EAAQ3E,IAAe,CAAEP,aAAc,CAAA,EACvCmF,EAAcC,EAAiBpF,EAAQoB,eACvCiE,EAAcC,EAAiBpF,IAAW,CAAEF,aAKlD,MAAO,CAAE+E,OAJMG,EAAMH,QAAU/E,EAAQsE,KAAKS,OAI3BC,OAHFG,EAAcE,GAAeH,EAAMF,QAAUhF,EAAQsE,KAAKU,QAGhD7D,QAFT,IAAIoD,QAAQvE,EAAQkB,SAASC,SAEXuD,OAAQ1E,EAAQkB,SAASwD,OAAQ,EAmKrBa,CAAoBvF,EAAS,CACvEE,WACAK,iBAGF,GAA+B,SAA3BP,EAAQa,QAAQ4D,QAAqB,CAAC,IAAK,IAAK,KAAKe,SAASd,GAIhE,OAHAlC,UACMH,EAAO2B,OAAOC,SAASC,OAAM,KAAe,IAE3C,IAAInD,SAAS,KAAM,CACxBI,UACAuD,WAIJ,MAAMe,EAAOC,EAAYX,EAAQ1C,EAAO2B,OAAQgB,EAAQxC,EAAOM,GACzD6C,EAAcC,EAClBH,EACArF,EAAckE,GAASlE,EAAW,CAAEJ,UAASsE,cAAUuB,GAKzD,OAFAxD,EAAOyD,QAEA,IAAI/E,SAAS4E,EAAa,CAC/BxE,UACAuD,UAEJ,CAAE,MAAOlB,GAOP,MANAhB,EAAMgB,GAEDnB,EAAO2B,OAAO+B,cACX1D,EAAO2B,OAAOC,OAAOT,GAAOU,OAAM,KAAe,IAGnDV,CACR,CAvPiB,IAACtC,CAuPlB"}
@@ -0,0 +1,7 @@
1
+ type TTransformHtml = (html: string) => string | undefined | void;
2
+ /**
3
+ * Apply response hooks to decoded text without splitting UTF-8 characters.
4
+ */
5
+ declare const transformHtml: (stream: ReadableStream<Uint8Array>, transform?: TTransformHtml) => ReadableStream<Uint8Array>;
6
+ export type { TTransformHtml };
7
+ export { transformHtml as default };
@@ -0,0 +1,2 @@
1
+ const e=(e,r)=>{if(!r)return e;const n=new TextDecoder,o=new TextEncoder,t=(e,n)=>{e&&n.enqueue(o.encode(r(e)||e))};return e.pipeThrough(new TransformStream({flush:e=>t(n.decode(),e),transform:(e,r)=>t(n.decode(e,{stream:!0}),r)}))};export{e as default};
2
+ //# sourceMappingURL=transform-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform-html.js","sources":["../../src/core/transform-html.ts"],"sourcesContent":["type TTransformHtml = (html: string) => string | undefined | void;\n\n/**\n * Apply response hooks to decoded text without splitting UTF-8 characters.\n */\nconst transformHtml = (\n stream: ReadableStream<Uint8Array>,\n transform?: TTransformHtml,\n): ReadableStream<Uint8Array> => {\n if (!transform) {\n return stream;\n }\n\n const decoder = new TextDecoder();\n const encoder = new TextEncoder();\n\n /**\n * Keep the original HTML when the response hook does not replace a chunk.\n */\n const apply = (html: string, controller: TransformStreamDefaultController<Uint8Array>): void => {\n if (!html) {\n return;\n }\n\n controller.enqueue(encoder.encode(transform(html) || html));\n };\n\n return stream.pipeThrough(\n new TransformStream<Uint8Array, Uint8Array>({\n /**\n * Flush any final characters retained by the streaming decoder.\n */\n flush: (controller) => apply(decoder.decode(), controller),\n\n /**\n * Decode successive chunks using the same UTF-8 state.\n */\n transform: (chunk, controller) => apply(decoder.decode(chunk, { stream: true }), controller),\n }),\n );\n};\n\nexport type { TTransformHtml };\n\nexport default transformHtml;\n"],"names":["transformHtml","stream","transform","decoder","TextDecoder","encoder","TextEncoder","apply","html","controller","enqueue","encode","pipeThrough","TransformStream","flush","decode","chunk"],"mappings":"AAKA,MAAMA,EAAgB,CACpBC,EACAC,KAEA,IAAKA,EACH,OAAOD,EAGT,MAAME,EAAU,IAAIC,YACdC,EAAU,IAAIC,YAKdC,EAAQ,CAACC,EAAcC,KACtBD,GAILC,EAAWC,QAAQL,EAAQM,OAAOT,EAAUM,IAASA,GAAM,EAG7D,OAAOP,EAAOW,YACZ,IAAIC,gBAAwC,CAI1CC,MAAQL,GAAeF,EAAMJ,EAAQY,SAAUN,GAK/CP,UAAW,CAACc,EAAOP,IAAeF,EAAMJ,EAAQY,OAAOC,EAAO,CAAEf,QAAQ,IAASQ,KAEpF"}
@@ -0,0 +1,8 @@
1
+ interface ISsrExecutionContext {
2
+ /**
3
+ * Deliver informational headers outside the final Fetch response when supported.
4
+ */
5
+ onEarlyHints?: (headers: Headers) => Promise<void> | void;
6
+ }
7
+ type TSsrHandler = (request: Request, context?: ISsrExecutionContext) => Promise<Response>;
8
+ export { ISsrExecutionContext, TSsrHandler };
package/core/types.js ADDED
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import { TRenderToStream } from "../core/render.js";
2
+ /**
3
+ * Adapt React Web streams and settle readiness even when initialization is aborted.
4
+ */
5
+ declare const renderToStream: TRenderToStream;
6
+ export { renderToStream as default };
@@ -0,0 +1,2 @@
1
+ import*as e from"react-dom/server";const r=async()=>e.renderToReadableStream??(await import("react-dom/server.browser")).renderToReadableStream,a=async(e,{onError:a,signal:t})=>{const o=new AbortController;let n;const s=new Promise(((e,r)=>{n=r})),l=()=>{const{reason:e}=t;o.abort(e);const{reason:r}=o.signal;n(r instanceof Error?r:new Error(String(r)))};let c;t.aborted?l():t.addEventListener("abort",l,{once:!0});try{c=await Promise.race([r().then((r=>r(e,{onError:a,signal:o.signal}))),s])}catch(e){t.removeEventListener("abort",l);const r=Promise.reject(e);return r.catch((()=>{})),{allReady:r,abort:e=>o.abort(e),shellReady:r,start:()=>{},stream:new ReadableStream}}const d=()=>t.removeEventListener("abort",l);return c.allReady.then(d,d),{allReady:c.allReady,abort:e=>o.abort(e),shellReady:Promise.resolve(),start:()=>{},stream:c}};export{a as default};
2
+ //# sourceMappingURL=render-to-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-to-stream.js","sources":["../../src/edge/render-to-stream.ts"],"sourcesContent":["import * as ReactDOMServer from 'react-dom/server';\nimport type { TRenderToStream } from '@core/render';\n\n/**\n * Let React select its workerd/Bun/Deno renderer. Node's React 18 entry lacks Web streams.\n */\nconst getRenderer = async (): Promise<typeof ReactDOMServer.renderToReadableStream> =>\n ReactDOMServer.renderToReadableStream ??\n (await import('react-dom/server.browser')).renderToReadableStream;\n\n/**\n * Adapt React Web streams and settle readiness even when initialization is aborted.\n */\nconst renderToStream: TRenderToStream = async (node, { onError, signal }) => {\n const controller = new AbortController();\n let rejectPending!: (reason: Error) => void;\n\n /**\n * Interrupt initialization even if React never settles its shell promise.\n */\n const pendingAbort = new Promise<never>((_, reject) => {\n rejectPending = reject;\n });\n\n /**\n * Reject pending readiness when the platform request is cancelled.\n */\n const abort = (): void => {\n const { reason: signalReason } = signal as { reason: unknown };\n\n controller.abort(signalReason);\n\n const { reason } = controller.signal as { reason: unknown };\n\n rejectPending(reason instanceof Error ? reason : new Error(String(reason)));\n };\n\n if (signal.aborted) {\n abort();\n } else {\n signal.addEventListener('abort', abort, { once: true });\n }\n\n let stream: Awaited<ReturnType<typeof ReactDOMServer.renderToReadableStream>>;\n\n try {\n stream = await Promise.race([\n getRenderer().then((renderToReadableStream) =>\n renderToReadableStream(node, { onError, signal: controller.signal }),\n ),\n pendingAbort,\n ]);\n } catch (error) {\n signal.removeEventListener('abort', abort);\n const shellError = Promise.reject(error);\n\n void shellError.catch(() => undefined);\n\n return {\n allReady: shellError,\n\n /**\n * Cancel the Web renderer with the reason supplied by the core.\n */\n abort: (reason) => controller.abort(reason),\n shellReady: shellError,\n\n /**\n * Web rendering starts through pulls and needs no separate pipe operation.\n */\n start: () => undefined,\n stream: new ReadableStream<Uint8Array>(),\n };\n }\n\n /**\n * Detach the cancellation listener once React settles.\n */\n const removeAbortListener = (): void => signal.removeEventListener('abort', abort);\n\n void stream.allReady.then(removeAbortListener, removeAbortListener);\n\n return {\n allReady: stream.allReady,\n\n /**\n * Cancel the Web renderer with the reason supplied by the core.\n */\n abort: (reason) => controller.abort(reason),\n shellReady: Promise.resolve(),\n\n /**\n * Web rendering starts through pulls and needs no separate pipe operation.\n */\n start: () => undefined,\n stream,\n };\n};\n\nexport default renderToStream;\n"],"names":["getRenderer","async","ReactDOMServer","renderToReadableStream","import","renderToStream","node","onError","signal","controller","AbortController","rejectPending","pendingAbort","Promise","_","reject","abort","reason","signalReason","Error","String","stream","aborted","addEventListener","once","race","then","error","removeEventListener","shellError","catch","allReady","shellReady","start","ReadableStream","removeAbortListener","resolve"],"mappings":"mCAMA,MAAMA,EAAcC,SAClBC,EAAeC,+BACRC,OAAO,6BAA6BD,uBAKvCE,EAAkCJ,MAAOK,GAAQC,UAASC,aAC9D,MAAMC,EAAa,IAAIC,gBACvB,IAAIC,EAKJ,MAAMC,EAAe,IAAIC,SAAe,CAACC,EAAGC,KAC1CJ,EAAgBI,CAAM,IAMlBC,EAAQ,KACZ,MAAQC,OAAQC,GAAiBV,EAEjCC,EAAWO,MAAME,GAEjB,MAAMD,OAAEA,GAAWR,EAAWD,OAE9BG,EAAcM,aAAkBE,MAAQF,EAAS,IAAIE,MAAMC,OAAOH,IAAS,EAS7E,IAAII,EANAb,EAAOc,QACTN,IAEAR,EAAOe,iBAAiB,QAASP,EAAO,CAAEQ,MAAM,IAKlD,IACEH,QAAeR,QAAQY,KAAK,CAC1BzB,IAAc0B,MAAMvB,GAClBA,EAAuBG,EAAM,CAAEC,UAASC,OAAQC,EAAWD,WAE7DI,GAEJ,CAAE,MAAOe,GACPnB,EAAOoB,oBAAoB,QAASZ,GACpC,MAAMa,EAAahB,QAAQE,OAAOY,GAIlC,OAFKE,EAAWC,OAAM,KAAe,IAE9B,CACLC,SAAUF,EAKVb,MAAQC,GAAWR,EAAWO,MAAMC,GACpCe,WAAYH,EAKZI,MAAO,KAAe,EACtBZ,OAAQ,IAAIa,eAEhB,CAKA,MAAMC,EAAsB,IAAY3B,EAAOoB,oBAAoB,QAASZ,GAI5E,OAFKK,EAAOU,SAASL,KAAKS,EAAqBA,GAExC,CACLJ,SAAUV,EAAOU,SAKjBf,MAAQC,GAAWR,EAAWO,MAAMC,GACpCe,WAAYnB,QAAQuB,UAKpBH,MAAO,KAAe,EACtBZ,SACD"}
@@ -1,6 +1,5 @@
1
- import { IRenderOptions } from "../node/render.js";
2
1
  /**
3
2
  * Build custom state
4
3
  */
5
- declare function buildCustomState(initState?: ReturnType<NonNullable<IRenderOptions['getState']>>): string;
4
+ declare function buildCustomState(initState?: Record<string, Record<string, any>> | void): string;
6
5
  export { buildCustomState as default };
@@ -1,2 +1,2 @@
1
- import t from"./html-escape.js";function r(r){return Object.entries(r??{}).map((([r,e])=>{if(!r||!e||!Object.keys(e||{}).length)return"";return`<script async>window.${r} = JSON.parse(${t(JSON.stringify(JSON.stringify(e)))});<\/script>`})).join("").trim()}export{r as default};
1
+ import t from"./html-escape.js";function r(r){return Object.entries(r??{}).map((([r,e])=>{if(!r||!e||!Object.keys(e||{}).length)return"";const i=t(JSON.stringify(JSON.stringify(e)));return`<script async>window${/^[A-Za-z_$][\w$]*$/.test(r)?`.${r}`:`[${t(JSON.stringify(r))}]`} = JSON.parse(${i});<\/script>`})).join("").trim()}export{r as default};
2
2
  //# sourceMappingURL=build-custom-state.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-custom-state.js","sources":["../../src/helpers/build-custom-state.ts"],"sourcesContent":["import htmlEscape from '@helpers/html-escape';\nimport type { IRenderOptions } from '@node/render';\n\n/**\n * Build custom state\n */\nfunction buildCustomState(initState?: ReturnType<NonNullable<IRenderOptions['getState']>>): string {\n const stateScripts = Object.entries(initState ?? {}).map(([key, state]) => {\n if (!key || !state || !Object.keys(state || {}).length) {\n return '';\n }\n\n const json = htmlEscape(JSON.stringify(JSON.stringify(state)));\n\n return `<script async>window.${key} = JSON.parse(${json});</script>`;\n });\n\n return stateScripts.join('').trim();\n}\n\nexport default buildCustomState;\n"],"names":["buildCustomState","initState","Object","entries","map","key","state","keys","length","htmlEscape","JSON","stringify","join","trim"],"mappings":"gCAMA,SAASA,EAAiBC,GAWxB,OAVqBC,OAAOC,QAAQF,GAAa,CAAA,GAAIG,KAAI,EAAEC,EAAKC,MAC9D,IAAKD,IAAQC,IAAUJ,OAAOK,KAAKD,GAAS,IAAIE,OAC9C,MAAO,GAKT,MAAO,wBAAwBH,kBAFlBI,EAAWC,KAAKC,UAAUD,KAAKC,UAAUL,kBAEc,IAGlDM,KAAK,IAAIC,MAC/B"}
1
+ {"version":3,"file":"build-custom-state.js","sources":["../../src/helpers/build-custom-state.ts"],"sourcesContent":["import htmlEscape from '@helpers/html-escape';\n\n/**\n * Build custom state\n */\nfunction buildCustomState(initState?: Record<string, Record<string, any>> | void): string {\n const stateScripts = Object.entries(initState ?? {}).map(([key, state]) => {\n if (!key || !state || !Object.keys(state || {}).length) {\n return '';\n }\n\n const json = htmlEscape(JSON.stringify(JSON.stringify(state)));\n const property = /^[A-Za-z_$][\\w$]*$/.test(key)\n ? `.${key}`\n : `[${htmlEscape(JSON.stringify(key))}]`;\n\n return `<script async>window${property} = JSON.parse(${json});</script>`;\n });\n\n return stateScripts.join('').trim();\n}\n\nexport default buildCustomState;\n"],"names":["buildCustomState","initState","Object","entries","map","key","state","keys","length","json","htmlEscape","JSON","stringify","test","join","trim"],"mappings":"gCAKA,SAASA,EAAiBC,GAcxB,OAbqBC,OAAOC,QAAQF,GAAa,CAAA,GAAIG,KAAI,EAAEC,EAAKC,MAC9D,IAAKD,IAAQC,IAAUJ,OAAOK,KAAKD,GAAS,IAAIE,OAC9C,MAAO,GAGT,MAAMC,EAAOC,EAAWC,KAAKC,UAAUD,KAAKC,UAAUN,KAKtD,MAAO,uBAJU,qBAAqBO,KAAKR,GACvC,IAAIA,IACJ,IAAIK,EAAWC,KAAKC,UAAUP,uBAEqBI,eAAiB,IAGtDK,KAAK,IAAIC,MAC/B"}
@@ -1,2 +1,2 @@
1
- import e from"../constants/stream-error.js";const r=r=>{const o=(r?.message??"Unknown.").replace("Error: ","");return"The render was aborted by the server without a reason."===o?{code:e.RenderAborted,message:o,original:r}:"The destination stream closed early."===o?{code:e.RenderSkip,message:o,original:r}:{code:e.Unknown,message:o||"Unknown error",original:r}};export{r as default};
1
+ import e from"../constants/stream-error.js";const r=r=>{const o=(r?.message??"Unknown.").replace("Error: ","");return"AbortError"===r?.name||"The render was aborted by the server without a reason."===o?{code:e.RenderAborted,message:o,original:r}:"The destination stream closed early."===o?{code:e.RenderSkip,message:o,original:r}:{code:e.Unknown,message:o||"Unknown error",original:r}};export{r as default};
2
2
  //# sourceMappingURL=obtain-stream-error.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"obtain-stream-error.js","sources":["../../src/helpers/obtain-stream-error.ts"],"sourcesContent":["import StreamError from '@constants/stream-error';\n\nexport interface IObtainStreamErrorOut {\n code: StreamError;\n message: string;\n original: unknown;\n}\n\n/**\n * Get react stream error\n */\nconst obtainStreamError = (err: unknown): IObtainStreamErrorOut => {\n const message = ((err as Error)?.message ?? 'Unknown.').replace('Error: ', '');\n\n if (message === 'The render was aborted by the server without a reason.') {\n return {\n code: StreamError.RenderAborted,\n message,\n original: err,\n };\n } else if (message === 'The destination stream closed early.') {\n return {\n code: StreamError.RenderSkip,\n message,\n original: err,\n };\n }\n\n return {\n code: StreamError.Unknown,\n message: message || 'Unknown error',\n original: err,\n };\n};\n\nexport default obtainStreamError;\n"],"names":["obtainStreamError","err","message","replace","code","StreamError","RenderAborted","original","RenderSkip","Unknown"],"mappings":"4CAWA,MAAMA,EAAqBC,IACzB,MAAMC,GAAYD,GAAeC,SAAW,YAAYC,QAAQ,UAAW,IAE3E,MAAgB,2DAAZD,EACK,CACLE,KAAMC,EAAYC,cAClBJ,UACAK,SAAUN,GAES,yCAAZC,EACF,CACLE,KAAMC,EAAYG,WAClBN,UACAK,SAAUN,GAIP,CACLG,KAAMC,EAAYI,QAClBP,QAASA,GAAW,gBACpBK,SAAUN,EACX"}
1
+ {"version":3,"file":"obtain-stream-error.js","sources":["../../src/helpers/obtain-stream-error.ts"],"sourcesContent":["import StreamError from '@constants/stream-error';\n\nexport interface IObtainStreamErrorOut {\n code: StreamError;\n message: string;\n original: unknown;\n}\n\n/**\n * Get react stream error\n */\nconst obtainStreamError = (err: unknown): IObtainStreamErrorOut => {\n const message = ((err as Error)?.message ?? 'Unknown.').replace('Error: ', '');\n\n if (\n (err as Error)?.name === 'AbortError' ||\n message === 'The render was aborted by the server without a reason.'\n ) {\n return {\n code: StreamError.RenderAborted,\n message,\n original: err,\n };\n } else if (message === 'The destination stream closed early.') {\n return {\n code: StreamError.RenderSkip,\n message,\n original: err,\n };\n }\n\n return {\n code: StreamError.Unknown,\n message: message || 'Unknown error',\n original: err,\n };\n};\n\nexport default obtainStreamError;\n"],"names":["obtainStreamError","err","message","replace","name","code","StreamError","RenderAborted","original","RenderSkip","Unknown"],"mappings":"4CAWA,MAAMA,EAAqBC,IACzB,MAAMC,GAAYD,GAAeC,SAAW,YAAYC,QAAQ,UAAW,IAE3E,MAC2B,eAAxBF,GAAeG,MACJ,2DAAZF,EAEO,CACLG,KAAMC,EAAYC,cAClBL,UACAM,SAAUP,GAES,yCAAZC,EACF,CACLG,KAAMC,EAAYG,WAClBP,UACAM,SAAUP,GAIP,CACLI,KAAMC,EAAYI,QAClBR,QAASA,GAAW,gBACpBM,SAAUP,EACX"}
@@ -1,7 +1,7 @@
1
1
  import { FC, PropsWithChildren } from 'react';
2
2
  import { RouteObject } from 'react-router';
3
+ import { IRequestContext } from "../adapters/express/render.js";
3
4
  import { FCC } from "./fc.js";
4
- import { IRequestContext } from "../node/render.js";
5
5
  declare module 'react-router' {
6
6
  interface LoaderFunctionArgs {
7
7
  context?: IRequestContext;
@@ -1 +1 @@
1
- {"version":3,"file":"fc-route.js","sources":["../../src/interfaces/fc-route.ts"],"sourcesContent":["import type { FC, PropsWithChildren } from 'react';\nimport type { RouteObject } from 'react-router';\nimport type { FCC } from '@interfaces/fc';\nimport type { IRequestContext } from '@node/render';\n\ndeclare module 'react-router' {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n export interface LoaderFunctionArgs {\n context?: IRequestContext;\n }\n\n // eslint-disable-next-line @typescript-eslint/naming-convention\n export interface ActionFunctionArgs {\n context?: IRequestContext;\n }\n}\n\nconst keys = ['loader', 'action', 'ErrorBoundary', 'errorElement'] as const;\n\ntype IRouteParams = Pick<RouteObject, (typeof keys)[number]> & {\n Suspense?: FCC<Record<string, any>>;\n};\n\ntype FCRoute<TProps = Record<string, any>> = FC<TProps> & IRouteParams;\ntype FCCRoute<TProps = Record<string, any>> = FC<PropsWithChildren<TProps>> & IRouteParams;\n\nexport type { FCRoute, FCCRoute, IRouteParams };\n\nexport { keys };\n"],"names":["keys"],"mappings":"AAiBA,MAAMA,EAAO,CAAC,SAAU,SAAU,gBAAiB"}
1
+ {"version":3,"file":"fc-route.js","sources":["../../src/interfaces/fc-route.ts"],"sourcesContent":["import type { FC, PropsWithChildren } from 'react';\nimport type { RouteObject } from 'react-router';\nimport type { IRequestContext } from '@adapters/express/render';\nimport type { FCC } from '@interfaces/fc';\n\ndeclare module 'react-router' {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n export interface LoaderFunctionArgs {\n context?: IRequestContext;\n }\n\n // eslint-disable-next-line @typescript-eslint/naming-convention\n export interface ActionFunctionArgs {\n context?: IRequestContext;\n }\n}\n\nconst keys = ['loader', 'action', 'ErrorBoundary', 'errorElement'] as const;\n\ntype IRouteParams = Pick<RouteObject, (typeof keys)[number]> & {\n Suspense?: FCC<Record<string, any>>;\n};\n\ntype FCRoute<TProps = Record<string, any>> = FC<TProps> & IRouteParams;\ntype FCCRoute<TProps = Record<string, any>> = FC<PropsWithChildren<TProps>> & IRouteParams;\n\nexport type { FCRoute, FCCRoute, IRouteParams };\n\nexport { keys };\n"],"names":["keys"],"mappings":"AAiBA,MAAMA,EAAO,CAAC,SAAU,SAAU,gBAAiB"}
@@ -0,0 +1,6 @@
1
+ import { TCompression } from "../adapters/compression.js";
2
+ /**
3
+ * Flush each compressed chunk so browsers can render the shell while React is suspended.
4
+ */
5
+ declare const compressNodeResponse: (request: Request, response: Response, compression?: TCompression) => Response;
6
+ export { compressNodeResponse as default };
@@ -0,0 +1,2 @@
1
+ import{Writable as e,Readable as r}from"node:stream";import{constants as t,createGzip as a,createDeflate as o}from"node:zlib";import s from"../adapters/compression.js";const i=(i,m,b)=>s(i,m,b,(s=>{const i=("gzip"===s?a:o)({flush:t.Z_SYNC_FLUSH});return{readable:r.toWeb(i,{strategy:{highWaterMark:i.readableHighWaterMark,size:e=>e.byteLength}}),writable:e.toWeb(i)}}));export{i as default};
2
+ //# sourceMappingURL=compress-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compress-response.js","sources":["../../src/node/compress-response.ts"],"sourcesContent":["import { Readable, Writable } from 'node:stream';\nimport { constants, createDeflate, createGzip } from 'node:zlib';\nimport compressResponse from '@adapters/compression';\nimport type { TCompression } from '@adapters/compression';\n\n/**\n * Flush each compressed chunk so browsers can render the shell while React is suspended.\n */\nconst compressNodeResponse = (\n request: Request,\n response: Response,\n compression?: TCompression,\n): Response =>\n compressResponse(request, response, compression, (format) => {\n const create = format === 'gzip' ? createGzip : createDeflate;\n const stream = create({ flush: constants.Z_SYNC_FLUSH });\n\n return {\n readable: Readable.toWeb(stream, {\n strategy: {\n highWaterMark: stream.readableHighWaterMark,\n size: (chunk: Uint8Array) => chunk.byteLength,\n },\n }),\n writable: Writable.toWeb(stream),\n } as ReadableWritablePair<Uint8Array, Uint8Array>;\n });\n\nexport default compressNodeResponse;\n"],"names":["compressNodeResponse","request","response","compression","compressResponse","format","stream","createGzip","createDeflate","flush","constants","Z_SYNC_FLUSH","readable","Readable","toWeb","strategy","highWaterMark","readableHighWaterMark","size","chunk","byteLength","writable","Writable"],"mappings":"wKAQA,MAAMA,EAAuB,CAC3BC,EACAC,EACAC,IAEAC,EAAiBH,EAASC,EAAUC,GAAcE,IAChD,MACMC,GADoB,SAAXD,EAAoBE,EAAaC,GAC1B,CAAEC,MAAOC,EAAUC,eAEzC,MAAO,CACLC,SAAUC,EAASC,MAAMR,EAAQ,CAC/BS,SAAU,CACRC,cAAeV,EAAOW,sBACtBC,KAAOC,GAAsBA,EAAMC,cAGvCC,SAAUC,EAASR,MAAMR,GACsB"}
@@ -0,0 +1,6 @@
1
+ type TNodeHeaders = Record<string, string | string[] | number | undefined>;
2
+ /**
3
+ * Convert Node headers to Fetch headers, excluding HTTP/2 pseudo-headers.
4
+ */
5
+ declare const createHeaders: (values: TNodeHeaders) => Headers;
6
+ export { createHeaders as default };
@@ -0,0 +1,2 @@
1
+ const e=e=>{const t=new Headers;for(const[r,s]of Object.entries(e))if(!r.startsWith(":")&&void 0!==s)if(Array.isArray(s))for(const e of s)t.append(r,e);else t.set(r,"number"==typeof s?String(s):s);return t};export{e as default};
2
+ //# sourceMappingURL=create-headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-headers.js","sources":["../../src/node/create-headers.ts"],"sourcesContent":["type TNodeHeaders = Record<string, string | string[] | number | undefined>;\n\n/**\n * Convert Node headers to Fetch headers, excluding HTTP/2 pseudo-headers.\n */\nconst createHeaders = (values: TNodeHeaders): Headers => {\n const headers = new Headers();\n\n for (const [name, value] of Object.entries(values)) {\n if (name.startsWith(':') || value === undefined) {\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n } else {\n headers.set(name, typeof value === 'number' ? String(value) : value);\n }\n }\n\n return headers;\n};\n\nexport default createHeaders;\n"],"names":["createHeaders","values","headers","Headers","name","value","Object","entries","startsWith","undefined","Array","isArray","item","append","set","String"],"mappings":"AAKA,MAAMA,EAAiBC,IACrB,MAAMC,EAAU,IAAIC,QAEpB,IAAK,MAAOC,EAAMC,KAAUC,OAAOC,QAAQN,GACzC,IAAIG,EAAKI,WAAW,WAAkBC,IAAVJ,EAI5B,GAAIK,MAAMC,QAAQN,GAChB,IAAK,MAAMO,KAAQP,EACjBH,EAAQW,OAAOT,EAAMQ,QAGvBV,EAAQY,IAAIV,EAAuB,iBAAVC,EAAqBU,OAAOV,GAASA,GAIlE,OAAOH,CAAO"}
@@ -0,0 +1,13 @@
1
+ import { TIncomingMessage } from "./http.js";
2
+ interface ICreateRequestOptions {
3
+ body?: BodyInit | null;
4
+ origin?: string;
5
+ signal?: AbortSignal;
6
+ url?: string;
7
+ }
8
+ /**
9
+ * Preserve the Node request URL, body and cancellation signal for Fetch handlers.
10
+ */
11
+ declare const createRequest: (req: TIncomingMessage, options?: ICreateRequestOptions) => Request;
12
+ export type { ICreateRequestOptions };
13
+ export { createRequest as default };
@@ -0,0 +1,2 @@
1
+ import e from"./create-headers.js";const t=(t,o={})=>{const{body:n,origin:r,signal:s}=o,d=e(t.headers),a=t.socket&&"encrypted"in t.socket&&t.socket.encrypted?"https":"http",h=t.headers[":authority"],i=d.get("host")??("string"==typeof h?h:null),l=new URL(r??`${a}://${i}`).origin,c=o.url??t.url??"/",p=new URL(c.startsWith("/")?l+c:c,l),y={headers:d,method:t.method,signal:s};return"GET"!==t.method&&"HEAD"!==t.method&&("body"in o?(d.delete("Content-Length"),n instanceof FormData&&d.delete("Content-Type"),y.body=n):y.body=t,y.duplex="half"),new Request(p,y)};export{t as default};
2
+ //# sourceMappingURL=create-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-request.js","sources":["../../src/node/create-request.ts"],"sourcesContent":["import createHeaders from '@node/create-headers';\nimport type { TIncomingMessage } from '@node/http';\n\ninterface ICreateRequestOptions {\n body?: BodyInit | null;\n origin?: string;\n signal?: AbortSignal;\n url?: string;\n}\n\n/**\n * Preserve the Node request URL, body and cancellation signal for Fetch handlers.\n */\nconst createRequest = (req: TIncomingMessage, options: ICreateRequestOptions = {}): Request => {\n const { body, origin, signal } = options;\n const headers = createHeaders(req.headers);\n const protocol =\n req.socket && 'encrypted' in req.socket && req.socket.encrypted ? 'https' : 'http';\n const authority = req.headers[':authority'];\n const host = headers.get('host') ?? (typeof authority === 'string' ? authority : null);\n const requestOrigin = new URL(origin ?? `${protocol}://${host}`).origin;\n const target = options.url ?? req.url ?? '/';\n\n /**\n * An origin-form target beginning with // is a path, not a replacement hostname.\n */\n const url = new URL(target.startsWith('/') ? requestOrigin + target : target, requestOrigin);\n const init: RequestInit & { duplex?: 'half' } = {\n headers,\n method: req.method,\n signal,\n };\n\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n if ('body' in options) {\n headers.delete('Content-Length');\n\n if (body instanceof FormData) {\n /**\n * Fetch must generate the boundary for a newly serialized multipart body.\n */\n headers.delete('Content-Type');\n }\n\n init.body = body;\n } else {\n init.body = req as unknown as BodyInit;\n }\n\n init.duplex = 'half';\n }\n\n return new Request(url, init);\n};\n\nexport type { ICreateRequestOptions };\n\nexport default createRequest;\n"],"names":["createRequest","req","options","body","origin","signal","headers","createHeaders","protocol","socket","encrypted","authority","host","get","requestOrigin","URL","target","url","startsWith","init","method","delete","FormData","duplex","Request"],"mappings":"mCAaA,MAAMA,EAAgB,CAACC,EAAuBC,EAAiC,MAC7E,MAAMC,KAAEA,EAAIC,OAAEA,EAAMC,OAAEA,GAAWH,EAC3BI,EAAUC,EAAcN,EAAIK,SAC5BE,EACJP,EAAIQ,QAAU,cAAeR,EAAIQ,QAAUR,EAAIQ,OAAOC,UAAY,QAAU,OACxEC,EAAYV,EAAIK,QAAQ,cACxBM,EAAON,EAAQO,IAAI,UAAiC,iBAAdF,EAAyBA,EAAY,MAC3EG,EAAgB,IAAIC,IAAIX,GAAU,GAAGI,OAAcI,KAAQR,OAC3DY,EAASd,EAAQe,KAAOhB,EAAIgB,KAAO,IAKnCA,EAAM,IAAIF,IAAIC,EAAOE,WAAW,KAAOJ,EAAgBE,EAASA,EAAQF,GACxEK,EAA0C,CAC9Cb,UACAc,OAAQnB,EAAImB,OACZf,UAsBF,MAnBmB,QAAfJ,EAAImB,QAAmC,SAAfnB,EAAImB,SAC1B,SAAUlB,GACZI,EAAQe,OAAO,kBAEXlB,aAAgBmB,UAIlBhB,EAAQe,OAAO,gBAGjBF,EAAKhB,KAAOA,GAEZgB,EAAKhB,KAAOF,EAGdkB,EAAKI,OAAS,QAGT,IAAIC,QAAQP,EAAKE,EAAK"}
package/node/http.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /// <reference types="node" />
2
+ import { IncomingMessage, ServerResponse } from 'node:http';
3
+ import { Http2ServerRequest, Http2ServerResponse } from 'node:http2';
4
+ type TIncomingMessage = IncomingMessage | Http2ServerRequest;
5
+ type TServerResponse = ServerResponse | Http2ServerResponse;
6
+ export { TIncomingMessage, TServerResponse };
package/node/http.js ADDED
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import { TRenderToStream } from "../core/render.js";
2
+ /**
3
+ * Adapt React pipeable rendering to Fetch streams with byte-based backpressure.
4
+ */
5
+ declare const renderToStream: TRenderToStream;
6
+ export { renderToStream as default };
@@ -0,0 +1,2 @@
1
+ import{PassThrough as r,Readable as e}from"node:stream";import{renderToPipeableStream as t}from"react-dom/server";const o=(o,n)=>{const a=new r,s=e.toWeb(a,{strategy:{highWaterMark:a.readableHighWaterMark,size:r=>r.byteLength}});let i,l,c,d;const g=new Promise(((r,e)=>{i=e,c=r})),h=new Promise(((r,e)=>{l=e,d=r}));g.catch((()=>{})),h.catch((()=>{}));let m=!1,b=!1;const E=t(o,{onAllReady:c,onError:n.onError,onShellError:r=>{const e=r instanceof Error?r:new Error(String(r));m=!0,i(e),l(e)},onShellReady:()=>{m=!0,d()}}),f=r=>{if(!b){if(b=!0,!m){m=!0;const e=r instanceof Error?r:new Error("string"==typeof r?r:"Render aborted");l(e),i(e)}E.abort(r)}},y=()=>f(n.signal.reason),p=()=>n.signal.removeEventListener("abort",y);n.signal.aborted?y():n.signal.addEventListener("abort",y,{once:!0}),g.then(p,p);let w=!1;return{allReady:g,abort:f,shellReady:h,start:()=>{w||(w=!0,E.pipe(a))},stream:s}};export{o as default};
2
+ //# sourceMappingURL=render-to-stream.js.map