@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.
- package/README.md +17 -1
- package/adapters/body.d.ts +5 -0
- package/adapters/body.js +2 -0
- package/adapters/body.js.map +1 -0
- package/adapters/compression.d.ts +11 -0
- package/adapters/compression.js +2 -0
- package/adapters/compression.js.map +1 -0
- package/adapters/edge.d.ts +11 -0
- package/adapters/edge.js +2 -0
- package/adapters/edge.js.map +1 -0
- package/adapters/express/create-request.d.ts +11 -0
- package/adapters/express/create-request.js +2 -0
- package/adapters/express/create-request.js.map +1 -0
- package/{node → adapters/express}/entry.d.ts +8 -14
- package/adapters/express/entry.js.map +1 -0
- package/{services → adapters/express}/prepare-server.d.ts +14 -21
- package/adapters/express/prepare-server.js +2 -0
- package/adapters/express/prepare-server.js.map +1 -0
- package/{node → adapters/express}/render.d.ts +8 -8
- package/adapters/express/render.js +2 -0
- package/adapters/express/render.js.map +1 -0
- package/{node → adapters/express}/server.d.ts +1 -1
- package/adapters/express/server.js +2 -0
- package/adapters/express/server.js.map +1 -0
- package/adapters/express.d.ts +11 -0
- package/adapters/express.js +2 -0
- package/adapters/express.js.map +1 -0
- package/adapters/fastify.d.ts +23 -0
- package/adapters/fastify.js +2 -0
- package/adapters/fastify.js.map +1 -0
- package/adapters/hono.d.ts +13 -0
- package/adapters/hono.js +2 -0
- package/adapters/hono.js.map +1 -0
- package/adapters/node.d.ts +18 -0
- package/adapters/node.js +2 -0
- package/adapters/node.js.map +1 -0
- package/cli/run-dev.js +1 -1
- package/cli/run-dev.js.map +1 -1
- package/cli/run-prod.d.ts +1 -1
- package/cli/run-prod.js +1 -1
- package/cli/run-prod.js.map +1 -1
- package/cli/run-serverless.d.ts +2 -2
- package/cli/run-serverless.js +1 -1
- package/cli/run-serverless.js.map +1 -1
- package/components/response-status.js +1 -1
- package/components/response-status.js.map +1 -1
- package/core/compose-html.d.ts +5 -0
- package/core/compose-html.js +2 -0
- package/core/compose-html.js.map +1 -0
- package/core/early-hints.d.ts +6 -0
- package/core/early-hints.js +2 -0
- package/core/early-hints.js.map +1 -0
- package/core/handler.d.ts +30 -0
- package/core/handler.js +2 -0
- package/core/handler.js.map +1 -0
- package/core/head-response.d.ts +5 -0
- package/core/head-response.js +2 -0
- package/core/head-response.js.map +1 -0
- package/core/headers.d.ts +13 -0
- package/core/headers.js +2 -0
- package/core/headers.js.map +1 -0
- package/core/render.d.ts +101 -0
- package/core/render.js +2 -0
- package/core/render.js.map +1 -0
- package/core/transform-html.d.ts +7 -0
- package/core/transform-html.js +2 -0
- package/core/transform-html.js.map +1 -0
- package/core/types.d.ts +8 -0
- package/core/types.js +2 -0
- package/core/types.js.map +1 -0
- package/edge/render-to-stream.d.ts +6 -0
- package/edge/render-to-stream.js +2 -0
- package/edge/render-to-stream.js.map +1 -0
- package/helpers/build-custom-state.d.ts +1 -2
- package/helpers/build-custom-state.js +1 -1
- package/helpers/build-custom-state.js.map +1 -1
- package/helpers/obtain-stream-error.js +1 -1
- package/helpers/obtain-stream-error.js.map +1 -1
- package/interfaces/fc-route.d.ts +1 -1
- package/interfaces/fc-route.js.map +1 -1
- package/node/compress-response.d.ts +6 -0
- package/node/compress-response.js +2 -0
- package/node/compress-response.js.map +1 -0
- package/node/create-headers.d.ts +6 -0
- package/node/create-headers.js +2 -0
- package/node/create-headers.js.map +1 -0
- package/node/create-request.d.ts +13 -0
- package/node/create-request.js +2 -0
- package/node/create-request.js.map +1 -0
- package/node/http.d.ts +6 -0
- package/node/http.js +2 -0
- package/node/http.js.map +1 -0
- package/node/render-to-stream.d.ts +6 -0
- package/node/render-to-stream.js +2 -0
- package/node/render-to-stream.js.map +1 -0
- package/node/request-signal.d.ts +10 -0
- package/node/request-signal.js +2 -0
- package/node/request-signal.js.map +1 -0
- package/node/split-link-header.d.ts +5 -0
- package/node/split-link-header.js +2 -0
- package/node/split-link-header.js.map +1 -0
- package/node/write-early-hints.d.ts +6 -0
- package/node/write-early-hints.js +2 -0
- package/node/write-early-hints.js.map +1 -0
- package/node/write-fetch-response.d.ts +10 -0
- package/node/write-fetch-response.js +2 -0
- package/node/write-fetch-response.js.map +1 -0
- package/package.json +45 -3
- package/services/server-config.d.ts +3 -4
- package/services/server-config.js.map +1 -1
- package/services/ssr-manifest.d.ts +25 -8
- package/services/ssr-manifest.js +1 -1
- package/services/ssr-manifest.js.map +1 -1
- package/helpers/handle-response.d.ts +0 -7
- package/helpers/handle-response.js +0 -2
- package/helpers/handle-response.js.map +0 -1
- package/node/create-fetch-request.d.ts +0 -7
- package/node/create-fetch-request.js +0 -2
- package/node/create-fetch-request.js.map +0 -1
- package/node/entry.js.map +0 -1
- package/node/render.js +0 -2
- package/node/render.js.map +0 -1
- package/node/server.js +0 -2
- package/node/server.js.map +0 -1
- package/node/write-response.d.ts +0 -14
- package/node/write-response.js +0 -2
- package/node/write-response.js.map +0 -1
- package/services/prepare-server.js +0 -2
- package/services/prepare-server.js.map +0 -1
- /package/{node → adapters/express}/entry.js +0 -0
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ It is built for applications that want to keep Vite and React Router visible, bu
|
|
|
14
14
|
- React Router on both client and server
|
|
15
15
|
- stream rendering with request lifecycle hooks
|
|
16
16
|
- a Vite-native plugin and CLI flow
|
|
17
|
-
-
|
|
17
|
+
- a Fetch-based SSR core with Node, Express, Fastify, Hono and edge adapters
|
|
18
18
|
- response-aware helpers such as redirects and status codes
|
|
19
19
|
- custom entrypoints for mobile, embedded and service-worker-friendly shells
|
|
20
20
|
|
|
@@ -35,10 +35,26 @@ It is built for applications that want to keep Vite and React Router visible, bu
|
|
|
35
35
|
npm i @lomray/vite-ssr-boost
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
Use Node 22 or newer. Start with the [React template](https://github.com/Lomray-Software/vite-template):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
git clone https://github.com/Lomray-Software/vite-template.git
|
|
42
|
+
cd vite-template
|
|
43
|
+
npm ci
|
|
44
|
+
npm run develop
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For production, run `npm run build` and `npm run start:ssr`. For a static SPA, run
|
|
48
|
+
`npm run build:spa` and `npm run start:spa`.
|
|
49
|
+
|
|
38
50
|
## Documentation
|
|
39
51
|
|
|
40
52
|
Full documentation lives here: [lomray-software.github.io/vite-ssr-boost](https://lomray-software.github.io/vite-ssr-boost/)
|
|
41
53
|
|
|
54
|
+
Existing Express applications move one import to `adapters/express/entry`; hooks are unchanged. See
|
|
55
|
+
[runtime adapters](https://lomray-software.github.io/vite-ssr-boost/guide/runtime-adapters)
|
|
56
|
+
for the Fetch API and the differences between the managed CLI server and a custom runtime.
|
|
57
|
+
|
|
42
58
|
## License
|
|
43
59
|
|
|
44
60
|
Made with 💚
|
package/adapters/body.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=(e,r="")=>{if(null==e)return null;if("string"==typeof e||e instanceof URLSearchParams||e instanceof Blob||e instanceof FormData||e instanceof ReadableStream||e instanceof ArrayBuffer||ArrayBuffer.isView(e))return e;const n=r.split(";")[0]?.trim().toLowerCase();if("application/x-www-form-urlencoded"===n)return(e=>{const r=new URLSearchParams;return Object.entries(e).forEach((([e,n])=>{(Array.isArray(n)?n:[n]).forEach((n=>{if(null!=n&&!["string","number","boolean","bigint"].includes(typeof n))throw new TypeError("Cannot serialize nested or non-primitive URL-encoded fields. Provide the adapter getBody option.");r.append(e,null==n?"":String(n))}))})),r})(e);if(!n||"application/json"===n||n.endsWith("+json"))return JSON.stringify(e);throw new TypeError(`Cannot serialize a parsed ${n} body. Provide the adapter getBody option.`)};export{e as default};
|
|
2
|
+
//# sourceMappingURL=body.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.js","sources":["../../src/adapters/body.ts"],"sourcesContent":["/**\n * Serialize flat form fields without silently flattening nested parser output.\n */\nconst serializeForm = (body: Record<string, unknown>): URLSearchParams => {\n const form = new URLSearchParams();\n\n Object.entries(body).forEach(([name, value]) => {\n const values = Array.isArray(value) ? value : [value];\n\n values.forEach((item) => {\n if (item != null && !['string', 'number', 'boolean', 'bigint'].includes(typeof item)) {\n throw new TypeError(\n 'Cannot serialize nested or non-primitive URL-encoded fields. Provide the adapter getBody option.',\n );\n }\n\n form.append(name, item == null ? '' : String(item));\n });\n });\n\n return form;\n};\n\n/**\n * Reconstruct supported parsed bodies while preserving native Fetch body values.\n */\nconst serializeBody = (body: unknown, contentType = ''): BodyInit | null => {\n if (body == null) {\n return null;\n }\n\n if (\n typeof body === 'string' ||\n body instanceof URLSearchParams ||\n body instanceof Blob ||\n body instanceof FormData ||\n body instanceof ReadableStream ||\n body instanceof ArrayBuffer ||\n ArrayBuffer.isView(body)\n ) {\n return body as BodyInit;\n }\n\n const mediaType = contentType.split(';')[0]?.trim().toLowerCase();\n\n if (mediaType === 'application/x-www-form-urlencoded') {\n return serializeForm(body as Record<string, unknown>);\n }\n\n if (!mediaType || mediaType === 'application/json' || mediaType.endsWith('+json')) {\n return JSON.stringify(body);\n }\n\n throw new TypeError(\n `Cannot serialize a parsed ${mediaType} body. Provide the adapter getBody option.`,\n );\n};\n\nexport default serializeBody;\n"],"names":["serializeBody","body","contentType","URLSearchParams","Blob","FormData","ReadableStream","ArrayBuffer","isView","mediaType","split","trim","toLowerCase","form","Object","entries","forEach","name","value","Array","isArray","item","includes","TypeError","append","String","serializeForm","endsWith","JSON","stringify"],"mappings":"AAGA,MAuBMA,EAAgB,CAACC,EAAeC,EAAc,MAClD,GAAY,MAARD,EACF,OAAO,KAGT,GACkB,iBAATA,GACPA,aAAgBE,iBAChBF,aAAgBG,MAChBH,aAAgBI,UAChBJ,aAAgBK,gBAChBL,aAAgBM,aAChBA,YAAYC,OAAOP,GAEnB,OAAOA,EAGT,MAAMQ,EAAYP,EAAYQ,MAAM,KAAK,IAAIC,OAAOC,cAEpD,GAAkB,sCAAdH,EACF,MA3CkB,CAACR,IACrB,MAAMY,EAAO,IAAIV,gBAgBjB,OAdAW,OAAOC,QAAQd,GAAMe,SAAQ,EAAEC,EAAMC,OACpBC,MAAMC,QAAQF,GAASA,EAAQ,CAACA,IAExCF,SAASK,IACd,GAAY,MAARA,IAAiB,CAAC,SAAU,SAAU,UAAW,UAAUC,gBAAgBD,GAC7E,MAAM,IAAIE,UACR,oGAIJV,EAAKW,OAAOP,EAAc,MAARI,EAAe,GAAKI,OAAOJ,GAAM,GACnD,IAGGR,CAAI,EA0BFa,CAAczB,GAGvB,IAAKQ,GAA2B,qBAAdA,GAAoCA,EAAUkB,SAAS,SACvE,OAAOC,KAAKC,UAAU5B,GAGxB,MAAM,IAAIsB,UACR,6BAA6Bd,8CAC9B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type TCompressionFormat = 'deflate' | 'gzip';
|
|
2
|
+
interface ICompressionOptions {
|
|
3
|
+
format?: TCompressionFormat;
|
|
4
|
+
}
|
|
5
|
+
type TCompression = boolean | ICompressionOptions;
|
|
6
|
+
type TCreateCompressionStream = (format: TCompressionFormat) => ReadableWritablePair<Uint8Array, Uint8Array>;
|
|
7
|
+
/**
|
|
8
|
+
* Compress eligible responses using the transport-provided stream implementation.
|
|
9
|
+
*/
|
|
10
|
+
declare const compressResponse: (request: Request, response: Response, compression?: TCompression, createStream?: TCreateCompressionStream) => Response;
|
|
11
|
+
export { compressResponse as default, TCompressionFormat, ICompressionOptions, TCompression };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const t=(t,e)=>{const s=new Map;return t.split(",").forEach((t=>{const[e="",...a]=t.trim().toLowerCase().split(";"),n=a.find((t=>t.trim().startsWith("q="))),o=n?Number(n.split("=")[1]):1;s.set(e,Number.isFinite(o)?o:0)})),s.get(e)??s.get("*")??0},e=(e,s,a=!1,n=(t=>new CompressionStream(t)))=>{if(!a||!s.body||s.bodyUsed||"HEAD"===e.method||s.status<200||[204,205,206,304].includes(s.status)||s.headers.has("Content-Encoding")||s.headers.has("Content-Range")||s.headers.get("Cache-Control")?.toLowerCase().includes("no-transform")||"function"!=typeof CompressionStream)return s;const o=e.headers.get("Accept-Encoding")??"",r="object"==typeof a?a.format:void 0,i=new Headers(s.headers),d=(r?[r]:["gzip","deflate"]).map((e=>({candidate:e,quality:t(o,e)}))).filter((({quality:t})=>t>0)).sort(((t,e)=>e.quality-t.quality))[0]?.candidate;return((t,e)=>{const s=t.get("Vary")?.split(",").map((t=>t.trim().toLowerCase()))??[];s.includes("*")||s.includes(e.toLowerCase())||t.append("Vary",e)})(i,"Accept-Encoding"),d?(i.delete("Content-Length"),i.set("Content-Encoding",d),new Response(s.body.pipeThrough(n(d)),{headers:i,status:s.status,statusText:s.statusText})):new Response(s.body,{headers:i,status:s.status,statusText:s.statusText})};export{e as default};
|
|
2
|
+
//# sourceMappingURL=compression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compression.js","sources":["../../src/adapters/compression.ts"],"sourcesContent":["export type TCompressionFormat = 'deflate' | 'gzip';\n\nexport interface ICompressionOptions {\n format?: TCompressionFormat;\n}\n\nexport type TCompression = boolean | ICompressionOptions;\n\ntype TCreateCompressionStream = (\n format: TCompressionFormat,\n) => ReadableWritablePair<Uint8Array, Uint8Array>;\n\n/**\n * Read the negotiated encoding quality, including wildcard fallbacks.\n */\nconst getQuality = (header: string, format: TCompressionFormat): number => {\n const values = new Map<string, number>();\n\n header.split(',').forEach((part) => {\n const [encoding = '', ...params] = part.trim().toLowerCase().split(';');\n const quality = params.find((param) => param.trim().startsWith('q='));\n const parsed = quality ? Number(quality.split('=')[1]) : 1;\n\n values.set(encoding, Number.isFinite(parsed) ? parsed : 0);\n });\n\n return values.get(format) ?? values.get('*') ?? 0;\n};\n\n/**\n * Include the negotiation header without duplicating existing Vary values.\n */\nconst appendVary = (headers: Headers, value: string): void => {\n const vary =\n headers\n .get('Vary')\n ?.split(',')\n .map((item) => item.trim().toLowerCase()) ?? [];\n\n if (!vary.includes('*') && !vary.includes(value.toLowerCase())) {\n headers.append('Vary', value);\n }\n};\n\n/**\n * Compress eligible responses using the transport-provided stream implementation.\n */\nconst compressResponse = (\n request: Request,\n response: Response,\n compression: TCompression = false,\n createStream: TCreateCompressionStream = (format) => new CompressionStream(format),\n): Response => {\n if (\n !compression ||\n !response.body ||\n response.bodyUsed ||\n request.method === 'HEAD' ||\n response.status < 200 ||\n [204, 205, 206, 304].includes(response.status) ||\n response.headers.has('Content-Encoding') ||\n response.headers.has('Content-Range') ||\n response.headers.get('Cache-Control')?.toLowerCase().includes('no-transform') ||\n typeof CompressionStream !== 'function'\n ) {\n return response;\n }\n\n const acceptEncoding = request.headers.get('Accept-Encoding') ?? '';\n const configured = typeof compression === 'object' ? compression.format : undefined;\n const headers = new Headers(response.headers);\n const format = (configured ? [configured] : (['gzip', 'deflate'] as const))\n .map((candidate) => ({ candidate, quality: getQuality(acceptEncoding, candidate) }))\n .filter(({ quality }) => quality > 0)\n .sort((left, right) => right.quality - left.quality)[0]?.candidate;\n\n appendVary(headers, 'Accept-Encoding');\n\n if (!format) {\n return new Response(response.body, {\n headers,\n status: response.status,\n statusText: response.statusText,\n });\n }\n\n headers.delete('Content-Length');\n headers.set('Content-Encoding', format);\n\n return new Response(response.body.pipeThrough(createStream(format)), {\n headers,\n status: response.status,\n statusText: response.statusText,\n });\n};\n\nexport default compressResponse;\n"],"names":["getQuality","header","format","values","Map","split","forEach","part","encoding","params","trim","toLowerCase","quality","find","param","startsWith","parsed","Number","set","isFinite","get","compressResponse","request","response","compression","createStream","CompressionStream","body","bodyUsed","method","status","includes","headers","has","acceptEncoding","configured","undefined","Headers","map","candidate","filter","sort","left","right","value","vary","item","append","appendVary","delete","Response","pipeThrough","statusText"],"mappings":"AAeA,MAAMA,EAAa,CAACC,EAAgBC,KAClC,MAAMC,EAAS,IAAIC,IAUnB,OARAH,EAAOI,MAAM,KAAKC,SAASC,IACzB,MAAOC,EAAW,MAAOC,GAAUF,EAAKG,OAAOC,cAAcN,MAAM,KAC7DO,EAAUH,EAAOI,MAAMC,GAAUA,EAAMJ,OAAOK,WAAW,QACzDC,EAASJ,EAAUK,OAAOL,EAAQP,MAAM,KAAK,IAAM,EAEzDF,EAAOe,IAAIV,EAAUS,OAAOE,SAASH,GAAUA,EAAS,EAAE,IAGrDb,EAAOiB,IAAIlB,IAAWC,EAAOiB,IAAI,MAAQ,CAAC,EAqB7CC,EAAmB,CACvBC,EACAC,EACAC,GAA4B,EAC5BC,EAAyC,CAACvB,GAAW,IAAIwB,kBAAkBxB,OAE3E,IACGsB,IACAD,EAASI,MACVJ,EAASK,UACU,SAAnBN,EAAQO,QACRN,EAASO,OAAS,KAClB,CAAC,IAAK,IAAK,IAAK,KAAKC,SAASR,EAASO,SACvCP,EAASS,QAAQC,IAAI,qBACrBV,EAASS,QAAQC,IAAI,kBACrBV,EAASS,QAAQZ,IAAI,kBAAkBT,cAAcoB,SAAS,iBACjC,mBAAtBL,kBAEP,OAAOH,EAGT,MAAMW,EAAiBZ,EAAQU,QAAQZ,IAAI,oBAAsB,GAC3De,EAAoC,iBAAhBX,EAA2BA,EAAYtB,YAASkC,EACpEJ,EAAU,IAAIK,QAAQd,EAASS,SAC/B9B,GAAUiC,EAAa,CAACA,GAAe,CAAC,OAAQ,YACnDG,KAAKC,IAAS,CAAQA,YAAW3B,QAASZ,EAAWkC,EAAgBK,OACrEC,QAAO,EAAG5B,aAAcA,EAAU,IAClC6B,MAAK,CAACC,EAAMC,IAAUA,EAAM/B,QAAU8B,EAAK9B,UAAS,IAAI2B,UAI3D,MA9CiB,EAACP,EAAkBY,KACpC,MAAMC,EACJb,EACGZ,IAAI,SACHf,MAAM,KACPiC,KAAKQ,GAASA,EAAKpC,OAAOC,iBAAkB,GAE5CkC,EAAKd,SAAS,MAASc,EAAKd,SAASa,EAAMjC,gBAC9CqB,EAAQe,OAAO,OAAQH,EACzB,EAmCAI,CAAWhB,EAAS,mBAEf9B,GAQL8B,EAAQiB,OAAO,kBACfjB,EAAQd,IAAI,mBAAoBhB,GAEzB,IAAIgD,SAAS3B,EAASI,KAAKwB,YAAY1B,EAAavB,IAAU,CACnE8B,UACAF,OAAQP,EAASO,OACjBsB,WAAY7B,EAAS6B,cAbd,IAAIF,SAAS3B,EAASI,KAAM,CACjCK,UACAF,OAAQP,EAASO,OACjBsB,WAAY7B,EAAS6B,YAWvB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TCompression } from "./compression.js";
|
|
2
|
+
import { TSsrHandler } from "../core/types.js";
|
|
3
|
+
interface IEdgeAdapterOptions {
|
|
4
|
+
compression?: TCompression;
|
|
5
|
+
}
|
|
6
|
+
type TEdgeHandler<TPlatformArgs extends unknown[] = unknown[]> = (request: Request, ...platformArgs: TPlatformArgs) => Promise<Response>;
|
|
7
|
+
/**
|
|
8
|
+
* Adapt the Fetch handler to runtime fetch entrypoints and optional compression.
|
|
9
|
+
*/
|
|
10
|
+
declare const adapterEdge: <TPlatformArgs extends unknown[] = unknown[]>(handler: TSsrHandler, { compression }?: IEdgeAdapterOptions) => TEdgeHandler<TPlatformArgs>;
|
|
11
|
+
export { adapterEdge as default, IEdgeAdapterOptions, TEdgeHandler };
|
package/adapters/edge.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge.js","sources":["../../src/adapters/edge.ts"],"sourcesContent":["import compressResponse from '@adapters/compression';\nimport type { TCompression } from '@adapters/compression';\nimport headResponse from '@core/head-response';\nimport type { TSsrHandler } from '@core/types';\n\nexport interface IEdgeAdapterOptions {\n compression?: TCompression;\n}\n\nexport type TEdgeHandler<TPlatformArgs extends unknown[] = unknown[]> = (\n request: Request,\n ...platformArgs: TPlatformArgs\n) => Promise<Response>;\n\n/**\n * Adapt the Fetch handler to runtime fetch entrypoints and optional compression.\n */\nconst adapterEdge = <TPlatformArgs extends unknown[] = unknown[]>(\n handler: TSsrHandler,\n { compression = false }: IEdgeAdapterOptions = {},\n): TEdgeHandler<TPlatformArgs> => {\n /**\n * Apply compression and HEAD handling to the completed Fetch response.\n */\n return async (...[request]: Parameters<TEdgeHandler<TPlatformArgs>>) => {\n return headResponse(request, compressResponse(request, await handler(request), compression));\n };\n};\n\nexport default adapterEdge;\n"],"names":["adapterEdge","handler","compression","async","request","headResponse","compressResponse"],"mappings":"wEAiBA,MAAMA,EAAc,CAClBC,GACEC,eAAc,GAA+B,CAAA,IAKxCC,UAAWC,KACTC,EAAaD,EAASE,EAAiBF,QAAeH,EAAQG,GAAUF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Request as ExpressRequest } from 'express';
|
|
2
|
+
interface ICreateFetchRequestOptions {
|
|
3
|
+
body?: BodyInit | null;
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Preserve Express's trusted protocol and original URL when creating the router request.
|
|
8
|
+
*/
|
|
9
|
+
declare function createFetchRequest(req: ExpressRequest, options?: ICreateFetchRequestOptions): Request;
|
|
10
|
+
export type { ICreateFetchRequestOptions };
|
|
11
|
+
export { createFetchRequest as default };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import o from"../body.js";import t from"../../node/create-request.js";function e(e,r={}){const d={...r,origin:`${e.protocol}://${e.get("host")}`,url:e.originalUrl||e.url};return Object.hasOwn(r,"body")||void 0===e.body||"GET"===e.method||"HEAD"===e.method||(d.body=o(e.body,e.get("Content-Type"))),t(e,d)}export{e as default};
|
|
2
|
+
//# sourceMappingURL=create-request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-request.js","sources":["../../../src/adapters/express/create-request.ts"],"sourcesContent":["import type { Request as ExpressRequest } from 'express';\nimport serializeBody from '@adapters/body';\nimport createRequest from '@node/create-request';\nimport type { ICreateRequestOptions } from '@node/create-request';\n\ninterface ICreateFetchRequestOptions {\n body?: BodyInit | null;\n signal?: AbortSignal;\n}\n\n/**\n * Preserve Express's trusted protocol and original URL when creating the router request.\n */\nfunction createFetchRequest(\n req: ExpressRequest,\n options: ICreateFetchRequestOptions = {},\n): Request {\n const requestOptions: ICreateRequestOptions = {\n ...options,\n origin: `${req.protocol}://${req.get('host') as string}`,\n url: req.originalUrl || req.url,\n };\n\n if (\n !Object.hasOwn(options, 'body') &&\n req.body !== undefined &&\n req.method !== 'GET' &&\n req.method !== 'HEAD'\n ) {\n requestOptions.body = serializeBody(req.body, req.get('Content-Type'));\n }\n\n return createRequest(req, requestOptions);\n}\n\nexport type { ICreateFetchRequestOptions };\n\nexport default createFetchRequest;\n"],"names":["createFetchRequest","req","options","requestOptions","origin","protocol","get","url","originalUrl","Object","hasOwn","undefined","body","method","serializeBody","createRequest"],"mappings":"sEAaA,SAASA,EACPC,EACAC,EAAsC,IAEtC,MAAMC,EAAwC,IACzCD,EACHE,OAAQ,GAAGH,EAAII,cAAcJ,EAAIK,IAAI,UACrCC,IAAKN,EAAIO,aAAeP,EAAIM,KAY9B,OARGE,OAAOC,OAAOR,EAAS,cACXS,IAAbV,EAAIW,MACW,QAAfX,EAAIY,QACW,SAAfZ,EAAIY,SAEJV,EAAeS,KAAOE,EAAcb,EAAIW,KAAMX,EAAIK,IAAI,kBAGjDS,EAAcd,EAAKE,EAC5B"}
|
|
@@ -7,26 +7,20 @@ import { FC, PropsWithChildren } from 'react';
|
|
|
7
7
|
import { createStaticHandler } from 'react-router';
|
|
8
8
|
import { ServeStaticOptions } from 'serve-static';
|
|
9
9
|
import { Logger } from 'vite';
|
|
10
|
-
import { TRouteObject } from "../interfaces/route-object.js";
|
|
11
10
|
import { IRenderOptions, TRender } from "./render.js";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
11
|
+
import { TRouteObject } from "../../interfaces/route-object.js";
|
|
12
|
+
import ServerApi from "../../services/server-api.js";
|
|
13
|
+
import ServerConfig from "../../services/server-config.js";
|
|
14
14
|
interface IInitServerRequestOut<T = Record<string, any>> {
|
|
15
15
|
appProps?: T;
|
|
16
16
|
hasEarlyHints?: boolean;
|
|
17
17
|
shouldSkip?: boolean;
|
|
18
18
|
shouldCancel?: boolean;
|
|
19
19
|
}
|
|
20
|
-
interface IEntrypointOptions<TAppProps = Record<string, any>> {
|
|
20
|
+
interface IEntrypointOptions<TAppProps = Record<string, any>> extends Pick<IRenderOptions<TAppProps>, 'onRouterReady' | 'onShellReady' | 'onShellError' | 'onResponse' | 'onError' | 'getState' | 'getBody'> {
|
|
21
21
|
onServerCreated?: (app: Express, serverApi: ServerApi) => Promise<void> | void;
|
|
22
22
|
onServerStarted?: (app: Express, serverApi: ServerApi, server: Server) => Promise<void> | void;
|
|
23
23
|
onRequest?: (req: Request, res: ExpressResponse) => Promise<IInitServerRequestOut<TAppProps>> | IInitServerRequestOut<TAppProps>;
|
|
24
|
-
onRouterReady?: IRenderOptions<TAppProps>['onRouterReady'];
|
|
25
|
-
onShellReady?: IRenderOptions<TAppProps>['onShellReady'];
|
|
26
|
-
onShellError?: IRenderOptions<TAppProps>['onShellError'];
|
|
27
|
-
onResponse?: IRenderOptions<TAppProps>['onResponse'];
|
|
28
|
-
onError?: IRenderOptions<TAppProps>['onError'];
|
|
29
|
-
getState?: IRenderOptions<TAppProps>['getState'];
|
|
30
24
|
}
|
|
31
25
|
interface IPrepareRenderOut<TAppProps = Record<string, any>> {
|
|
32
26
|
render: TRender;
|
|
@@ -37,6 +31,9 @@ interface IPrepareRenderOut<TAppProps = Record<string, any>> {
|
|
|
37
31
|
loggerDev?: Logger;
|
|
38
32
|
middlewares?: {
|
|
39
33
|
compression?: CompressionOptions | false;
|
|
34
|
+
/**
|
|
35
|
+
* basename should be same as vite 'base' config
|
|
36
|
+
*/
|
|
40
37
|
expressStatic?: (ServeStaticOptions & {
|
|
41
38
|
basename?: string;
|
|
42
39
|
}) | false;
|
|
@@ -46,14 +43,11 @@ interface IAppServerProps<T = Record<string, any>> {
|
|
|
46
43
|
server: T;
|
|
47
44
|
}
|
|
48
45
|
type TApp<T> = FC<PropsWithChildren<Record<string, any> & IAppServerProps<T>>>;
|
|
49
|
-
interface IEntryServerOptions<TAppProps = Record<string, any>> {
|
|
46
|
+
interface IEntryServerOptions<TAppProps = Record<string, any>> extends Pick<IPrepareRenderOut, 'loggerProd' | 'loggerDev' | 'middlewares'> {
|
|
50
47
|
abortDelay?: number;
|
|
51
48
|
init?: (params: {
|
|
52
49
|
config: ServerConfig;
|
|
53
50
|
}) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;
|
|
54
|
-
loggerProd?: IPrepareRenderOut['loggerProd'];
|
|
55
|
-
loggerDev?: IPrepareRenderOut['loggerDev'];
|
|
56
|
-
middlewares?: IPrepareRenderOut['middlewares'];
|
|
57
51
|
routerOptions?: Parameters<typeof createStaticHandler>[1];
|
|
58
52
|
}
|
|
59
53
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","sources":["../../../src/adapters/express/entry.tsx"],"sourcesContent":["import type { Server } from 'node:net';\nimport type { CompressionOptions } from 'compression';\nimport type { Express, Request, Response as ExpressResponse } from 'express';\nimport type { FC, PropsWithChildren } from 'react';\nimport type { RouteObject } from 'react-router';\nimport { createStaticHandler } from 'react-router';\nimport type { ServeStaticOptions } from 'serve-static';\nimport type { Logger } from 'vite';\nimport type { IRenderOptions, IRenderParams, TRender } from '@adapters/express/render';\nimport render from '@adapters/express/render';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport type ServerApi from '@services/server-api';\nimport type ServerConfig from '@services/server-config';\n\nexport interface IInitServerRequestOut<T = Record<string, any>> {\n appProps?: T;\n hasEarlyHints?: boolean;\n shouldSkip?: boolean;\n shouldCancel?: boolean;\n}\n\nexport interface IEntrypointOptions<TAppProps = Record<string, any>> extends Pick<\n IRenderOptions<TAppProps>,\n | 'onRouterReady'\n | 'onShellReady'\n | 'onShellError'\n | 'onResponse'\n | 'onError'\n | 'getState'\n | 'getBody'\n> {\n onServerCreated?: (app: Express, serverApi: ServerApi) => Promise<void> | void;\n onServerStarted?: (app: Express, serverApi: ServerApi, server: Server) => Promise<void> | void;\n onRequest?: (\n req: Request,\n res: ExpressResponse,\n ) => Promise<IInitServerRequestOut<TAppProps>> | IInitServerRequestOut<TAppProps>;\n}\n\nexport interface IPrepareRenderOut<TAppProps = Record<string, any>> {\n render: TRender;\n init: IEntryServerOptions<TAppProps>['init'];\n routes: TRouteObject[];\n abortDelay?: number;\n loggerProd?: Logger;\n loggerDev?: Logger;\n middlewares?: {\n compression?: CompressionOptions | false;\n\n /**\n * basename should be same as vite 'base' config\n */\n expressStatic?: (ServeStaticOptions & { basename?: string }) | false;\n };\n}\n\nexport interface IAppServerProps<T = Record<string, any>> {\n server: T;\n}\n\nexport type TApp<T> = FC<PropsWithChildren<Record<string, any> & IAppServerProps<T>>>;\n\nexport interface IEntryServerOptions<TAppProps = Record<string, any>> extends Pick<\n IPrepareRenderOut,\n 'loggerProd' | 'loggerDev' | 'middlewares'\n> {\n abortDelay?: number;\n init?: (params: {\n config: ServerConfig;\n }) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;\n routerOptions?: Parameters<typeof createStaticHandler>[1];\n}\n\n/**\n * Render server side application\n */\nfunction entry<TAppProps>(\n App: TApp<TAppProps>,\n routes: TRouteObject[],\n { init, routerOptions, ...rest }: IEntryServerOptions<TAppProps> = {},\n): IPrepareRenderOut<TAppProps> {\n const handler = createStaticHandler(routes as RouteObject[], routerOptions);\n\n return {\n render: render.bind(null, { handler, App } as IRenderParams<TAppProps>) as TRender,\n init,\n routes,\n ...rest,\n };\n}\n\nexport default entry;\n"],"names":["entry","App","routes","init","routerOptions","rest","handler","createStaticHandler","render","bind"],"mappings":"8EA4EA,SAASA,EACPC,EACAC,GACAC,KAAEA,EAAIC,cAAEA,KAAkBC,GAAyC,IAEnE,MAAMC,EAAUC,EAAoBL,EAAyBE,GAE7D,MAAO,CACLI,OAAQA,EAAOC,KAAK,KAAM,CAAEH,UAASL,QACrCE,OACAD,YACGG,EAEP"}
|
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
import { Request } from 'express';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import ServerApi from "
|
|
6
|
-
import ServerConfig from "
|
|
7
|
-
interface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> {
|
|
2
|
+
import { IEntrypointOptions, IPrepareRenderOut } from "./entry.js";
|
|
3
|
+
import { TRender } from "./render.js";
|
|
4
|
+
import { TRouteObject } from "../../interfaces/route-object.js";
|
|
5
|
+
import ServerApi from "../../services/server-api.js";
|
|
6
|
+
import ServerConfig from "../../services/server-config.js";
|
|
7
|
+
interface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> extends Omit<IEntrypointOptions<TAppProps>, 'onServerCreated' | 'onServerStarted'> {
|
|
8
8
|
render: TRender;
|
|
9
9
|
routes: TRouteObject[];
|
|
10
10
|
abortDelay?: number;
|
|
11
|
-
onRequest?: IEntrypointOptions<TAppProps>['onRequest'];
|
|
12
|
-
onRouterReady?: IEntrypointOptions<TAppProps>['onRouterReady'];
|
|
13
|
-
onShellReady?: IEntrypointOptions<TAppProps>['onShellReady'];
|
|
14
|
-
onShellError?: IEntrypointOptions<TAppProps>['onShellError'];
|
|
15
|
-
onResponse?: IEntrypointOptions<TAppProps>['onResponse'];
|
|
16
|
-
onError?: IEntrypointOptions<TAppProps>['onError'];
|
|
17
|
-
getState?: IEntrypointOptions<TAppProps>['getState'];
|
|
18
11
|
}
|
|
19
12
|
/**
|
|
20
13
|
* Load server entrypoint and template
|
|
21
14
|
* DEV MODE: refresh entrypoint and template
|
|
22
15
|
*/
|
|
23
16
|
declare class PrepareServer {
|
|
17
|
+
/**
|
|
18
|
+
* Hook which calls after express server started
|
|
19
|
+
*/
|
|
20
|
+
onServerStarted?: NonNullable<IEntrypointOptions['onServerStarted']>;
|
|
24
21
|
/**
|
|
25
22
|
* Server configuration
|
|
26
23
|
*/
|
|
@@ -36,11 +33,7 @@ declare class PrepareServer {
|
|
|
36
33
|
/**
|
|
37
34
|
* Hook which calls after express server created
|
|
38
35
|
*/
|
|
39
|
-
protected onServerCreated?: IEntrypointOptions['onServerCreated']
|
|
40
|
-
/**
|
|
41
|
-
* Hook which calls after express server started
|
|
42
|
-
*/
|
|
43
|
-
onServerStarted?: IEntrypointOptions['onServerStarted'];
|
|
36
|
+
protected onServerCreated?: NonNullable<IEntrypointOptions['onServerCreated']>;
|
|
44
37
|
/**
|
|
45
38
|
* Html shell
|
|
46
39
|
*/
|
|
@@ -48,12 +41,12 @@ declare class PrepareServer {
|
|
|
48
41
|
/**
|
|
49
42
|
* Middlewares configs
|
|
50
43
|
*/
|
|
51
|
-
protected middlewaresConfigs?: IPrepareRenderOut['middlewares']
|
|
44
|
+
protected middlewaresConfigs?: NonNullable<IPrepareRenderOut['middlewares']>;
|
|
52
45
|
/**
|
|
53
|
-
*
|
|
46
|
+
* Retain runtime configuration and reuse the existing server API.
|
|
54
47
|
*/
|
|
55
48
|
/**
|
|
56
|
-
*
|
|
49
|
+
* Retain runtime configuration and reuse the existing server API.
|
|
57
50
|
*/
|
|
58
51
|
protected constructor(config: ServerConfig, serverApi?: ServerApi);
|
|
59
52
|
/**
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"node:fs";import t from"node:path";import r from"node:process";import{pathToFileURL as i}from"node:url";import o from"chalk";import s from"../../services/server-api.js";class n{onServerStarted;config;serverApi;entrypoint;onServerCreated;html;middlewaresConfigs;constructor(e,t){this.config=e,this.serverApi=t??new s}static init(e,t){return new n(e,t)}async loadEntrypoint(e=!0){if(this.entrypoint&&this.config.isProd)return this.entrypoint;const{root:s,isProd:n,serverFile:a}=this.config.getParams(),l=t.resolve(`${s}/${a}`);let d;try{d=n?(await import(i(l).toString())).default:(await this.config.getVite().ssrLoadModule(l,{fixStacktrace:!0})).default}catch(e){if(e instanceof Error&&e.message.includes("Cannot find module")&&e.message.includes("/build/"))return this.config.getLogger().error(o.red(`Before starting the server, you need to create a build: ${o.yellow("ssr-boost build")} or provide path to build dir: ${o.yellow("ssr-boost start --build-dir build")}`)),r.exit(1);throw e}!e&&d.init&&delete d.init;const{render:c,init:g,routes:h,abortDelay:f,loggerProd:p,loggerDev:m,middlewares:u}=d;this.middlewaresConfigs=u,p&&n?this.config.setLogger(p):m&&!n&&this.config.setLogger(m);const{onServerCreated:y,onServerStarted:v,...w}=await(g?.({config:this.config}))??{};return this.entrypoint={render:c,routes:h,abortDelay:f,...w},this.onServerCreated=y,this.onServerStarted=v,this.entrypoint}async loadHtml(r){const{isProd:i,root:o,indexFile:s,clientFile:n}=this.config.getParams();this.html&&i||(this.html=e.readFileSync(t.resolve(`${o}/${s}`),"utf-8"));let a=this.html;if(!i){const e=t.posix.normalize(`${this.config.getVite()?.config.base}/${n}`);a=(await this.config.getVite().transformIndexHtml(r.originalUrl,this.html,s)).replace(new RegExp(String.raw`<script[^>]*?\bsrc=["']/?${e}([^"']*)["'][^>]*?\sasync\b`,"g"),(e=>e.replace(/\sasync\b/,"")))}return a.split("\x3c!--ssr-outlet--\x3e")}async onAppCreated(){return await this.loadEntrypoint(),await(this.onServerCreated?.(this.config.getApp(),this.serverApi)),this}getMiddlewaresConfig(){const{compression:e,expressStatic:t}=this.middlewaresConfigs??{};return{compression:!1!==e&&{...e},expressStatic:!1!==t&&{...t,basename:t?.basename??"/"}}}}export{n as default};
|
|
2
|
+
//# sourceMappingURL=prepare-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare-server.js","sources":["../../../src/adapters/express/prepare-server.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { pathToFileURL } from 'node:url';\nimport chalk from 'chalk';\nimport type { Express, Request } from 'express';\nimport type { IEntrypointOptions, IPrepareRenderOut } from '@adapters/express/entry';\nimport type { TRender } from '@adapters/express/render';\nimport type { TRouteObject } from '@interfaces/route-object';\nimport ServerApi from '@services/server-api';\nimport type ServerConfig from '@services/server-config';\n\ninterface IPrepareServerEntrypointLoadOut<TAppProps = Record<string, any>> extends Omit<\n IEntrypointOptions<TAppProps>,\n 'onServerCreated' | 'onServerStarted'\n> {\n render: TRender;\n routes: TRouteObject[];\n abortDelay?: number;\n}\n\n/**\n * Load server entrypoint and template\n * DEV MODE: refresh entrypoint and template\n */\nclass PrepareServer {\n /**\n * Hook which calls after express server started\n */\n public onServerStarted?: NonNullable<IEntrypointOptions['onServerStarted']>;\n\n /**\n * Server configuration\n */\n protected readonly config: ServerConfig;\n\n /**\n * Server API\n */\n protected readonly serverApi: ServerApi;\n\n /**\n * Entrypoint resolved params\n */\n protected entrypoint?: IPrepareServerEntrypointLoadOut;\n\n /**\n * Hook which calls after express server created\n */\n protected onServerCreated?: NonNullable<IEntrypointOptions['onServerCreated']>;\n\n /**\n * Html shell\n */\n protected html: string;\n\n /**\n * Middlewares configs\n */\n protected middlewaresConfigs?: NonNullable<IPrepareRenderOut['middlewares']>;\n\n /**\n * Retain runtime configuration and reuse the existing server API.\n */\n protected constructor(config: ServerConfig, serverApi?: ServerApi) {\n this.config = config;\n this.serverApi = serverApi ?? new ServerApi();\n }\n\n /**\n * Init service\n */\n public static init(config: ServerConfig, serverApi?: ServerApi): PrepareServer {\n return new PrepareServer(config, serverApi);\n }\n\n /**\n * Resolve and return entrypoint params\n */\n public async loadEntrypoint(shouldInit = true): Promise<IPrepareServerEntrypointLoadOut> {\n /**\n * load server entrypoint each time only in development mode (for fast refresh)\n */\n if (this.entrypoint && this.config.isProd) {\n return this.entrypoint;\n }\n\n const { root, isProd, serverFile } = this.config.getParams();\n const entrypointPath = path.resolve(`${root}/${serverFile}`);\n\n let resolvedEntrypoint: IPrepareRenderOut;\n\n try {\n if (!isProd) {\n resolvedEntrypoint = (\n await this.config.getVite()!.ssrLoadModule(entrypointPath, {\n fixStacktrace: true,\n })\n ).default as IPrepareRenderOut;\n } else {\n resolvedEntrypoint = (\n (await import(pathToFileURL(entrypointPath).toString())) as { default: IPrepareRenderOut }\n ).default;\n }\n } catch (e) {\n if (\n e instanceof Error &&\n e.message.includes('Cannot find module') &&\n e.message.includes('/build/')\n ) {\n this.config\n .getLogger()\n .error(\n chalk.red(\n `Before starting the server, you need to create a build: ${chalk.yellow(\n 'ssr-boost build',\n )} or provide path to build dir: ${chalk.yellow(\n 'ssr-boost start --build-dir build',\n )}`,\n ),\n );\n\n return process.exit(1);\n }\n\n throw e;\n }\n\n if (!shouldInit && resolvedEntrypoint.init) {\n delete resolvedEntrypoint.init;\n }\n\n const { render, init, routes, abortDelay, loggerProd, loggerDev, middlewares } =\n resolvedEntrypoint;\n\n this.middlewaresConfigs = middlewares;\n\n if (loggerProd && isProd) {\n this.config.setLogger(loggerProd);\n } else if (loggerDev && !isProd) {\n this.config.setLogger(loggerDev);\n }\n\n const { onServerCreated, onServerStarted, ...renderParams } =\n (await init?.({\n config: this.config,\n })) ?? {};\n\n this.entrypoint = {\n render,\n routes,\n abortDelay,\n ...renderParams,\n };\n this.onServerCreated = onServerCreated;\n this.onServerStarted = onServerStarted;\n\n return this.entrypoint;\n }\n\n /**\n * Load and return html shell\n */\n public async loadHtml(req: Request): Promise<[string, string]> {\n const { isProd, root, indexFile, clientFile } = this.config.getParams();\n\n if (!this.html || !isProd) {\n this.html = fs.readFileSync(path.resolve(`${root}/${indexFile}`), 'utf-8');\n }\n\n let modifiedHtml = this.html;\n\n if (!isProd) {\n const clientFileEntry = path.posix.normalize(\n `${this.config.getVite()?.config.base}/${clientFile}`,\n );\n\n /**\n * Apply Vite transforms and remove async from the entry to preserve plugin preambles.\n */\n modifiedHtml = (\n await this.config.getVite()!.transformIndexHtml(req.originalUrl, this.html, indexFile)\n ).replace(\n new RegExp(\n String.raw`<script[^>]*?\\bsrc=[\"']/?${clientFileEntry}([^\"']*)[\"'][^>]*?\\sasync\\b`,\n 'g',\n ),\n (match) => match.replace(/\\sasync\\b/, ''),\n );\n }\n\n return modifiedHtml.split('<!--ssr-outlet-->') as [string, string];\n }\n\n /**\n * Run server created hook\n */\n public async onAppCreated(): Promise<PrepareServer> {\n await this.loadEntrypoint();\n await this.onServerCreated?.(this.config.getApp<Express>()!, this.serverApi);\n\n return this;\n }\n\n /**\n * Return SSR express middlewares configs\n */\n public getMiddlewaresConfig(): NonNullable<PrepareServer['middlewaresConfigs']> {\n const { compression, expressStatic } = this.middlewaresConfigs ?? {};\n\n return {\n compression:\n compression !== false\n ? {\n ...compression,\n }\n : false,\n expressStatic:\n expressStatic !== false\n ? {\n ...expressStatic,\n basename: expressStatic?.basename ?? '/',\n }\n : false,\n };\n }\n}\n\nexport default PrepareServer;\n"],"names":["PrepareServer","onServerStarted","config","serverApi","entrypoint","onServerCreated","html","middlewaresConfigs","constructor","this","ServerApi","static","async","shouldInit","isProd","root","serverFile","getParams","entrypointPath","path","resolve","resolvedEntrypoint","import","pathToFileURL","toString","default","getVite","ssrLoadModule","fixStacktrace","e","Error","message","includes","getLogger","error","chalk","red","yellow","process","exit","init","render","routes","abortDelay","loggerProd","loggerDev","middlewares","setLogger","renderParams","req","indexFile","clientFile","fs","readFileSync","modifiedHtml","clientFileEntry","posix","normalize","base","transformIndexHtml","originalUrl","replace","RegExp","String","raw","match","split","loadEntrypoint","getApp","getMiddlewaresConfig","compression","expressStatic","basename"],"mappings":"sLAyBA,MAAMA,EAIGC,gBAKYC,OAKAC,UAKTC,WAKAC,gBAKAC,KAKAC,mBAKVC,YAAsBN,EAAsBC,GAC1CM,KAAKP,OAASA,EACdO,KAAKN,UAAYA,GAAa,IAAIO,CACpC,CAKOC,YAAYT,EAAsBC,GACvC,OAAO,IAAIH,EAAcE,EAAQC,EACnC,CAKOS,qBAAqBC,GAAa,GAIvC,GAAIJ,KAAKL,YAAcK,KAAKP,OAAOY,OACjC,OAAOL,KAAKL,WAGd,MAAMW,KAAEA,EAAID,OAAEA,EAAME,WAAEA,GAAeP,KAAKP,OAAOe,YAC3CC,EAAiBC,EAAKC,QAAQ,GAAGL,KAAQC,KAE/C,IAAIK,EAEJ,IAQIA,EAPGP,SAQMQ,OAAOC,EAAcL,GAAgBM,aAC5CC,eAPMhB,KAAKP,OAAOwB,UAAWC,cAAcT,EAAgB,CACzDU,eAAe,KAEjBH,OAMN,CAAE,MAAOI,GACP,GACEA,aAAaC,OACbD,EAAEE,QAAQC,SAAS,uBACnBH,EAAEE,QAAQC,SAAS,WAcnB,OAZAvB,KAAKP,OACF+B,YACAC,MACCC,EAAMC,IACJ,2DAA2DD,EAAME,OAC/D,oDACiCF,EAAME,OACvC,yCAKDC,EAAQC,KAAK,GAGtB,MAAMV,CACR,EAEKhB,GAAcQ,EAAmBmB,aAC7BnB,EAAmBmB,KAG5B,MAAMC,OAAEA,EAAMD,KAAEA,EAAIE,OAAEA,EAAMC,WAAEA,EAAUC,WAAEA,EAAUC,UAAEA,EAASC,YAAEA,GAC/DzB,EAEFZ,KAAKF,mBAAqBuC,EAEtBF,GAAc9B,EAChBL,KAAKP,OAAO6C,UAAUH,GACbC,IAAc/B,GACvBL,KAAKP,OAAO6C,UAAUF,GAGxB,MAAMxC,gBAAEA,EAAeJ,gBAAEA,KAAoB+C,SACpCR,IAAO,CACZtC,OAAQO,KAAKP,WACR,CAAA,EAWT,OATAO,KAAKL,WAAa,CAChBqC,SACAC,SACAC,gBACGK,GAELvC,KAAKJ,gBAAkBA,EACvBI,KAAKR,gBAAkBA,EAEhBQ,KAAKL,UACd,CAKOQ,eAAeqC,GACpB,MAAMnC,OAAEA,EAAMC,KAAEA,EAAImC,UAAEA,EAASC,WAAEA,GAAe1C,KAAKP,OAAOe,YAEvDR,KAAKH,MAASQ,IACjBL,KAAKH,KAAO8C,EAAGC,aAAalC,EAAKC,QAAQ,GAAGL,KAAQmC,KAAc,UAGpE,IAAII,EAAe7C,KAAKH,KAExB,IAAKQ,EAAQ,CACX,MAAMyC,EAAkBpC,EAAKqC,MAAMC,UACjC,GAAGhD,KAAKP,OAAOwB,WAAWxB,OAAOwD,QAAQP,KAM3CG,SACQ7C,KAAKP,OAAOwB,UAAWiC,mBAAmBV,EAAIW,YAAanD,KAAKH,KAAM4C,IAC5EW,QACA,IAAIC,OACFC,OAAOC,GAAG,4BAA4BT,+BACtC,MAEDU,GAAUA,EAAMJ,QAAQ,YAAa,KAE1C,CAEA,OAAOP,EAAaY,MAAM,0BAC5B,CAKOtD,qBAIL,aAHMH,KAAK0D,uBACL1D,KAAKJ,kBAAkBI,KAAKP,OAAOkE,SAAoB3D,KAAKN,YAE3DM,IACT,CAKO4D,uBACL,MAAMC,YAAEA,EAAWC,cAAEA,GAAkB9D,KAAKF,oBAAsB,CAAA,EAElE,MAAO,CACL+D,aACkB,IAAhBA,GACI,IACKA,GAGXC,eACoB,IAAlBA,GACI,IACKA,EACHC,SAAUD,GAAeC,UAAY,KAIjD"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Request } from 'express';
|
|
1
|
+
import { Request as ExpressRequest } from 'express';
|
|
2
2
|
import { Response as ExpressResponse } from "express";
|
|
3
3
|
import { StaticHandlerContext, StaticHandler } from 'react-router';
|
|
4
|
-
import StreamError from "../constants/stream-error.js";
|
|
5
|
-
import { IServerContext } from "../context/server.js";
|
|
6
|
-
import { IObtainStreamErrorOut } from "../helpers/obtain-stream-error.js";
|
|
7
4
|
import { TApp } from "./entry.js";
|
|
8
|
-
import
|
|
5
|
+
import StreamError from "../../constants/stream-error.js";
|
|
6
|
+
import { IServerContext } from "../../context/server.js";
|
|
7
|
+
import { IObtainStreamErrorOut } from "../../helpers/obtain-stream-error.js";
|
|
8
|
+
import ServerConfig from "../../services/server-config.js";
|
|
9
9
|
interface IRequestContext<TAppProps = Record<any, any>> {
|
|
10
|
-
req:
|
|
10
|
+
req: ExpressRequest;
|
|
11
11
|
res: ExpressResponse;
|
|
12
12
|
appProps: NonNullable<TAppProps>;
|
|
13
13
|
html: {
|
|
@@ -27,6 +27,7 @@ interface IRenderParams<TAppProps = Record<string, any>> {
|
|
|
27
27
|
}
|
|
28
28
|
interface IRenderOptions<TAppProps = Record<string, any>> {
|
|
29
29
|
abortDelay?: number;
|
|
30
|
+
getBody?: (request: ExpressRequest) => BodyInit | null | undefined;
|
|
30
31
|
onRouterReady?: (params: {
|
|
31
32
|
context: IRequestContext<TAppProps>;
|
|
32
33
|
}) => Promise<IRouterReadyOut> | IRouterReadyOut;
|
|
@@ -59,6 +60,5 @@ interface IShellReadyOut {
|
|
|
59
60
|
/**
|
|
60
61
|
* Render application
|
|
61
62
|
*/
|
|
62
|
-
declare function render({ App, handler }: IRenderParams,
|
|
63
|
-
config: ServerConfig, context: IRequestContext, { onRouterReady, onShellReady, onResponse, onShellError, onError, getState, abortDelay, }: IRenderOptions): Promise<void>;
|
|
63
|
+
declare function render({ App, handler }: IRenderParams, config: ServerConfig, context: IRequestContext, { onRouterReady, onShellReady, onResponse, onShellError, onError, getBody, getState, abortDelay, }: IRenderOptions): Promise<void>;
|
|
64
64
|
export { render as default, IRequestContext, TRender, IRenderParams, IRenderOptions, IRouterReadyOut, IShellReadyOut };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"chalk";import r from"react";import t from"./create-request.js";import o from"../../constants/stream-error.js";import s from"../../core/early-hints.js";import{getHeaderEntries as n,getSetCookieHeaders as a}from"../../core/headers.js";import i from"../../core/render.js";import d from"../../node/render-to-stream.js";import c from"../../node/request-signal.js";import m from"../../node/write-early-hints.js";import p,{writeFetchHeaders as l}from"../../node/write-fetch-response.js";import h from"../../services/ssr-manifest.js";const f=(e,r,o)=>{const s={signal:r};return o&&"GET"!==e.method&&"HEAD"!==e.method&&(s.body=o(e)??null),t(e,s)};async function u({App:t,handler:u},x,y,{onRouterReady:g,onShellReady:S,onResponse:E,onShellError:w,onError:C,getBody:j,getState:v,abortDelay:R=15e3}){const{appProps:b,html:A,req:H,res:q}=y,k=x.getLogger(),D=c(H,q);try{const c={appProps:b,html:A,request:f(H,D.signal,j),response:{headers:new Headers}},L=e=>(y.didError=e.didError,y.html=e.html,y.isStream=e.isStream,y.routerContext=e.routerContext,y.serverContext=e.serverContext,y),P=await i({createApp:(e,o)=>r.createElement(t,{server:{...o.appProps,req:H}},e),handler:u,renderToStream:d},c,{abortDelay:R,getState:v?({context:e})=>v({context:L(e)}):void 0,onError:({context:r,error:t})=>{const{code:s,message:n}=t;if(C?.({context:L(r),error:t}),k.info(e.red(`Stream error. Code: ${s}`)),[o.RenderAborted,o.RenderTimeout,o.RenderCancel].includes(s))return void k.info(e.dim(n));const{original:a}=t;k.error(a)},onResponse:E?({context:e,html:r})=>E({context:L(e),html:r}):void 0,onRouterReady:g?({context:e})=>g({context:L(e)}):void 0,onShellError:w?({context:e,error:r})=>w({context:L(e),error:r}):void 0,onShellReady:({context:e})=>{const r=L(e);((e,r)=>{if(r.headersSent||r.writableEnded)return;r.status(e.response.status??200),n(e.response.headers).forEach((([e,t])=>r.setHeader(e,t)));const t=a(e.response.headers);t.length&&r.setHeader("Set-Cookie",t)})(e,q);const t=S?.({context:r});return((e,r)=>{const t=new Headers(e.response.headers);t.delete("Set-Cookie");for(const[e,o]of Object.entries(r.getHeaders()))if("set-cookie"!==e.toLowerCase()&&void 0!==o)if(Array.isArray(o))for(const r of o)t.append(e,r);else t.set(e,"number"==typeof o?String(o):o);e.response.headers=t,e.response.status=r.statusCode})(e,q),t??{}},prepare:async({context:e,executionContext:r})=>{const t=L(e),o=h.get(x);await o.prepareDevAssets(e.routerContext?.matches);const n=o.injectAssets(t);t.hasEarlyHints&&await s(r,n)},routerRequestContext:y},{onEarlyHints:e=>m(q,e)});L(c),await(async(e,r)=>{const t=r.headers.get("Location");if(t&&r.status>=300&&r.status<400&&!e.headersSent&&!e.writableEnded&&!e.destroyed)return l(e,r),await(r.body?.cancel()),void e.redirect(r.status,t);await p(e,r)})(q,P)}finally{D.dispose()}}export{u as default};
|
|
2
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sources":["../../../src/adapters/express/render.tsx"],"sourcesContent":["import chalk from 'chalk';\nimport type { Request as ExpressRequest, Response as ExpressResponse } from 'express';\nimport React from 'react';\nimport type { StaticHandlerContext, StaticHandler } from 'react-router';\nimport createFetchRequest from '@adapters/express/create-request';\nimport type { TApp } from '@adapters/express/entry';\nimport StreamError from '@constants/stream-error';\nimport type { IServerContext } from '@context/server';\nimport emitEarlyHints from '@core/early-hints';\nimport { getHeaderEntries, getSetCookieHeaders } from '@core/headers';\nimport coreRender from '@core/render';\nimport type { ISsrRequestContext } from '@core/render';\nimport type { IObtainStreamErrorOut } from '@helpers/obtain-stream-error';\nimport renderToStream from '@node/render-to-stream';\nimport createRequestSignal from '@node/request-signal';\nimport writeEarlyHints from '@node/write-early-hints';\nimport writeFetchResponse, { writeFetchHeaders } from '@node/write-fetch-response';\nimport type ServerConfig from '@services/server-config';\nimport SsrManifest from '@services/ssr-manifest';\n\nexport interface IRequestContext<TAppProps = Record<any, any>> {\n req: ExpressRequest;\n res: ExpressResponse;\n appProps: NonNullable<TAppProps>;\n html: { header: string; footer: string };\n routerContext?: StaticHandlerContext;\n serverContext?: IServerContext;\n isStream?: boolean;\n hasEarlyHints?: boolean;\n didError?: StreamError;\n}\n\nexport type TRender<TAppProps = Record<any, any>> = (\n config: ServerConfig,\n context: IRequestContext<TAppProps>,\n options: IRenderOptions,\n) => Promise<void>;\n\nexport interface IRenderParams<TAppProps = Record<string, any>> {\n App: TApp<TAppProps>;\n handler: StaticHandler;\n}\n\nexport interface IRenderOptions<TAppProps = Record<string, any>> {\n abortDelay?: number;\n getBody?: (request: ExpressRequest) => BodyInit | null | undefined;\n onRouterReady?: (params: {\n context: IRequestContext<TAppProps>;\n }) => Promise<IRouterReadyOut> | IRouterReadyOut;\n onShellReady?: (params: { context: IRequestContext<TAppProps> }) => IShellReadyOut;\n onShellError?: (params: {\n context: IRequestContext<TAppProps>;\n error: Error;\n }) => string | undefined | void;\n onError?: (params: { context: IRequestContext<TAppProps>; error: IObtainStreamErrorOut }) => void;\n onResponse?: (params: {\n context: IRequestContext<TAppProps>;\n html: string;\n }) => string | undefined | void;\n getState?: (params: {\n context: IRequestContext<TAppProps>;\n }) => Record<string, Record<string, any>> | undefined | void;\n}\n\nexport interface IRouterReadyOut {\n isStream?: boolean;\n}\n\nexport interface IShellReadyOut {\n header?: string;\n footer?: string;\n}\n\n/**\n * Reconstruct parsed bodies only for request methods that can carry them.\n */\nconst createRenderRequest = (\n req: ExpressRequest,\n signal: AbortSignal,\n getBody: IRenderOptions['getBody'],\n): Request => {\n const options: { signal: AbortSignal; body?: BodyInit | null } = { signal };\n\n if (getBody && req.method !== 'GET' && req.method !== 'HEAD') {\n options.body = getBody(req) ?? null;\n }\n\n return createFetchRequest(req, options);\n};\n\n/**\n * Expose core response metadata to legacy hooks before they run.\n */\nconst prepareResponse = (context: ISsrRequestContext, res: ExpressResponse): void => {\n if (res.headersSent || res.writableEnded) {\n return;\n }\n\n res.status(context.response.status ?? 200);\n getHeaderEntries(context.response.headers).forEach(([name, value]) => res.setHeader(name, value));\n\n const cookies = getSetCookieHeaders(context.response.headers);\n\n if (cookies.length) {\n res.setHeader('Set-Cookie', cookies);\n }\n};\n\n/**\n * Copy legacy hook metadata back to the core; cookies remain on the live response.\n */\nconst syncResponse = (context: ISsrRequestContext, res: ExpressResponse): void => {\n const headers = new Headers(context.response.headers);\n\n headers.delete('Set-Cookie');\n\n for (const [name, value] of Object.entries(res.getHeaders())) {\n if (name.toLowerCase() === 'set-cookie' || 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 context.response.headers = headers;\n context.response.status = res.statusCode;\n};\n\n/**\n * Preserve Express redirect formatting before falling back to the Fetch body writer.\n */\nconst writeResponse = async (res: ExpressResponse, response: Response): Promise<void> => {\n const location = response.headers.get('Location');\n\n if (\n location &&\n response.status >= 300 &&\n response.status < 400 &&\n !res.headersSent &&\n !res.writableEnded &&\n !res.destroyed\n ) {\n writeFetchHeaders(res, response);\n await response.body?.cancel();\n res.redirect(response.status, location);\n\n return;\n }\n\n await writeFetchResponse(res, response);\n};\n\n/**\n * Render application\n */\nasync function render(\n { App, handler }: IRenderParams,\n config: ServerConfig,\n context: IRequestContext,\n {\n onRouterReady,\n onShellReady,\n onResponse,\n onShellError,\n onError,\n getBody,\n getState,\n abortDelay = 15000,\n }: IRenderOptions,\n): Promise<void> {\n const { appProps, html: shellHtml, req, res } = context;\n const Logger = config.getLogger();\n const requestSignal = createRequestSignal(req, res);\n try {\n const coreContext: ISsrRequestContext = {\n appProps,\n html: shellHtml,\n request: createRenderRequest(req, requestSignal.signal, getBody),\n response: {\n headers: new Headers(),\n },\n };\n\n /**\n * Keep legacy hooks attached to their original mutable request context.\n */\n const syncContext = (updated: ISsrRequestContext): IRequestContext => {\n context.didError = updated.didError;\n context.html = updated.html;\n context.isStream = updated.isStream;\n context.routerContext = updated.routerContext;\n context.serverContext = updated.serverContext;\n\n return context;\n };\n const response = await coreRender(\n {\n /**\n * Preserve the legacy App server props and live Express request.\n */\n createApp: (children, updated) => (\n <App server={{ ...updated.appProps, req }}>{children}</App>\n ),\n handler,\n renderToStream,\n },\n coreContext,\n {\n abortDelay,\n\n /**\n * Read custom state through the legacy request context.\n */\n getState: getState\n ? ({ context: updated }) => getState({ context: syncContext(updated) })\n : undefined,\n\n /**\n * Preserve legacy error hooks and keep expected aborts at info level.\n */\n onError: ({ context: updated, error }) => {\n const { code, message } = error;\n\n onError?.({ context: syncContext(updated), error });\n Logger.info(chalk.red(`Stream error. Code: ${code}`));\n\n if (\n [\n StreamError.RenderAborted,\n StreamError.RenderTimeout,\n StreamError.RenderCancel,\n ].includes(code)\n ) {\n Logger.info(chalk.dim(message));\n\n return;\n }\n\n const { original } = error;\n\n Logger.error(original as string);\n },\n\n /**\n * Transform streamed HTML using the existing legacy hook signature.\n */\n onResponse: onResponse\n ? ({ context: updated, html }) => onResponse({ context: syncContext(updated), html })\n : undefined,\n\n /**\n * Expose router state before the legacy hook chooses a rendering mode.\n */\n onRouterReady: onRouterReady\n ? ({ context: updated }) => onRouterReady({ context: syncContext(updated) })\n : undefined,\n\n /**\n * Let the legacy hook replace the default shell-error response.\n */\n onShellError: onShellError\n ? ({ context: updated, error }) => onShellError({ context: syncContext(updated), error })\n : undefined,\n\n /**\n * Synchronize response metadata around the legacy shell hook.\n */\n onShellReady: ({ context: updated }) => {\n const legacyContext = syncContext(updated);\n\n prepareResponse(updated, res);\n\n const shell = onShellReady?.({ context: legacyContext });\n\n syncResponse(updated, res);\n\n return shell ?? {};\n },\n\n /**\n * Prepare Vite assets and send requested early hints before rendering.\n */\n prepare: async ({ context: updated, executionContext }) => {\n const legacyContext = syncContext(updated);\n const manifest = SsrManifest.get(config);\n\n await manifest.prepareDevAssets(updated.routerContext?.matches);\n\n const hints = manifest.injectAssets(legacyContext);\n\n if (legacyContext.hasEarlyHints) {\n await emitEarlyHints(executionContext, hints);\n }\n },\n routerRequestContext: context,\n },\n {\n /**\n * Deliver early hints through the existing Express response.\n */\n onEarlyHints: (headers) => writeEarlyHints(res, headers),\n },\n );\n\n syncContext(coreContext);\n\n await writeResponse(res, response);\n } finally {\n requestSignal.dispose();\n }\n}\n\nexport default render;\n"],"names":["createRenderRequest","req","signal","getBody","options","method","body","createFetchRequest","async","render","App","handler","config","context","onRouterReady","onShellReady","onResponse","onShellError","onError","getState","abortDelay","appProps","html","shellHtml","res","Logger","getLogger","requestSignal","createRequestSignal","coreContext","request","response","headers","Headers","syncContext","updated","didError","isStream","routerContext","serverContext","coreRender","createApp","children","React","createElement","server","renderToStream","undefined","error","code","message","info","chalk","red","StreamError","RenderAborted","RenderTimeout","RenderCancel","includes","dim","original","legacyContext","headersSent","writableEnded","status","getHeaderEntries","forEach","name","value","setHeader","cookies","getSetCookieHeaders","length","prepareResponse","shell","delete","Object","entries","getHeaders","toLowerCase","Array","isArray","item","append","set","String","statusCode","syncResponse","prepare","executionContext","manifest","SsrManifest","get","prepareDevAssets","matches","hints","injectAssets","hasEarlyHints","emitEarlyHints","routerRequestContext","onEarlyHints","writeEarlyHints","location","destroyed","writeFetchHeaders","cancel","redirect","writeFetchResponse","writeResponse","dispose"],"mappings":"4hBA4EA,MAAMA,EAAsB,CAC1BC,EACAC,EACAC,KAEA,MAAMC,EAA2D,CAAEF,UAMnE,OAJIC,GAA0B,QAAfF,EAAII,QAAmC,SAAfJ,EAAII,SACzCD,EAAQE,KAAOH,EAAQF,IAAQ,MAG1BM,EAAmBN,EAAKG,EAAQ,EA0EzCI,eAAeC,GACbC,IAAEA,EAAGC,QAAEA,GACPC,EACAC,GACAC,cACEA,EAAaC,aACbA,EAAYC,WACZA,EAAUC,aACVA,EAAYC,QACZA,EAAOf,QACPA,EAAOgB,SACPA,EAAQC,WACRA,EAAa,OAGf,MAAMC,SAAEA,EAAUC,KAAMC,EAAStB,IAAEA,EAAGuB,IAAEA,GAAQX,EAC1CY,EAASb,EAAOc,YAChBC,EAAgBC,EAAoB3B,EAAKuB,GAC/C,IACE,MAAMK,EAAkC,CACtCR,WACAC,KAAMC,EACNO,QAAS9B,EAAoBC,EAAK0B,EAAczB,OAAQC,GACxD4B,SAAU,CACRC,QAAS,IAAIC,UAOXC,EAAeC,IACnBtB,EAAQuB,SAAWD,EAAQC,SAC3BvB,EAAQS,KAAOa,EAAQb,KACvBT,EAAQwB,SAAWF,EAAQE,SAC3BxB,EAAQyB,cAAgBH,EAAQG,cAChCzB,EAAQ0B,cAAgBJ,EAAQI,cAEzB1B,GAEHkB,QAAiBS,EACrB,CAIEC,UAAW,CAACC,EAAUP,IACpBQ,EAAAC,cAAClC,EAAG,CAACmC,OAAQ,IAAKV,EAAQd,SAAUpB,QAAQyC,GAE9C/B,UACAmC,kBAEFjB,EACA,CACET,aAKAD,SAAUA,EACN,EAAGN,QAASsB,KAAchB,EAAS,CAAEN,QAASqB,EAAYC,UAC1DY,EAKJ7B,QAAS,EAAGL,QAASsB,EAASa,YAC5B,MAAMC,KAAEA,EAAIC,QAAEA,GAAYF,EAK1B,GAHA9B,IAAU,CAAEL,QAASqB,EAAYC,GAAUa,UAC3CvB,EAAO0B,KAAKC,EAAMC,IAAI,uBAAuBJ,MAG3C,CACEK,EAAYC,cACZD,EAAYE,cACZF,EAAYG,cACZC,SAAST,GAIX,YAFAxB,EAAO0B,KAAKC,EAAMO,IAAIT,IAKxB,MAAMU,SAAEA,GAAaZ,EAErBvB,EAAOuB,MAAMY,EAAmB,EAMlC5C,WAAYA,EACR,EAAGH,QAASsB,EAASb,UAAWN,EAAW,CAAEH,QAASqB,EAAYC,GAAUb,cAC5EyB,EAKJjC,cAAeA,EACX,EAAGD,QAASsB,KAAcrB,EAAc,CAAED,QAASqB,EAAYC,UAC/DY,EAKJ9B,aAAcA,EACV,EAAGJ,QAASsB,EAASa,WAAY/B,EAAa,CAAEJ,QAASqB,EAAYC,GAAUa,eAC/ED,EAKJhC,aAAc,EAAGF,QAASsB,MACxB,MAAM0B,EAAgB3B,EAAYC,GArLpB,EAACtB,EAA6BW,KACpD,GAAIA,EAAIsC,aAAetC,EAAIuC,cACzB,OAGFvC,EAAIwC,OAAOnD,EAAQkB,SAASiC,QAAU,KACtCC,EAAiBpD,EAAQkB,SAASC,SAASkC,SAAQ,EAAEC,EAAMC,KAAW5C,EAAI6C,UAAUF,EAAMC,KAE1F,MAAME,EAAUC,EAAoB1D,EAAQkB,SAASC,SAEjDsC,EAAQE,QACVhD,EAAI6C,UAAU,aAAcC,EAC9B,EA2KQG,CAAgBtC,EAASX,GAEzB,MAAMkD,EAAQ3D,IAAe,CAAEF,QAASgD,IAIxC,MA3KW,EAAChD,EAA6BW,KACjD,MAAMQ,EAAU,IAAIC,QAAQpB,EAAQkB,SAASC,SAE7CA,EAAQ2C,OAAO,cAEf,IAAK,MAAOR,EAAMC,KAAUQ,OAAOC,QAAQrD,EAAIsD,cAC7C,GAA2B,eAAvBX,EAAKY,oBAA4ChC,IAAVqB,EAI3C,GAAIY,MAAMC,QAAQb,GAChB,IAAK,MAAMc,KAAQd,EACjBpC,EAAQmD,OAAOhB,EAAMe,QAGvBlD,EAAQoD,IAAIjB,EAAuB,iBAAVC,EAAqBiB,OAAOjB,GAASA,GAIlEvD,EAAQkB,SAASC,QAAUA,EAC3BnB,EAAQkB,SAASiC,OAASxC,EAAI8D,UAAU,EAqJhCC,CAAapD,EAASX,GAEfkD,GAAS,CAAA,CAAE,EAMpBc,QAAShF,OAASK,QAASsB,EAASsD,uBAClC,MAAM5B,EAAgB3B,EAAYC,GAC5BuD,EAAWC,EAAYC,IAAIhF,SAE3B8E,EAASG,iBAAiB1D,EAAQG,eAAewD,SAEvD,MAAMC,EAAQL,EAASM,aAAanC,GAEhCA,EAAcoC,qBACVC,EAAeT,EAAkBM,EACzC,EAEFI,qBAAsBtF,GAExB,CAIEuF,aAAepE,GAAYqE,EAAgB7E,EAAKQ,KAIpDE,EAAYL,QA7KMrB,OAAOgB,EAAsBO,KACjD,MAAMuE,EAAWvE,EAASC,QAAQ4D,IAAI,YAEtC,GACEU,GACAvE,EAASiC,QAAU,KACnBjC,EAASiC,OAAS,MACjBxC,EAAIsC,cACJtC,EAAIuC,gBACJvC,EAAI+E,UAML,OAJAC,EAAkBhF,EAAKO,SACjBA,EAASzB,MAAMmG,eACrBjF,EAAIkF,SAAS3E,EAASiC,OAAQsC,SAK1BK,EAAmBnF,EAAKO,EAAS,EA6J/B6E,CAAcpF,EAAKO,EAC3B,SACEJ,EAAckF,SAChB,CACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Server } from 'node:net';
|
|
3
3
|
import { Express } from 'express';
|
|
4
|
-
import ServerConfig from "
|
|
4
|
+
import ServerConfig from "../../services/server-config.js";
|
|
5
5
|
interface ICreateServerOut {
|
|
6
6
|
run: (options?: {
|
|
7
7
|
version?: string;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"node:http";import r from"node:https";import t from"node:path";import s from"compression";import o from"express";import i from"./prepare-server.js";import a from"../../helpers/print-server-info.js";import n from"../../services/server-api.js";async function p(p){const d=o().disable("x-powered-by");p.isProd&&d.set("env","production");const c=new n;p.setApp(d);const m=i.init(p,c);if(!p.isProd){const e=await(await import("vite")).createServer({server:{middlewareMode:!0,watch:{usePolling:!0,interval:100}},appType:"custom",mode:p.mode});d.use(e.middlewares),p.setVite(e)}const{isSPA:l}=p.getParams();if(l||await m.onAppCreated(),p.isProd){const{root:e,publicDir:r}=p.getParams(),{compression:i,expressStatic:a}=m.getMiddlewaresConfig();if(i&&d.use(s(i)),l||d.use(((e,r,t)=>{"/index.html"!==e.url||c.hasAccessIndexHtml()||(e.url="/index-not-found.html"),t()})),a){const{basename:s,...i}=a;d.use(s,o.static(t.resolve(`${e}/${r}`),{...i,index:!!l&&void 0}))}}return l?d.use(/(.*)/,((e,r,t)=>{(async()=>{try{const t=(await m.loadHtml(e)).join("");r.send(t)}catch(e){p.getLogger().error(`Failed to handle request: ${e?.message}`,{error:e}),t(e)}})()})):d.use(/(.*)/,((e,r,t)=>{(async()=>{try{const[{render:s,onRequest:o,...i},a]=await Promise.all([m.loadEntrypoint(),m.loadHtml(e)]),{appProps:n,hasEarlyHints:d,shouldSkip:c,shouldCancel:l}=await(o?.(e,r))??{},[u,f]=a;if(l)return;if(c)return t();const h={req:e,res:r,hasEarlyHints:d,appProps:n??{},html:{header:u,footer:f}};await s(p,h,i)}catch(e){p.getLogger().error(`Failed to handle request: ${e?.message}`,{error:e}),t(e)}})()})),{run:({version:t,isPrintInfo:s=!0}={})=>{const{port:o,host:i}=p.getParams(),n=Boolean(p.getVite()?.config?.server?.https);p.isHost&&!p.isProd&&(p.getVite().config.server.host=i);const l=(n?r.createServer(p.getVite().config.server.https,d):e.createServer(d)).listen(o,i,(()=>{m.onServerStarted?.(d,c,l),s&&a(p,{version:t,server:l})}));return l},app:d}}export{p as default};
|
|
2
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../../src/adapters/express/server.ts"],"sourcesContent":["import http from 'node:http';\nimport https from 'node:https';\nimport type { Server } from 'node:net';\nimport path from 'node:path';\nimport compression from 'compression';\nimport type { Express } from 'express';\nimport express from 'express';\nimport PrepareServer from '@adapters/express/prepare-server';\nimport type { IRequestContext } from '@adapters/express/render';\nimport printServerInfo from '@helpers/print-server-info';\nimport ServerApi from '@services/server-api';\nimport type ServerConfig from '@services/server-config';\n\nexport interface ICreateServerOut {\n run: (options?: { version?: string; isPrintInfo?: boolean }) => Server;\n app: Express;\n}\n\n/**\n * Create SSR server\n */\nasync function createServer(config: ServerConfig): Promise<ICreateServerOut> {\n const app = express().disable('x-powered-by');\n\n if (config.isProd) {\n app.set('env', 'production');\n }\n\n const serverApi = new ServerApi();\n\n config.setApp(app);\n\n const prepareServer = PrepareServer.init(config, serverApi);\n\n if (!config.isProd) {\n /**\n * Create Vite server in middleware mode and configure the app type as\n * 'custom', disabling Vite's own HTML serving logic so parent server\n * can take control\n */\n const vite = await (\n await import('vite')\n ).createServer({\n server: {\n middlewareMode: true,\n watch: {\n /**\n * During tests, we edit the files too fast and sometimes chokidar\n * misses change events, so enforce polling for consistency\n */\n usePolling: true,\n interval: 100,\n },\n },\n appType: 'custom',\n mode: config.mode,\n });\n\n /**\n * Use vite's connect instance as middleware\n */\n app.use(vite.middlewares);\n\n config.setVite(vite);\n }\n\n const { isSPA } = config.getParams();\n\n if (!isSPA) {\n await prepareServer.onAppCreated();\n }\n\n if (config.isProd) {\n const { root, publicDir } = config.getParams();\n const { compression: compressionConfig, expressStatic } = prepareServer.getMiddlewaresConfig();\n\n if (compressionConfig) {\n app.use(compression(compressionConfig));\n }\n\n if (!isSPA) {\n /**\n * ignore index.html file in SSR mode\n */\n app.use((req, _, next) => {\n if (req.url === '/index.html' && !serverApi.hasAccessIndexHtml()) {\n req.url = '/index-not-found.html';\n }\n\n next();\n });\n }\n\n if (expressStatic) {\n const { basename, ...expressStaticOpts } = expressStatic;\n\n app.use(\n basename!,\n express.static(path.resolve(`${root}/${publicDir}`), {\n ...expressStaticOpts,\n index: isSPA ? undefined : false,\n }),\n );\n }\n }\n\n /**\n * SSR mode\n */\n if (!isSPA) {\n app.use(/(.*)/, (req, res, next) => {\n void (async () => {\n try {\n const [{ render, onRequest, ...renderParams }, clientHtml] = await Promise.all([\n prepareServer.loadEntrypoint(),\n prepareServer.loadHtml(req),\n ]);\n const { appProps, hasEarlyHints, shouldSkip, shouldCancel } =\n (await onRequest?.(req, res)) ?? {};\n const [header, footer] = clientHtml;\n\n if (shouldCancel) {\n return;\n }\n\n if (shouldSkip) {\n return next();\n }\n\n const context: IRequestContext = {\n req,\n res,\n hasEarlyHints,\n appProps: appProps ?? {},\n html: { header, footer },\n };\n\n await render(config, context, renderParams);\n } catch (e) {\n config\n .getLogger()\n .error(`Failed to handle request: ${(e as Error)?.message}`, { error: e as Error });\n next(e);\n }\n })();\n });\n } else {\n /**\n * SPA mode, redirect any request to index.html\n */\n app.use(/(.*)/, (req, res, next) => {\n void (async () => {\n try {\n const html = (await prepareServer.loadHtml(req)).join('');\n\n res.send(html);\n } catch (e) {\n config\n .getLogger()\n .error(`Failed to handle request: ${(e as Error)?.message}`, { error: e as Error });\n next(e);\n }\n })();\n });\n }\n\n return {\n /**\n * Start the configured transport and notify the server lifecycle hook.\n */\n run: ({ version, isPrintInfo = true } = {}): Server => {\n const { port, host } = config.getParams();\n const isHTTPS = Boolean(config.getVite()?.config?.server?.https);\n\n /**\n * update resolved host for print network link\n */\n if (config.isHost && !config.isProd) {\n config.getVite()!.config.server.host = host;\n }\n\n const server = (\n isHTTPS\n ? https.createServer(config.getVite()!.config.server.https!, app)\n : http.createServer(app)\n ).listen(port, host, () => {\n void prepareServer.onServerStarted?.(app, serverApi, server);\n\n if (!isPrintInfo) {\n return;\n }\n\n void printServerInfo(config, { version, server });\n });\n\n return server;\n },\n app,\n };\n}\n\nexport default createServer;\n"],"names":["async","createServer","config","app","express","disable","isProd","set","serverApi","ServerApi","setApp","prepareServer","PrepareServer","init","vite","import","server","middlewareMode","watch","usePolling","interval","appType","mode","use","middlewares","setVite","isSPA","getParams","onAppCreated","root","publicDir","compression","compressionConfig","expressStatic","getMiddlewaresConfig","req","_","next","url","hasAccessIndexHtml","basename","expressStaticOpts","static","path","resolve","index","undefined","res","html","loadHtml","join","send","e","getLogger","error","message","render","onRequest","renderParams","clientHtml","Promise","all","loadEntrypoint","appProps","hasEarlyHints","shouldSkip","shouldCancel","header","footer","context","run","version","isPrintInfo","port","host","isHTTPS","Boolean","getVite","https","isHost","http","listen","onServerStarted","printServerInfo"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Request, RequestHandler } from 'express';
|
|
2
|
+
import { INodeAdapterOptions } from "./node.js";
|
|
3
|
+
import { TSsrHandler } from "../core/types.js";
|
|
4
|
+
interface IExpressAdapterOptions extends INodeAdapterOptions {
|
|
5
|
+
getBody?: (request: Request) => BodyInit | null | undefined;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Bridge Express middleware to Fetch while retaining parsed body support.
|
|
9
|
+
*/
|
|
10
|
+
declare const adapterExpress: (handler: TSsrHandler, options?: IExpressAdapterOptions) => RequestHandler;
|
|
11
|
+
export { adapterExpress as default, IExpressAdapterOptions };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import o from"./body.js";import s from"./express/create-request.js";import{handleRequest as e}from"./node.js";import t from"../node/request-signal.js";const r=(r,n={})=>(d,i,a)=>{const p=t(d,i),c=o=>{p.signal.aborted||a(o)};try{const t={signal:p.signal};n.getBody?t.body=n.getBody(d)??null:void 0!==d.body&&(t.body=o(d.body,d.get("Content-Type"))),e(r,s(d,t),i,{compression:n.compression}).catch(c).finally(p.dispose)}catch(o){p.dispose(),c(o)}};export{r as default};
|
|
2
|
+
//# sourceMappingURL=express.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.js","sources":["../../src/adapters/express.ts"],"sourcesContent":["import type { NextFunction, Request, RequestHandler, Response as ExpressResponse } from 'express';\nimport serializeBody from '@adapters/body';\nimport createFetchRequest from '@adapters/express/create-request';\nimport { handleRequest } from '@adapters/node';\nimport type { INodeAdapterOptions } from '@adapters/node';\nimport type { TSsrHandler } from '@core/types';\nimport createRequestSignal from '@node/request-signal';\n\nexport interface IExpressAdapterOptions extends INodeAdapterOptions {\n getBody?: (request: Request) => BodyInit | null | undefined;\n}\n\n/**\n * Bridge Express middleware to Fetch while retaining parsed body support.\n */\nconst adapterExpress = (\n handler: TSsrHandler,\n options: IExpressAdapterOptions = {},\n): RequestHandler => {\n /**\n * Preserve Express routing semantics and dispose request listeners on completion.\n */\n return (req: Request, res: ExpressResponse, next: NextFunction): void => {\n const requestSignal = createRequestSignal(req, res);\n\n /**\n * Forward failures only while the client connection is still active.\n */\n const onError = (error: unknown): void => {\n if (!requestSignal.signal.aborted) {\n next(error);\n }\n };\n\n try {\n const requestOptions: { body?: BodyInit | null; signal: AbortSignal } = {\n signal: requestSignal.signal,\n };\n\n if (options.getBody) {\n requestOptions.body = options.getBody(req) ?? null;\n } else if (req.body !== undefined) {\n requestOptions.body = serializeBody(req.body, req.get('Content-Type'));\n }\n\n void handleRequest(handler, createFetchRequest(req, requestOptions), res, {\n compression: options.compression,\n })\n .catch(onError)\n .finally(requestSignal.dispose);\n } catch (error) {\n requestSignal.dispose();\n onError(error);\n }\n };\n};\n\nexport default adapterExpress;\n"],"names":["adapterExpress","handler","options","req","res","next","requestSignal","createRequestSignal","onError","error","signal","aborted","requestOptions","getBody","body","undefined","serializeBody","get","handleRequest","createFetchRequest","compression","catch","finally","dispose"],"mappings":"uJAeA,MAAMA,EAAiB,CACrBC,EACAC,EAAkC,KAK3B,CAACC,EAAcC,EAAsBC,KAC1C,MAAMC,EAAgBC,EAAoBJ,EAAKC,GAKzCI,EAAWC,IACVH,EAAcI,OAAOC,SACxBN,EAAKI,EACP,EAGF,IACE,MAAMG,EAAkE,CACtEF,OAAQJ,EAAcI,QAGpBR,EAAQW,QACVD,EAAeE,KAAOZ,EAAQW,QAAQV,IAAQ,UACxBY,IAAbZ,EAAIW,OACbF,EAAeE,KAAOE,EAAcb,EAAIW,KAAMX,EAAIc,IAAI,kBAGnDC,EAAcjB,EAASkB,EAAmBhB,EAAKS,GAAiBR,EAAK,CACxEgB,YAAalB,EAAQkB,cAEpBC,MAAMb,GACNc,QAAQhB,EAAciB,QAC3B,CAAE,MAAOd,GACPH,EAAciB,UACdf,EAAQC,EACV"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { TCompression } from "./compression.js";
|
|
2
|
+
import { TSsrHandler } from "../core/types.js";
|
|
3
|
+
import { TIncomingMessage, TServerResponse } from "../node/http.js";
|
|
4
|
+
interface IFastifyRequest {
|
|
5
|
+
body?: unknown;
|
|
6
|
+
raw: TIncomingMessage;
|
|
7
|
+
}
|
|
8
|
+
interface IFastifyReply {
|
|
9
|
+
getHeaders?: () => Record<string, string | number | string[] | undefined>;
|
|
10
|
+
hijack: () => void;
|
|
11
|
+
raw: TServerResponse;
|
|
12
|
+
}
|
|
13
|
+
type TFastifyHandler = (request: IFastifyRequest, reply: IFastifyReply) => Promise<void>;
|
|
14
|
+
interface IFastifyAdapterOptions {
|
|
15
|
+
compression?: TCompression;
|
|
16
|
+
getBody?: (request: IFastifyRequest) => BodyInit | null | undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Bridge Fastify requests and hook metadata to the raw streaming transport.
|
|
20
|
+
*/
|
|
21
|
+
declare const adapterFastify: (handler: TSsrHandler, options?: IFastifyAdapterOptions) => TFastifyHandler;
|
|
22
|
+
export { adapterFastify as default, IFastifyAdapterOptions };
|
|
23
|
+
export type { IFastifyReply, IFastifyRequest, TFastifyHandler };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import o from"./body.js";import e from"../node/compress-response.js";import r from"../node/create-request.js";import t from"../node/request-signal.js";import s from"../node/write-early-hints.js";import a from"../node/write-fetch-response.js";const i=(i,n={})=>async(d,c)=>{const{body:m,raw:p}=d,y=t(p,c.raw);try{const t={signal:y.signal};n.getBody?t.body=n.getBody(d)??null:void 0!==m&&(t.body=o(m,String(p.headers["content-type"]??"")));const f=r(p,t),l=e(f,await i(f,{onEarlyHints:o=>s(c.raw,o)}),n.compression);Object.entries(c.getHeaders?.()??{}).forEach((([o,e])=>{void 0!==e&&c.raw.setHeader(o,e)})),c.hijack(),await a(c.raw,l)}catch(o){if(!y.signal.aborted)throw o;c.hijack()}finally{y.dispose()}};export{i as default};
|
|
2
|
+
//# sourceMappingURL=fastify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastify.js","sources":["../../src/adapters/fastify.ts"],"sourcesContent":["import serializeBody from '@adapters/body';\nimport type { TCompression } from '@adapters/compression';\nimport type { TSsrHandler } from '@core/types';\nimport compressResponse from '@node/compress-response';\nimport createRequest from '@node/create-request';\nimport type { TIncomingMessage, TServerResponse } from '@node/http';\nimport createRequestSignal from '@node/request-signal';\nimport writeEarlyHints from '@node/write-early-hints';\nimport writeFetchResponse from '@node/write-fetch-response';\n\ninterface IFastifyRequest {\n body?: unknown;\n raw: TIncomingMessage;\n}\n\ninterface IFastifyReply {\n getHeaders?: () => Record<string, string | number | string[] | undefined>;\n hijack: () => void;\n raw: TServerResponse;\n}\n\ntype TFastifyHandler = (request: IFastifyRequest, reply: IFastifyReply) => Promise<void>;\n\nexport interface IFastifyAdapterOptions {\n compression?: TCompression;\n getBody?: (request: IFastifyRequest) => BodyInit | null | undefined;\n}\n\n/**\n * Bridge Fastify requests and hook metadata to the raw streaming transport.\n */\nconst adapterFastify = (\n handler: TSsrHandler,\n options: IFastifyAdapterOptions = {},\n): TFastifyHandler => {\n /**\n * Render one request before handing response ownership to the raw transport.\n */\n return async (request, reply) => {\n const { body, raw: req } = request;\n const requestSignal = createRequestSignal(req, reply.raw);\n\n try {\n const requestOptions: { body?: BodyInit | null; signal: AbortSignal } = {\n signal: requestSignal.signal,\n };\n\n if (options.getBody) {\n requestOptions.body = options.getBody(request) ?? null;\n } else if (body !== undefined) {\n requestOptions.body = serializeBody(body, String(req.headers['content-type'] ?? ''));\n }\n\n const fetchRequest = createRequest(req, requestOptions);\n const response = compressResponse(\n fetchRequest,\n await handler(fetchRequest, {\n onEarlyHints: (headers) => writeEarlyHints(reply.raw, headers),\n }),\n options.compression,\n );\n\n /**\n * Fastify keeps reply.header() values separately from the raw Node response.\n */\n Object.entries(reply.getHeaders?.() ?? {}).forEach(([name, value]) => {\n if (value !== undefined) {\n reply.raw.setHeader(name, value);\n }\n });\n reply.hijack();\n await writeFetchResponse(reply.raw, response);\n } catch (error) {\n if (!requestSignal.signal.aborted) {\n throw error;\n }\n\n /**\n * The socket is gone; prevent Fastify from attempting an automatic response.\n */\n reply.hijack();\n } finally {\n requestSignal.dispose();\n }\n };\n};\n\nexport type { IFastifyReply, IFastifyRequest, TFastifyHandler };\n\nexport default adapterFastify;\n"],"names":["adapterFastify","handler","options","async","request","reply","body","raw","req","requestSignal","createRequestSignal","requestOptions","signal","getBody","undefined","serializeBody","String","headers","fetchRequest","createRequest","response","compressResponse","onEarlyHints","writeEarlyHints","compression","Object","entries","getHeaders","forEach","name","value","setHeader","hijack","writeFetchResponse","error","aborted","dispose"],"mappings":"kPA+BA,MAAMA,EAAiB,CACrBC,EACAC,EAAkC,KAK3BC,MAAOC,EAASC,KACrB,MAAMC,KAAEA,EAAMC,IAAKC,GAAQJ,EACrBK,EAAgBC,EAAoBF,EAAKH,EAAME,KAErD,IACE,MAAMI,EAAkE,CACtEC,OAAQH,EAAcG,QAGpBV,EAAQW,QACVF,EAAeL,KAAOJ,EAAQW,QAAQT,IAAY,UAChCU,IAATR,IACTK,EAAeL,KAAOS,EAAcT,EAAMU,OAAOR,EAAIS,QAAQ,iBAAmB,MAGlF,MAAMC,EAAeC,EAAcX,EAAKG,GAClCS,EAAWC,EACfH,QACMjB,EAAQiB,EAAc,CAC1BI,aAAeL,GAAYM,EAAgBlB,EAAME,IAAKU,KAExDf,EAAQsB,aAMVC,OAAOC,QAAQrB,EAAMsB,gBAAkB,CAAA,GAAIC,SAAQ,EAAEC,EAAMC,WAC3ChB,IAAVgB,GACFzB,EAAME,IAAIwB,UAAUF,EAAMC,EAC5B,IAEFzB,EAAM2B,eACAC,EAAmB5B,EAAME,IAAKa,EACtC,CAAE,MAAOc,GACP,IAAKzB,EAAcG,OAAOuB,QACxB,MAAMD,EAMR7B,EAAM2B,QACR,SACEvB,EAAc2B,SAChB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { TSsrHandler } from "../core/types.js";
|
|
2
|
+
interface IHonoContext {
|
|
3
|
+
req: {
|
|
4
|
+
raw: Request;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
type THonoHandler = (context: IHonoContext) => Promise<Response>;
|
|
8
|
+
/**
|
|
9
|
+
* Pass Hono's raw request to the Fetch handler.
|
|
10
|
+
*/
|
|
11
|
+
declare const adapterHono: (handler: TSsrHandler) => THonoHandler;
|
|
12
|
+
export type { IHonoContext, THonoHandler };
|
|
13
|
+
export { adapterHono as default };
|
package/adapters/hono.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.js","sources":["../../src/adapters/hono.ts"],"sourcesContent":["import type { TSsrHandler } from '@core/types';\n\ninterface IHonoContext {\n req: {\n raw: Request;\n };\n}\n\ntype THonoHandler = (context: IHonoContext) => Promise<Response>;\n\n/**\n * Pass Hono's raw request to the Fetch handler.\n */\nconst adapterHono = (handler: TSsrHandler): THonoHandler => {\n return ({ req }) => handler(req.raw);\n};\n\nexport type { IHonoContext, THonoHandler };\n\nexport default adapterHono;\n"],"names":["adapterHono","handler","req","raw"],"mappings":"AAaA,MAAMA,EAAeC,GACZ,EAAGC,SAAUD,EAAQC,EAAIC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TCompression } from "./compression.js";
|
|
2
|
+
import { TSsrHandler } from "../core/types.js";
|
|
3
|
+
import { TIncomingMessage, TServerResponse } from "../node/http.js";
|
|
4
|
+
type TNext = (error?: unknown) => void;
|
|
5
|
+
type TNodeHandler = (req: TIncomingMessage, res: TServerResponse, next?: TNext) => Promise<void>;
|
|
6
|
+
interface INodeAdapterOptions {
|
|
7
|
+
compression?: TCompression;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Render and write a Fetch response using the Node transport.
|
|
11
|
+
*/
|
|
12
|
+
declare const handleRequest: (handler: TSsrHandler, request: Request, res: TServerResponse, { compression }?: INodeAdapterOptions) => Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Connect Node requests to the Fetch handler and dispose disconnect listeners.
|
|
15
|
+
*/
|
|
16
|
+
declare const adapterNode: (handler: TSsrHandler, options?: INodeAdapterOptions) => TNodeHandler;
|
|
17
|
+
export { adapterNode as default, INodeAdapterOptions, handleRequest };
|
|
18
|
+
export type { TNodeHandler };
|
package/adapters/node.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import e from"../node/compress-response.js";import r from"../node/create-request.js";import s from"../node/request-signal.js";import o from"../node/write-early-hints.js";import t from"../node/write-fetch-response.js";const n=async(r,s,n,{compression:a=!1}={})=>{const i=e(s,await r(s,{onEarlyHints:e=>o(n,e)}),a);await t(n,i)},a=(e,o={})=>async(t,a,i)=>{const d=s(t,a);try{await n(e,r(t,{signal:d.signal}),a,o)}catch(e){if(d.signal.aborted)return;i?i(e):a.headersSent?a.destroy(e):(a.statusCode=500,a.end("Internal Server Error"))}finally{d.dispose()}};export{a as default,n as handleRequest};
|
|
2
|
+
//# sourceMappingURL=node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sources":["../../src/adapters/node.ts"],"sourcesContent":["import type { TCompression } from '@adapters/compression';\nimport type { TSsrHandler } from '@core/types';\nimport compressResponse from '@node/compress-response';\nimport createRequest from '@node/create-request';\nimport type { TIncomingMessage, TServerResponse } from '@node/http';\nimport createRequestSignal from '@node/request-signal';\nimport writeEarlyHints from '@node/write-early-hints';\nimport writeFetchResponse from '@node/write-fetch-response';\n\ntype TNext = (error?: unknown) => void;\ntype TNodeHandler = (req: TIncomingMessage, res: TServerResponse, next?: TNext) => Promise<void>;\n\nexport interface INodeAdapterOptions {\n compression?: TCompression;\n}\n\n/**\n * Render and write a Fetch response using the Node transport.\n */\nconst handleRequest = async (\n handler: TSsrHandler,\n request: Request,\n res: TServerResponse,\n { compression = false }: INodeAdapterOptions = {},\n): Promise<void> => {\n const response = compressResponse(\n request,\n await handler(request, {\n onEarlyHints: (headers) => writeEarlyHints(res, headers),\n }),\n compression,\n );\n\n await writeFetchResponse(res, response);\n};\n\n/**\n * Connect Node requests to the Fetch handler and dispose disconnect listeners.\n */\nconst adapterNode = (handler: TSsrHandler, options: INodeAdapterOptions = {}): TNodeHandler => {\n /**\n * Scope disconnect handling to the lifetime of this Node request.\n */\n return async (req, res, next) => {\n const requestSignal = createRequestSignal(req, res);\n\n try {\n await handleRequest(\n handler,\n createRequest(req, { signal: requestSignal.signal }),\n res,\n options,\n );\n } catch (error) {\n if (requestSignal.signal.aborted) {\n return;\n }\n\n if (next) {\n next(error);\n } else if (res.headersSent) {\n res.destroy(error as Error);\n } else {\n res.statusCode = 500;\n res.end('Internal Server Error');\n }\n } finally {\n requestSignal.dispose();\n }\n };\n};\n\nexport type { TNodeHandler };\n\nexport { handleRequest };\n\nexport default adapterNode;\n"],"names":["handleRequest","async","handler","request","res","compression","response","compressResponse","onEarlyHints","headers","writeEarlyHints","writeFetchResponse","adapterNode","options","req","next","requestSignal","createRequestSignal","createRequest","signal","error","aborted","headersSent","destroy","statusCode","end","dispose"],"mappings":"yNAmBA,MAAMA,EAAgBC,MACpBC,EACAC,EACAC,GACEC,eAAc,GAA+B,MAE/C,MAAMC,EAAWC,EACfJ,QACMD,EAAQC,EAAS,CACrBK,aAAeC,GAAYC,EAAgBN,EAAKK,KAElDJ,SAGIM,EAAmBP,EAAKE,EAAS,EAMnCM,EAAc,CAACV,EAAsBW,EAA+B,KAIjEZ,MAAOa,EAAKV,EAAKW,KACtB,MAAMC,EAAgBC,EAAoBH,EAAKV,GAE/C,UACQJ,EACJE,EACAgB,EAAcJ,EAAK,CAAEK,OAAQH,EAAcG,SAC3Cf,EACAS,EAEJ,CAAE,MAAOO,GACP,GAAIJ,EAAcG,OAAOE,QACvB,OAGEN,EACFA,EAAKK,GACIhB,EAAIkB,YACblB,EAAImB,QAAQH,IAEZhB,EAAIoB,WAAa,IACjBpB,EAAIqB,IAAI,yBAEZ,SACET,EAAcU,SAChB"}
|
package/cli/run-dev.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{performance as o}from"node:perf_hooks";import r from"../
|
|
1
|
+
import{performance as o}from"node:perf_hooks";import r from"../adapters/express/server.js";import{setCurrentEntrypointName as t}from"../plugins/handle-custom-entrypoint.js";import e from"../services/server-config.js";async function s({version:s,isHost:i,isPrintInfo:n,mode:m,port:p,entrypointName:a}){global.viteBoostStartTime=o.now(),a&&t(a);const f=e.init({isHost:i,mode:m,entrypointName:a},{port:p}),{run:c}=await r(f);return{server:c({version:s,isPrintInfo:n}),config:f}}export{s as default};
|
|
2
2
|
//# sourceMappingURL=run-dev.js.map
|
package/cli/run-dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-dev.js","sources":["../../src/cli/run-dev.ts"],"sourcesContent":["import type { Server } from 'node:net';\nimport { performance } from 'node:perf_hooks';\nimport createServer from '@
|
|
1
|
+
{"version":3,"file":"run-dev.js","sources":["../../src/cli/run-dev.ts"],"sourcesContent":["import type { Server } from 'node:net';\nimport { performance } from 'node:perf_hooks';\nimport createServer from '@adapters/express/server';\nimport { setCurrentEntrypointName } from '@plugins/handle-custom-entrypoint';\nimport ServerConfig from '@services/server-config';\n\ninterface IRunDevParams {\n version: string;\n isHost?: boolean;\n isPrintInfo?: boolean;\n mode?: string;\n port?: number;\n entrypointName?: string;\n}\n\ninterface IRunDevOut {\n server: Server;\n config: ServerConfig;\n}\n\n/**\n * Run development server\n */\nasync function runDev({\n version,\n isHost,\n isPrintInfo,\n mode,\n port,\n entrypointName,\n}: IRunDevParams): Promise<IRunDevOut> {\n global.viteBoostStartTime = performance.now();\n\n if (entrypointName) {\n setCurrentEntrypointName(entrypointName);\n }\n\n const config = ServerConfig.init(\n { isHost, mode, entrypointName },\n {\n port,\n },\n );\n const { run } = await createServer(config);\n\n return {\n server: run({ version, isPrintInfo }),\n config,\n };\n}\n\nexport default runDev;\n"],"names":["async","runDev","version","isHost","isPrintInfo","mode","port","entrypointName","global","viteBoostStartTime","performance","now","setCurrentEntrypointName","config","ServerConfig","init","run","createServer","server"],"mappings":"yNAuBAA,eAAeC,GAAOC,QACpBA,EAAOC,OACPA,EAAMC,YACNA,EAAWC,KACXA,EAAIC,KACJA,EAAIC,eACJA,IAEAC,OAAOC,mBAAqBC,EAAYC,MAEpCJ,GACFK,EAAyBL,GAG3B,MAAMM,EAASC,EAAaC,KAC1B,CAAEZ,SAAQE,OAAME,kBAChB,CACED,UAGEU,IAAEA,SAAcC,EAAaJ,GAEnC,MAAO,CACLK,OAAQF,EAAI,CAAEd,UAASE,gBACvBS,SAEJ"}
|