@nestjs-ssr/react 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{index-Dq2qZSge.d.ts → index-CSvZfKpi.d.ts} +76 -8
- package/dist/{index-CiYcz-1T.d.mts → index-ZpkYrPcK.d.mts} +76 -8
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +83 -31
- package/dist/index.mjs +83 -31
- package/dist/render/index.d.mts +2 -2
- package/dist/render/index.d.ts +2 -2
- package/dist/render/index.js +83 -31
- package/dist/render/index.mjs +83 -31
- package/package.json +12 -1
|
@@ -1,12 +1,79 @@
|
|
|
1
1
|
import { DynamicModule, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
|
2
2
|
import { ComponentType } from 'react';
|
|
3
|
-
import { Request, Response } from 'express';
|
|
4
3
|
import { H as HeadData, R as RenderContext } from './render-response.interface-ClWJXKL4.js';
|
|
4
|
+
import { ServerResponse } from 'http';
|
|
5
5
|
import { ViteDevServer } from 'vite';
|
|
6
6
|
import { Reflector } from '@nestjs/core';
|
|
7
7
|
import { Observable } from 'rxjs';
|
|
8
8
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Common HTTP request interface that works with both Express and Fastify.
|
|
12
|
+
* This represents the minimal interface needed for SSR context building.
|
|
13
|
+
*/
|
|
14
|
+
interface SSRRequest {
|
|
15
|
+
/** Full request URL including query string */
|
|
16
|
+
url: string;
|
|
17
|
+
/** HTTP method (GET, POST, etc.) */
|
|
18
|
+
method: string;
|
|
19
|
+
/** Request headers */
|
|
20
|
+
headers: Record<string, string | string[] | undefined>;
|
|
21
|
+
/** URL path (Express: path, Fastify: routeOptions.url or url without query) */
|
|
22
|
+
path?: string;
|
|
23
|
+
/** Parsed query parameters */
|
|
24
|
+
query?: Record<string, string | string[] | undefined>;
|
|
25
|
+
/** Route parameters */
|
|
26
|
+
params?: Record<string, string>;
|
|
27
|
+
/** Parsed cookies (requires cookie-parser middleware) */
|
|
28
|
+
cookies?: Record<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* User object populated by authentication middleware (e.g., Passport).
|
|
31
|
+
* Type is `unknown` since the shape depends on your auth strategy.
|
|
32
|
+
*/
|
|
33
|
+
user?: unknown;
|
|
34
|
+
/** Allow any additional properties for framework-specific extensions */
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Minimal interface for the raw Node.js response.
|
|
39
|
+
* This is a subset of ServerResponse that we actually use for streaming SSR.
|
|
40
|
+
*/
|
|
41
|
+
interface RawServerResponse {
|
|
42
|
+
statusCode: number;
|
|
43
|
+
headersSent: boolean;
|
|
44
|
+
writableEnded: boolean;
|
|
45
|
+
setHeader(name: string, value: string | number | readonly string[]): void;
|
|
46
|
+
write(chunk: string | Buffer): boolean;
|
|
47
|
+
end(data?: string | Buffer): void;
|
|
48
|
+
on?(event: string, listener: (...args: any[]) => void): this;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Common HTTP response interface that works with both Express and Fastify.
|
|
52
|
+
* For streaming SSR, we access the raw Node.js ServerResponse.
|
|
53
|
+
*/
|
|
54
|
+
interface SSRResponse {
|
|
55
|
+
/** HTTP status code (optional - Fastify has it on raw) */
|
|
56
|
+
statusCode?: number;
|
|
57
|
+
/** Whether headers have been sent (Express) */
|
|
58
|
+
headersSent?: boolean;
|
|
59
|
+
/** Whether headers have been sent (Fastify uses 'sent') */
|
|
60
|
+
sent?: boolean;
|
|
61
|
+
/** Whether the response stream has ended */
|
|
62
|
+
writableEnded?: boolean;
|
|
63
|
+
/** Set a response header */
|
|
64
|
+
setHeader?(name: string, value: string | number | readonly string[]): void;
|
|
65
|
+
/** Write data to the response */
|
|
66
|
+
write?(chunk: string | Buffer): boolean;
|
|
67
|
+
/** End the response */
|
|
68
|
+
end?(data?: string | Buffer): void;
|
|
69
|
+
/** Event listener for 'close' event */
|
|
70
|
+
on?(event: string, listener: (...args: any[]) => void): this;
|
|
71
|
+
/** Raw Node.js response (Fastify) - uses minimal interface for easier testing */
|
|
72
|
+
raw?: RawServerResponse | ServerResponse;
|
|
73
|
+
/** Allow additional properties */
|
|
74
|
+
[key: string]: unknown;
|
|
75
|
+
}
|
|
76
|
+
|
|
10
77
|
/**
|
|
11
78
|
* Custom context properties that can be added via context factory.
|
|
12
79
|
* Allows any properties to be merged into RenderContext.
|
|
@@ -16,7 +83,7 @@ type CustomContextProperties = Record<string, unknown>;
|
|
|
16
83
|
* Context factory function signature
|
|
17
84
|
* Called for each request to build custom context properties
|
|
18
85
|
*
|
|
19
|
-
* @param params - Object containing the Express
|
|
86
|
+
* @param params - Object containing the HTTP request (Express or Fastify)
|
|
20
87
|
* @returns Custom context properties to merge into RenderContext (sync or async)
|
|
21
88
|
*
|
|
22
89
|
* @example
|
|
@@ -37,8 +104,9 @@ type CustomContextProperties = Record<string, unknown>;
|
|
|
37
104
|
* })
|
|
38
105
|
* ```
|
|
39
106
|
*/
|
|
40
|
-
type ContextFactory = (params: {
|
|
41
|
-
|
|
107
|
+
type ContextFactory<TRequest extends SSRRequest = SSRRequest> = (params: {
|
|
108
|
+
/** HTTP request object (Express Request or Fastify FastifyRequest) */
|
|
109
|
+
req: TRequest;
|
|
42
110
|
}) => CustomContextProperties | Promise<CustomContextProperties>;
|
|
43
111
|
/**
|
|
44
112
|
* SSR rendering mode configuration
|
|
@@ -486,7 +554,7 @@ declare class StreamingErrorHandler {
|
|
|
486
554
|
* Handle error that occurred before shell was ready
|
|
487
555
|
* Can still set HTTP status code and send error page
|
|
488
556
|
*/
|
|
489
|
-
handleShellError(error: Error, res:
|
|
557
|
+
handleShellError(error: Error, res: SSRResponse, viewPath: string, isDevelopment: boolean): void;
|
|
490
558
|
/**
|
|
491
559
|
* Handle error that occurred during streaming
|
|
492
560
|
* Headers already sent, can only log the error
|
|
@@ -552,11 +620,11 @@ declare class StreamRenderer {
|
|
|
552
620
|
*
|
|
553
621
|
* @param viewComponent - The React component to render
|
|
554
622
|
* @param data - Data to pass to the component
|
|
555
|
-
* @param res -
|
|
623
|
+
* @param res - HTTP response object (Express or Fastify)
|
|
556
624
|
* @param context - Render context with Vite and manifest info
|
|
557
625
|
* @param head - Head data for SEO tags
|
|
558
626
|
*/
|
|
559
|
-
render(viewComponent: any, data: any, res:
|
|
627
|
+
render(viewComponent: any, data: any, res: SSRResponse, context: StreamRenderContext, head?: HeadData): Promise<void>;
|
|
560
628
|
}
|
|
561
629
|
|
|
562
630
|
/**
|
|
@@ -621,7 +689,7 @@ declare class RenderService {
|
|
|
621
689
|
* - Better TTFB, progressive rendering
|
|
622
690
|
* - Requires response object
|
|
623
691
|
*/
|
|
624
|
-
render(viewComponent: any, data?: any, res?:
|
|
692
|
+
render(viewComponent: any, data?: any, res?: SSRResponse, head?: HeadData): Promise<string | void>;
|
|
625
693
|
/**
|
|
626
694
|
* Render a segment for client-side navigation.
|
|
627
695
|
* Always uses string mode (streaming not supported for segments).
|
|
@@ -1,12 +1,79 @@
|
|
|
1
1
|
import { DynamicModule, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
|
2
2
|
import { ComponentType } from 'react';
|
|
3
|
-
import { Request, Response } from 'express';
|
|
4
3
|
import { H as HeadData, R as RenderContext } from './render-response.interface-ClWJXKL4.mjs';
|
|
4
|
+
import { ServerResponse } from 'http';
|
|
5
5
|
import { ViteDevServer } from 'vite';
|
|
6
6
|
import { Reflector } from '@nestjs/core';
|
|
7
7
|
import { Observable } from 'rxjs';
|
|
8
8
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Common HTTP request interface that works with both Express and Fastify.
|
|
12
|
+
* This represents the minimal interface needed for SSR context building.
|
|
13
|
+
*/
|
|
14
|
+
interface SSRRequest {
|
|
15
|
+
/** Full request URL including query string */
|
|
16
|
+
url: string;
|
|
17
|
+
/** HTTP method (GET, POST, etc.) */
|
|
18
|
+
method: string;
|
|
19
|
+
/** Request headers */
|
|
20
|
+
headers: Record<string, string | string[] | undefined>;
|
|
21
|
+
/** URL path (Express: path, Fastify: routeOptions.url or url without query) */
|
|
22
|
+
path?: string;
|
|
23
|
+
/** Parsed query parameters */
|
|
24
|
+
query?: Record<string, string | string[] | undefined>;
|
|
25
|
+
/** Route parameters */
|
|
26
|
+
params?: Record<string, string>;
|
|
27
|
+
/** Parsed cookies (requires cookie-parser middleware) */
|
|
28
|
+
cookies?: Record<string, string>;
|
|
29
|
+
/**
|
|
30
|
+
* User object populated by authentication middleware (e.g., Passport).
|
|
31
|
+
* Type is `unknown` since the shape depends on your auth strategy.
|
|
32
|
+
*/
|
|
33
|
+
user?: unknown;
|
|
34
|
+
/** Allow any additional properties for framework-specific extensions */
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Minimal interface for the raw Node.js response.
|
|
39
|
+
* This is a subset of ServerResponse that we actually use for streaming SSR.
|
|
40
|
+
*/
|
|
41
|
+
interface RawServerResponse {
|
|
42
|
+
statusCode: number;
|
|
43
|
+
headersSent: boolean;
|
|
44
|
+
writableEnded: boolean;
|
|
45
|
+
setHeader(name: string, value: string | number | readonly string[]): void;
|
|
46
|
+
write(chunk: string | Buffer): boolean;
|
|
47
|
+
end(data?: string | Buffer): void;
|
|
48
|
+
on?(event: string, listener: (...args: any[]) => void): this;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Common HTTP response interface that works with both Express and Fastify.
|
|
52
|
+
* For streaming SSR, we access the raw Node.js ServerResponse.
|
|
53
|
+
*/
|
|
54
|
+
interface SSRResponse {
|
|
55
|
+
/** HTTP status code (optional - Fastify has it on raw) */
|
|
56
|
+
statusCode?: number;
|
|
57
|
+
/** Whether headers have been sent (Express) */
|
|
58
|
+
headersSent?: boolean;
|
|
59
|
+
/** Whether headers have been sent (Fastify uses 'sent') */
|
|
60
|
+
sent?: boolean;
|
|
61
|
+
/** Whether the response stream has ended */
|
|
62
|
+
writableEnded?: boolean;
|
|
63
|
+
/** Set a response header */
|
|
64
|
+
setHeader?(name: string, value: string | number | readonly string[]): void;
|
|
65
|
+
/** Write data to the response */
|
|
66
|
+
write?(chunk: string | Buffer): boolean;
|
|
67
|
+
/** End the response */
|
|
68
|
+
end?(data?: string | Buffer): void;
|
|
69
|
+
/** Event listener for 'close' event */
|
|
70
|
+
on?(event: string, listener: (...args: any[]) => void): this;
|
|
71
|
+
/** Raw Node.js response (Fastify) - uses minimal interface for easier testing */
|
|
72
|
+
raw?: RawServerResponse | ServerResponse;
|
|
73
|
+
/** Allow additional properties */
|
|
74
|
+
[key: string]: unknown;
|
|
75
|
+
}
|
|
76
|
+
|
|
10
77
|
/**
|
|
11
78
|
* Custom context properties that can be added via context factory.
|
|
12
79
|
* Allows any properties to be merged into RenderContext.
|
|
@@ -16,7 +83,7 @@ type CustomContextProperties = Record<string, unknown>;
|
|
|
16
83
|
* Context factory function signature
|
|
17
84
|
* Called for each request to build custom context properties
|
|
18
85
|
*
|
|
19
|
-
* @param params - Object containing the Express
|
|
86
|
+
* @param params - Object containing the HTTP request (Express or Fastify)
|
|
20
87
|
* @returns Custom context properties to merge into RenderContext (sync or async)
|
|
21
88
|
*
|
|
22
89
|
* @example
|
|
@@ -37,8 +104,9 @@ type CustomContextProperties = Record<string, unknown>;
|
|
|
37
104
|
* })
|
|
38
105
|
* ```
|
|
39
106
|
*/
|
|
40
|
-
type ContextFactory = (params: {
|
|
41
|
-
|
|
107
|
+
type ContextFactory<TRequest extends SSRRequest = SSRRequest> = (params: {
|
|
108
|
+
/** HTTP request object (Express Request or Fastify FastifyRequest) */
|
|
109
|
+
req: TRequest;
|
|
42
110
|
}) => CustomContextProperties | Promise<CustomContextProperties>;
|
|
43
111
|
/**
|
|
44
112
|
* SSR rendering mode configuration
|
|
@@ -486,7 +554,7 @@ declare class StreamingErrorHandler {
|
|
|
486
554
|
* Handle error that occurred before shell was ready
|
|
487
555
|
* Can still set HTTP status code and send error page
|
|
488
556
|
*/
|
|
489
|
-
handleShellError(error: Error, res:
|
|
557
|
+
handleShellError(error: Error, res: SSRResponse, viewPath: string, isDevelopment: boolean): void;
|
|
490
558
|
/**
|
|
491
559
|
* Handle error that occurred during streaming
|
|
492
560
|
* Headers already sent, can only log the error
|
|
@@ -552,11 +620,11 @@ declare class StreamRenderer {
|
|
|
552
620
|
*
|
|
553
621
|
* @param viewComponent - The React component to render
|
|
554
622
|
* @param data - Data to pass to the component
|
|
555
|
-
* @param res -
|
|
623
|
+
* @param res - HTTP response object (Express or Fastify)
|
|
556
624
|
* @param context - Render context with Vite and manifest info
|
|
557
625
|
* @param head - Head data for SEO tags
|
|
558
626
|
*/
|
|
559
|
-
render(viewComponent: any, data: any, res:
|
|
627
|
+
render(viewComponent: any, data: any, res: SSRResponse, context: StreamRenderContext, head?: HeadData): Promise<void>;
|
|
560
628
|
}
|
|
561
629
|
|
|
562
630
|
/**
|
|
@@ -621,7 +689,7 @@ declare class RenderService {
|
|
|
621
689
|
* - Better TTFB, progressive rendering
|
|
622
690
|
* - Requires response object
|
|
623
691
|
*/
|
|
624
|
-
render(viewComponent: any, data?: any, res?:
|
|
692
|
+
render(viewComponent: any, data?: any, res?: SSRResponse, head?: HeadData): Promise<string | void>;
|
|
625
693
|
/**
|
|
626
694
|
* Render a segment for client-side navigation.
|
|
627
695
|
* Always uses string mode (streaming not supported for segments).
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export { C as ContextFactory, E as ErrorPageDevelopment, e as ErrorPageProduction, c as RenderConfig, b as RenderInterceptor, R as RenderModule, a as RenderService, d as SSRMode, S as StreamingErrorHandler, T as TemplateParserService } from './index-
|
|
1
|
+
export { C as ContextFactory, E as ErrorPageDevelopment, e as ErrorPageProduction, c as RenderConfig, b as RenderInterceptor, R as RenderModule, a as RenderService, d as SSRMode, S as StreamingErrorHandler, T as TemplateParserService } from './index-ZpkYrPcK.mjs';
|
|
2
2
|
import React, { ComponentType, ReactNode } from 'react';
|
|
3
3
|
import { P as PageProps } from './use-page-context-CVC9DHcL.mjs';
|
|
4
4
|
export { a as PageContextProvider, c as createSSRHooks, i as useCookie, h as useCookies, g as useHeader, f as useHeaders, u as usePageContext, b as useParams, d as useQuery, e as useRequest } from './use-page-context-CVC9DHcL.mjs';
|
|
5
5
|
import { R as RenderContext, H as HeadData, a as RenderResponse } from './render-response.interface-ClWJXKL4.mjs';
|
|
6
6
|
import '@nestjs/common';
|
|
7
|
-
import '
|
|
7
|
+
import 'http';
|
|
8
8
|
import 'vite';
|
|
9
9
|
import '@nestjs/core';
|
|
10
10
|
import 'rxjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export { C as ContextFactory, E as ErrorPageDevelopment, e as ErrorPageProduction, c as RenderConfig, b as RenderInterceptor, R as RenderModule, a as RenderService, d as SSRMode, S as StreamingErrorHandler, T as TemplateParserService } from './index-
|
|
1
|
+
export { C as ContextFactory, E as ErrorPageDevelopment, e as ErrorPageProduction, c as RenderConfig, b as RenderInterceptor, R as RenderModule, a as RenderService, d as SSRMode, S as StreamingErrorHandler, T as TemplateParserService } from './index-CSvZfKpi.js';
|
|
2
2
|
import React, { ComponentType, ReactNode } from 'react';
|
|
3
3
|
import { P as PageProps } from './use-page-context-DChgHhL9.js';
|
|
4
4
|
export { a as PageContextProvider, c as createSSRHooks, i as useCookie, h as useCookies, g as useHeader, f as useHeaders, u as usePageContext, b as useParams, d as useQuery, e as useRequest } from './use-page-context-DChgHhL9.js';
|
|
5
5
|
import { R as RenderContext, H as HeadData, a as RenderResponse } from './render-response.interface-ClWJXKL4.js';
|
|
6
6
|
import '@nestjs/common';
|
|
7
|
-
import '
|
|
7
|
+
import 'http';
|
|
8
8
|
import 'vite';
|
|
9
9
|
import '@nestjs/core';
|
|
10
10
|
import 'rxjs';
|
package/dist/index.js
CHANGED
|
@@ -462,6 +462,39 @@ function ErrorPageProduction() {
|
|
|
462
462
|
}
|
|
463
463
|
__name(ErrorPageProduction, "ErrorPageProduction");
|
|
464
464
|
|
|
465
|
+
// src/render/adapters/http-adapter-utils.ts
|
|
466
|
+
function detectAdapterType(httpAdapterHost) {
|
|
467
|
+
const adapter = httpAdapterHost?.httpAdapter;
|
|
468
|
+
if (!adapter) return "unknown";
|
|
469
|
+
const instance = adapter.getInstance();
|
|
470
|
+
if (instance && typeof instance.register === "function") {
|
|
471
|
+
return "fastify";
|
|
472
|
+
}
|
|
473
|
+
if (instance && typeof instance.use === "function" && typeof instance.get === "function") {
|
|
474
|
+
return "express";
|
|
475
|
+
}
|
|
476
|
+
return "unknown";
|
|
477
|
+
}
|
|
478
|
+
__name(detectAdapterType, "detectAdapterType");
|
|
479
|
+
function isFastifyLikeResponse(res) {
|
|
480
|
+
return res != null && typeof res === "object" && "raw" in res && res.raw != null && typeof res.raw.write === "function";
|
|
481
|
+
}
|
|
482
|
+
__name(isFastifyLikeResponse, "isFastifyLikeResponse");
|
|
483
|
+
function getRawResponse(res) {
|
|
484
|
+
if (isFastifyLikeResponse(res)) {
|
|
485
|
+
return res.raw;
|
|
486
|
+
}
|
|
487
|
+
return res;
|
|
488
|
+
}
|
|
489
|
+
__name(getRawResponse, "getRawResponse");
|
|
490
|
+
function isHeadersSent(res) {
|
|
491
|
+
if (typeof res.sent === "boolean") {
|
|
492
|
+
return res.sent;
|
|
493
|
+
}
|
|
494
|
+
return res.headersSent === true;
|
|
495
|
+
}
|
|
496
|
+
__name(isHeadersSent, "isHeadersSent");
|
|
497
|
+
|
|
465
498
|
// src/render/streaming-error-handler.ts
|
|
466
499
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
467
500
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -497,21 +530,19 @@ exports.StreamingErrorHandler = class _StreamingErrorHandler {
|
|
|
497
530
|
*/
|
|
498
531
|
handleShellError(error, res, viewPath, isDevelopment) {
|
|
499
532
|
this.logger.error(`Shell error rendering ${viewPath}: ${error.message}`, error.stack);
|
|
500
|
-
|
|
533
|
+
const rawRes = getRawResponse(res);
|
|
534
|
+
if (isHeadersSent(res)) {
|
|
501
535
|
this.logger.error(`Cannot send error page for ${viewPath} - headers already sent (streaming started)`);
|
|
502
|
-
if (!
|
|
503
|
-
|
|
504
|
-
|
|
536
|
+
if (!rawRes.writableEnded) {
|
|
537
|
+
rawRes.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
|
|
538
|
+
rawRes.end();
|
|
505
539
|
}
|
|
506
540
|
return;
|
|
507
541
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
} else {
|
|
513
|
-
res.send(this.renderProductionErrorPage());
|
|
514
|
-
}
|
|
542
|
+
rawRes.statusCode = 500;
|
|
543
|
+
rawRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
544
|
+
const html = isDevelopment ? this.renderDevelopmentErrorPage(error, viewPath, "shell") : this.renderProductionErrorPage();
|
|
545
|
+
rawRes.end(html);
|
|
515
546
|
}
|
|
516
547
|
/**
|
|
517
548
|
* Handle error that occurred during streaming
|
|
@@ -667,7 +698,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
667
698
|
*
|
|
668
699
|
* @param viewComponent - The React component to render
|
|
669
700
|
* @param data - Data to pass to the component
|
|
670
|
-
* @param res -
|
|
701
|
+
* @param res - HTTP response object (Express or Fastify)
|
|
671
702
|
* @param context - Render context with Vite and manifest info
|
|
672
703
|
* @param head - Head data for SEO tags
|
|
673
704
|
*/
|
|
@@ -709,20 +740,21 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
709
740
|
const { PassThrough } = await import('stream');
|
|
710
741
|
const reactStream = new PassThrough();
|
|
711
742
|
let allReadyFired = false;
|
|
743
|
+
const rawRes = getRawResponse(res);
|
|
712
744
|
const { pipe, abort } = renderModule.renderComponentStream(viewComponent, data, {
|
|
713
745
|
onShellReady: /* @__PURE__ */ __name(() => {
|
|
714
746
|
shellReadyTime = Date.now();
|
|
715
|
-
if (!
|
|
716
|
-
|
|
717
|
-
|
|
747
|
+
if (!rawRes.headersSent) {
|
|
748
|
+
rawRes.statusCode = didError ? 500 : 200;
|
|
749
|
+
rawRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
718
750
|
}
|
|
719
751
|
let htmlStart = templateParts.htmlStart;
|
|
720
752
|
htmlStart = htmlStart.replace("<!--styles-->", stylesheetTags);
|
|
721
753
|
htmlStart = htmlStart.replace("<!--head-meta-->", headTags);
|
|
722
|
-
|
|
723
|
-
|
|
754
|
+
rawRes.write(htmlStart);
|
|
755
|
+
rawRes.write(templateParts.rootStart);
|
|
724
756
|
pipe(reactStream);
|
|
725
|
-
reactStream.pipe(
|
|
757
|
+
reactStream.pipe(rawRes, {
|
|
726
758
|
end: false
|
|
727
759
|
});
|
|
728
760
|
if (context.isDevelopment) {
|
|
@@ -747,11 +779,11 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
747
779
|
if (shellErrorOccurred) {
|
|
748
780
|
return;
|
|
749
781
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
782
|
+
rawRes.write(inlineScripts);
|
|
783
|
+
rawRes.write(clientScript);
|
|
784
|
+
rawRes.write(templateParts.rootEnd);
|
|
785
|
+
rawRes.write(templateParts.htmlEnd);
|
|
786
|
+
rawRes.end();
|
|
755
787
|
if (context.isDevelopment) {
|
|
756
788
|
const totalTime = Date.now() - startTime;
|
|
757
789
|
const streamTime = Date.now() - shellReadyTime;
|
|
@@ -763,7 +795,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
763
795
|
reactStream.on("error", (error) => {
|
|
764
796
|
reject(error);
|
|
765
797
|
});
|
|
766
|
-
|
|
798
|
+
rawRes.on("close", () => {
|
|
767
799
|
abort();
|
|
768
800
|
resolve();
|
|
769
801
|
});
|
|
@@ -1361,7 +1393,7 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1361
1393
|
if (isDevelopment) {
|
|
1362
1394
|
await this.setupDevelopmentMode();
|
|
1363
1395
|
} else {
|
|
1364
|
-
this.setupProductionMode();
|
|
1396
|
+
await this.setupProductionMode();
|
|
1365
1397
|
}
|
|
1366
1398
|
}
|
|
1367
1399
|
async setupDevelopmentMode() {
|
|
@@ -1403,18 +1435,38 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1403
1435
|
this.logger.warn(`Failed to setup Vite proxy: ${error.message}. Make sure http-proxy-middleware is installed.`);
|
|
1404
1436
|
}
|
|
1405
1437
|
}
|
|
1406
|
-
setupProductionMode() {
|
|
1438
|
+
async setupProductionMode() {
|
|
1407
1439
|
try {
|
|
1408
1440
|
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
1409
|
-
if (httpAdapter)
|
|
1410
|
-
|
|
1411
|
-
|
|
1441
|
+
if (!httpAdapter) return;
|
|
1442
|
+
const app = httpAdapter.getInstance();
|
|
1443
|
+
const { join: join2 } = __require("path");
|
|
1444
|
+
const staticPath = join2(process.cwd(), "dist/client");
|
|
1445
|
+
const adapterType = detectAdapterType(this.httpAdapterHost);
|
|
1446
|
+
if (adapterType === "fastify") {
|
|
1447
|
+
try {
|
|
1448
|
+
const fastifyStatic = await import('@fastify/static').catch(() => null);
|
|
1449
|
+
if (fastifyStatic) {
|
|
1450
|
+
await app.register(fastifyStatic.default, {
|
|
1451
|
+
root: staticPath,
|
|
1452
|
+
prefix: "/",
|
|
1453
|
+
index: false,
|
|
1454
|
+
maxAge: 31536e6
|
|
1455
|
+
});
|
|
1456
|
+
this.logger.log("\u2713 Static assets configured (dist/client) [Fastify]");
|
|
1457
|
+
} else {
|
|
1458
|
+
this.logger.warn("For Fastify static file serving, install @fastify/static: npm install @fastify/static");
|
|
1459
|
+
}
|
|
1460
|
+
} catch {
|
|
1461
|
+
this.logger.warn("For Fastify static file serving, install @fastify/static: npm install @fastify/static");
|
|
1462
|
+
}
|
|
1463
|
+
} else {
|
|
1412
1464
|
const express = __require("express");
|
|
1413
|
-
app.use(express.static(
|
|
1465
|
+
app.use(express.static(staticPath, {
|
|
1414
1466
|
index: false,
|
|
1415
1467
|
maxAge: "1y"
|
|
1416
1468
|
}));
|
|
1417
|
-
this.logger.log("\u2713 Static assets configured (dist/client)");
|
|
1469
|
+
this.logger.log("\u2713 Static assets configured (dist/client) [Express]");
|
|
1418
1470
|
}
|
|
1419
1471
|
} catch (error) {
|
|
1420
1472
|
this.logger.warn(`Failed to setup static assets: ${error.message}`);
|
package/dist/index.mjs
CHANGED
|
@@ -455,6 +455,39 @@ function ErrorPageProduction() {
|
|
|
455
455
|
}
|
|
456
456
|
__name(ErrorPageProduction, "ErrorPageProduction");
|
|
457
457
|
|
|
458
|
+
// src/render/adapters/http-adapter-utils.ts
|
|
459
|
+
function detectAdapterType(httpAdapterHost) {
|
|
460
|
+
const adapter = httpAdapterHost?.httpAdapter;
|
|
461
|
+
if (!adapter) return "unknown";
|
|
462
|
+
const instance = adapter.getInstance();
|
|
463
|
+
if (instance && typeof instance.register === "function") {
|
|
464
|
+
return "fastify";
|
|
465
|
+
}
|
|
466
|
+
if (instance && typeof instance.use === "function" && typeof instance.get === "function") {
|
|
467
|
+
return "express";
|
|
468
|
+
}
|
|
469
|
+
return "unknown";
|
|
470
|
+
}
|
|
471
|
+
__name(detectAdapterType, "detectAdapterType");
|
|
472
|
+
function isFastifyLikeResponse(res) {
|
|
473
|
+
return res != null && typeof res === "object" && "raw" in res && res.raw != null && typeof res.raw.write === "function";
|
|
474
|
+
}
|
|
475
|
+
__name(isFastifyLikeResponse, "isFastifyLikeResponse");
|
|
476
|
+
function getRawResponse(res) {
|
|
477
|
+
if (isFastifyLikeResponse(res)) {
|
|
478
|
+
return res.raw;
|
|
479
|
+
}
|
|
480
|
+
return res;
|
|
481
|
+
}
|
|
482
|
+
__name(getRawResponse, "getRawResponse");
|
|
483
|
+
function isHeadersSent(res) {
|
|
484
|
+
if (typeof res.sent === "boolean") {
|
|
485
|
+
return res.sent;
|
|
486
|
+
}
|
|
487
|
+
return res.headersSent === true;
|
|
488
|
+
}
|
|
489
|
+
__name(isHeadersSent, "isHeadersSent");
|
|
490
|
+
|
|
458
491
|
// src/render/streaming-error-handler.ts
|
|
459
492
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
460
493
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -490,21 +523,19 @@ var StreamingErrorHandler = class _StreamingErrorHandler {
|
|
|
490
523
|
*/
|
|
491
524
|
handleShellError(error, res, viewPath, isDevelopment) {
|
|
492
525
|
this.logger.error(`Shell error rendering ${viewPath}: ${error.message}`, error.stack);
|
|
493
|
-
|
|
526
|
+
const rawRes = getRawResponse(res);
|
|
527
|
+
if (isHeadersSent(res)) {
|
|
494
528
|
this.logger.error(`Cannot send error page for ${viewPath} - headers already sent (streaming started)`);
|
|
495
|
-
if (!
|
|
496
|
-
|
|
497
|
-
|
|
529
|
+
if (!rawRes.writableEnded) {
|
|
530
|
+
rawRes.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
|
|
531
|
+
rawRes.end();
|
|
498
532
|
}
|
|
499
533
|
return;
|
|
500
534
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
} else {
|
|
506
|
-
res.send(this.renderProductionErrorPage());
|
|
507
|
-
}
|
|
535
|
+
rawRes.statusCode = 500;
|
|
536
|
+
rawRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
537
|
+
const html = isDevelopment ? this.renderDevelopmentErrorPage(error, viewPath, "shell") : this.renderProductionErrorPage();
|
|
538
|
+
rawRes.end(html);
|
|
508
539
|
}
|
|
509
540
|
/**
|
|
510
541
|
* Handle error that occurred during streaming
|
|
@@ -660,7 +691,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
660
691
|
*
|
|
661
692
|
* @param viewComponent - The React component to render
|
|
662
693
|
* @param data - Data to pass to the component
|
|
663
|
-
* @param res -
|
|
694
|
+
* @param res - HTTP response object (Express or Fastify)
|
|
664
695
|
* @param context - Render context with Vite and manifest info
|
|
665
696
|
* @param head - Head data for SEO tags
|
|
666
697
|
*/
|
|
@@ -702,20 +733,21 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
702
733
|
const { PassThrough } = await import('stream');
|
|
703
734
|
const reactStream = new PassThrough();
|
|
704
735
|
let allReadyFired = false;
|
|
736
|
+
const rawRes = getRawResponse(res);
|
|
705
737
|
const { pipe, abort } = renderModule.renderComponentStream(viewComponent, data, {
|
|
706
738
|
onShellReady: /* @__PURE__ */ __name(() => {
|
|
707
739
|
shellReadyTime = Date.now();
|
|
708
|
-
if (!
|
|
709
|
-
|
|
710
|
-
|
|
740
|
+
if (!rawRes.headersSent) {
|
|
741
|
+
rawRes.statusCode = didError ? 500 : 200;
|
|
742
|
+
rawRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
711
743
|
}
|
|
712
744
|
let htmlStart = templateParts.htmlStart;
|
|
713
745
|
htmlStart = htmlStart.replace("<!--styles-->", stylesheetTags);
|
|
714
746
|
htmlStart = htmlStart.replace("<!--head-meta-->", headTags);
|
|
715
|
-
|
|
716
|
-
|
|
747
|
+
rawRes.write(htmlStart);
|
|
748
|
+
rawRes.write(templateParts.rootStart);
|
|
717
749
|
pipe(reactStream);
|
|
718
|
-
reactStream.pipe(
|
|
750
|
+
reactStream.pipe(rawRes, {
|
|
719
751
|
end: false
|
|
720
752
|
});
|
|
721
753
|
if (context.isDevelopment) {
|
|
@@ -740,11 +772,11 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
740
772
|
if (shellErrorOccurred) {
|
|
741
773
|
return;
|
|
742
774
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
775
|
+
rawRes.write(inlineScripts);
|
|
776
|
+
rawRes.write(clientScript);
|
|
777
|
+
rawRes.write(templateParts.rootEnd);
|
|
778
|
+
rawRes.write(templateParts.htmlEnd);
|
|
779
|
+
rawRes.end();
|
|
748
780
|
if (context.isDevelopment) {
|
|
749
781
|
const totalTime = Date.now() - startTime;
|
|
750
782
|
const streamTime = Date.now() - shellReadyTime;
|
|
@@ -756,7 +788,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
756
788
|
reactStream.on("error", (error) => {
|
|
757
789
|
reject(error);
|
|
758
790
|
});
|
|
759
|
-
|
|
791
|
+
rawRes.on("close", () => {
|
|
760
792
|
abort();
|
|
761
793
|
resolve();
|
|
762
794
|
});
|
|
@@ -1354,7 +1386,7 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1354
1386
|
if (isDevelopment) {
|
|
1355
1387
|
await this.setupDevelopmentMode();
|
|
1356
1388
|
} else {
|
|
1357
|
-
this.setupProductionMode();
|
|
1389
|
+
await this.setupProductionMode();
|
|
1358
1390
|
}
|
|
1359
1391
|
}
|
|
1360
1392
|
async setupDevelopmentMode() {
|
|
@@ -1396,18 +1428,38 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1396
1428
|
this.logger.warn(`Failed to setup Vite proxy: ${error.message}. Make sure http-proxy-middleware is installed.`);
|
|
1397
1429
|
}
|
|
1398
1430
|
}
|
|
1399
|
-
setupProductionMode() {
|
|
1431
|
+
async setupProductionMode() {
|
|
1400
1432
|
try {
|
|
1401
1433
|
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
1402
|
-
if (httpAdapter)
|
|
1403
|
-
|
|
1404
|
-
|
|
1434
|
+
if (!httpAdapter) return;
|
|
1435
|
+
const app = httpAdapter.getInstance();
|
|
1436
|
+
const { join: join2 } = __require("path");
|
|
1437
|
+
const staticPath = join2(process.cwd(), "dist/client");
|
|
1438
|
+
const adapterType = detectAdapterType(this.httpAdapterHost);
|
|
1439
|
+
if (adapterType === "fastify") {
|
|
1440
|
+
try {
|
|
1441
|
+
const fastifyStatic = await import('@fastify/static').catch(() => null);
|
|
1442
|
+
if (fastifyStatic) {
|
|
1443
|
+
await app.register(fastifyStatic.default, {
|
|
1444
|
+
root: staticPath,
|
|
1445
|
+
prefix: "/",
|
|
1446
|
+
index: false,
|
|
1447
|
+
maxAge: 31536e6
|
|
1448
|
+
});
|
|
1449
|
+
this.logger.log("\u2713 Static assets configured (dist/client) [Fastify]");
|
|
1450
|
+
} else {
|
|
1451
|
+
this.logger.warn("For Fastify static file serving, install @fastify/static: npm install @fastify/static");
|
|
1452
|
+
}
|
|
1453
|
+
} catch {
|
|
1454
|
+
this.logger.warn("For Fastify static file serving, install @fastify/static: npm install @fastify/static");
|
|
1455
|
+
}
|
|
1456
|
+
} else {
|
|
1405
1457
|
const express = __require("express");
|
|
1406
|
-
app.use(express.static(
|
|
1458
|
+
app.use(express.static(staticPath, {
|
|
1407
1459
|
index: false,
|
|
1408
1460
|
maxAge: "1y"
|
|
1409
1461
|
}));
|
|
1410
|
-
this.logger.log("\u2713 Static assets configured (dist/client)");
|
|
1462
|
+
this.logger.log("\u2713 Static assets configured (dist/client) [Express]");
|
|
1411
1463
|
}
|
|
1412
1464
|
} catch (error) {
|
|
1413
1465
|
this.logger.warn(`Failed to setup static assets: ${error.message}`);
|
package/dist/render/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { E as ErrorPageDevelopment, e as ErrorPageProduction, b as RenderInterceptor, R as RenderModule, a as RenderService, S as StreamingErrorHandler, T as TemplateParserService } from '../index-
|
|
1
|
+
export { E as ErrorPageDevelopment, e as ErrorPageProduction, b as RenderInterceptor, R as RenderModule, a as RenderService, S as StreamingErrorHandler, T as TemplateParserService } from '../index-ZpkYrPcK.mjs';
|
|
2
2
|
import '@nestjs/common';
|
|
3
3
|
import 'react';
|
|
4
|
-
import 'express';
|
|
5
4
|
import '../render-response.interface-ClWJXKL4.mjs';
|
|
5
|
+
import 'http';
|
|
6
6
|
import 'vite';
|
|
7
7
|
import '@nestjs/core';
|
|
8
8
|
import 'rxjs';
|
package/dist/render/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { E as ErrorPageDevelopment, e as ErrorPageProduction, b as RenderInterceptor, R as RenderModule, a as RenderService, S as StreamingErrorHandler, T as TemplateParserService } from '../index-
|
|
1
|
+
export { E as ErrorPageDevelopment, e as ErrorPageProduction, b as RenderInterceptor, R as RenderModule, a as RenderService, S as StreamingErrorHandler, T as TemplateParserService } from '../index-CSvZfKpi.js';
|
|
2
2
|
import '@nestjs/common';
|
|
3
3
|
import 'react';
|
|
4
|
-
import 'express';
|
|
5
4
|
import '../render-response.interface-ClWJXKL4.js';
|
|
5
|
+
import 'http';
|
|
6
6
|
import 'vite';
|
|
7
7
|
import '@nestjs/core';
|
|
8
8
|
import 'rxjs';
|
package/dist/render/index.js
CHANGED
|
@@ -461,6 +461,39 @@ function ErrorPageProduction() {
|
|
|
461
461
|
}
|
|
462
462
|
__name(ErrorPageProduction, "ErrorPageProduction");
|
|
463
463
|
|
|
464
|
+
// src/render/adapters/http-adapter-utils.ts
|
|
465
|
+
function detectAdapterType(httpAdapterHost) {
|
|
466
|
+
const adapter = httpAdapterHost?.httpAdapter;
|
|
467
|
+
if (!adapter) return "unknown";
|
|
468
|
+
const instance = adapter.getInstance();
|
|
469
|
+
if (instance && typeof instance.register === "function") {
|
|
470
|
+
return "fastify";
|
|
471
|
+
}
|
|
472
|
+
if (instance && typeof instance.use === "function" && typeof instance.get === "function") {
|
|
473
|
+
return "express";
|
|
474
|
+
}
|
|
475
|
+
return "unknown";
|
|
476
|
+
}
|
|
477
|
+
__name(detectAdapterType, "detectAdapterType");
|
|
478
|
+
function isFastifyLikeResponse(res) {
|
|
479
|
+
return res != null && typeof res === "object" && "raw" in res && res.raw != null && typeof res.raw.write === "function";
|
|
480
|
+
}
|
|
481
|
+
__name(isFastifyLikeResponse, "isFastifyLikeResponse");
|
|
482
|
+
function getRawResponse(res) {
|
|
483
|
+
if (isFastifyLikeResponse(res)) {
|
|
484
|
+
return res.raw;
|
|
485
|
+
}
|
|
486
|
+
return res;
|
|
487
|
+
}
|
|
488
|
+
__name(getRawResponse, "getRawResponse");
|
|
489
|
+
function isHeadersSent(res) {
|
|
490
|
+
if (typeof res.sent === "boolean") {
|
|
491
|
+
return res.sent;
|
|
492
|
+
}
|
|
493
|
+
return res.headersSent === true;
|
|
494
|
+
}
|
|
495
|
+
__name(isHeadersSent, "isHeadersSent");
|
|
496
|
+
|
|
464
497
|
// src/render/streaming-error-handler.ts
|
|
465
498
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
466
499
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -496,21 +529,19 @@ exports.StreamingErrorHandler = class _StreamingErrorHandler {
|
|
|
496
529
|
*/
|
|
497
530
|
handleShellError(error, res, viewPath, isDevelopment) {
|
|
498
531
|
this.logger.error(`Shell error rendering ${viewPath}: ${error.message}`, error.stack);
|
|
499
|
-
|
|
532
|
+
const rawRes = getRawResponse(res);
|
|
533
|
+
if (isHeadersSent(res)) {
|
|
500
534
|
this.logger.error(`Cannot send error page for ${viewPath} - headers already sent (streaming started)`);
|
|
501
|
-
if (!
|
|
502
|
-
|
|
503
|
-
|
|
535
|
+
if (!rawRes.writableEnded) {
|
|
536
|
+
rawRes.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
|
|
537
|
+
rawRes.end();
|
|
504
538
|
}
|
|
505
539
|
return;
|
|
506
540
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
} else {
|
|
512
|
-
res.send(this.renderProductionErrorPage());
|
|
513
|
-
}
|
|
541
|
+
rawRes.statusCode = 500;
|
|
542
|
+
rawRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
543
|
+
const html = isDevelopment ? this.renderDevelopmentErrorPage(error, viewPath, "shell") : this.renderProductionErrorPage();
|
|
544
|
+
rawRes.end(html);
|
|
514
545
|
}
|
|
515
546
|
/**
|
|
516
547
|
* Handle error that occurred during streaming
|
|
@@ -666,7 +697,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
666
697
|
*
|
|
667
698
|
* @param viewComponent - The React component to render
|
|
668
699
|
* @param data - Data to pass to the component
|
|
669
|
-
* @param res -
|
|
700
|
+
* @param res - HTTP response object (Express or Fastify)
|
|
670
701
|
* @param context - Render context with Vite and manifest info
|
|
671
702
|
* @param head - Head data for SEO tags
|
|
672
703
|
*/
|
|
@@ -708,20 +739,21 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
708
739
|
const { PassThrough } = await import('stream');
|
|
709
740
|
const reactStream = new PassThrough();
|
|
710
741
|
let allReadyFired = false;
|
|
742
|
+
const rawRes = getRawResponse(res);
|
|
711
743
|
const { pipe, abort } = renderModule.renderComponentStream(viewComponent, data, {
|
|
712
744
|
onShellReady: /* @__PURE__ */ __name(() => {
|
|
713
745
|
shellReadyTime = Date.now();
|
|
714
|
-
if (!
|
|
715
|
-
|
|
716
|
-
|
|
746
|
+
if (!rawRes.headersSent) {
|
|
747
|
+
rawRes.statusCode = didError ? 500 : 200;
|
|
748
|
+
rawRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
717
749
|
}
|
|
718
750
|
let htmlStart = templateParts.htmlStart;
|
|
719
751
|
htmlStart = htmlStart.replace("<!--styles-->", stylesheetTags);
|
|
720
752
|
htmlStart = htmlStart.replace("<!--head-meta-->", headTags);
|
|
721
|
-
|
|
722
|
-
|
|
753
|
+
rawRes.write(htmlStart);
|
|
754
|
+
rawRes.write(templateParts.rootStart);
|
|
723
755
|
pipe(reactStream);
|
|
724
|
-
reactStream.pipe(
|
|
756
|
+
reactStream.pipe(rawRes, {
|
|
725
757
|
end: false
|
|
726
758
|
});
|
|
727
759
|
if (context.isDevelopment) {
|
|
@@ -746,11 +778,11 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
746
778
|
if (shellErrorOccurred) {
|
|
747
779
|
return;
|
|
748
780
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
781
|
+
rawRes.write(inlineScripts);
|
|
782
|
+
rawRes.write(clientScript);
|
|
783
|
+
rawRes.write(templateParts.rootEnd);
|
|
784
|
+
rawRes.write(templateParts.htmlEnd);
|
|
785
|
+
rawRes.end();
|
|
754
786
|
if (context.isDevelopment) {
|
|
755
787
|
const totalTime = Date.now() - startTime;
|
|
756
788
|
const streamTime = Date.now() - shellReadyTime;
|
|
@@ -762,7 +794,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
762
794
|
reactStream.on("error", (error) => {
|
|
763
795
|
reject(error);
|
|
764
796
|
});
|
|
765
|
-
|
|
797
|
+
rawRes.on("close", () => {
|
|
766
798
|
abort();
|
|
767
799
|
resolve();
|
|
768
800
|
});
|
|
@@ -1342,7 +1374,7 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1342
1374
|
if (isDevelopment) {
|
|
1343
1375
|
await this.setupDevelopmentMode();
|
|
1344
1376
|
} else {
|
|
1345
|
-
this.setupProductionMode();
|
|
1377
|
+
await this.setupProductionMode();
|
|
1346
1378
|
}
|
|
1347
1379
|
}
|
|
1348
1380
|
async setupDevelopmentMode() {
|
|
@@ -1384,18 +1416,38 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1384
1416
|
this.logger.warn(`Failed to setup Vite proxy: ${error.message}. Make sure http-proxy-middleware is installed.`);
|
|
1385
1417
|
}
|
|
1386
1418
|
}
|
|
1387
|
-
setupProductionMode() {
|
|
1419
|
+
async setupProductionMode() {
|
|
1388
1420
|
try {
|
|
1389
1421
|
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
1390
|
-
if (httpAdapter)
|
|
1391
|
-
|
|
1392
|
-
|
|
1422
|
+
if (!httpAdapter) return;
|
|
1423
|
+
const app = httpAdapter.getInstance();
|
|
1424
|
+
const { join: join2 } = __require("path");
|
|
1425
|
+
const staticPath = join2(process.cwd(), "dist/client");
|
|
1426
|
+
const adapterType = detectAdapterType(this.httpAdapterHost);
|
|
1427
|
+
if (adapterType === "fastify") {
|
|
1428
|
+
try {
|
|
1429
|
+
const fastifyStatic = await import('@fastify/static').catch(() => null);
|
|
1430
|
+
if (fastifyStatic) {
|
|
1431
|
+
await app.register(fastifyStatic.default, {
|
|
1432
|
+
root: staticPath,
|
|
1433
|
+
prefix: "/",
|
|
1434
|
+
index: false,
|
|
1435
|
+
maxAge: 31536e6
|
|
1436
|
+
});
|
|
1437
|
+
this.logger.log("\u2713 Static assets configured (dist/client) [Fastify]");
|
|
1438
|
+
} else {
|
|
1439
|
+
this.logger.warn("For Fastify static file serving, install @fastify/static: npm install @fastify/static");
|
|
1440
|
+
}
|
|
1441
|
+
} catch {
|
|
1442
|
+
this.logger.warn("For Fastify static file serving, install @fastify/static: npm install @fastify/static");
|
|
1443
|
+
}
|
|
1444
|
+
} else {
|
|
1393
1445
|
const express = __require("express");
|
|
1394
|
-
app.use(express.static(
|
|
1446
|
+
app.use(express.static(staticPath, {
|
|
1395
1447
|
index: false,
|
|
1396
1448
|
maxAge: "1y"
|
|
1397
1449
|
}));
|
|
1398
|
-
this.logger.log("\u2713 Static assets configured (dist/client)");
|
|
1450
|
+
this.logger.log("\u2713 Static assets configured (dist/client) [Express]");
|
|
1399
1451
|
}
|
|
1400
1452
|
} catch (error) {
|
|
1401
1453
|
this.logger.warn(`Failed to setup static assets: ${error.message}`);
|
package/dist/render/index.mjs
CHANGED
|
@@ -455,6 +455,39 @@ function ErrorPageProduction() {
|
|
|
455
455
|
}
|
|
456
456
|
__name(ErrorPageProduction, "ErrorPageProduction");
|
|
457
457
|
|
|
458
|
+
// src/render/adapters/http-adapter-utils.ts
|
|
459
|
+
function detectAdapterType(httpAdapterHost) {
|
|
460
|
+
const adapter = httpAdapterHost?.httpAdapter;
|
|
461
|
+
if (!adapter) return "unknown";
|
|
462
|
+
const instance = adapter.getInstance();
|
|
463
|
+
if (instance && typeof instance.register === "function") {
|
|
464
|
+
return "fastify";
|
|
465
|
+
}
|
|
466
|
+
if (instance && typeof instance.use === "function" && typeof instance.get === "function") {
|
|
467
|
+
return "express";
|
|
468
|
+
}
|
|
469
|
+
return "unknown";
|
|
470
|
+
}
|
|
471
|
+
__name(detectAdapterType, "detectAdapterType");
|
|
472
|
+
function isFastifyLikeResponse(res) {
|
|
473
|
+
return res != null && typeof res === "object" && "raw" in res && res.raw != null && typeof res.raw.write === "function";
|
|
474
|
+
}
|
|
475
|
+
__name(isFastifyLikeResponse, "isFastifyLikeResponse");
|
|
476
|
+
function getRawResponse(res) {
|
|
477
|
+
if (isFastifyLikeResponse(res)) {
|
|
478
|
+
return res.raw;
|
|
479
|
+
}
|
|
480
|
+
return res;
|
|
481
|
+
}
|
|
482
|
+
__name(getRawResponse, "getRawResponse");
|
|
483
|
+
function isHeadersSent(res) {
|
|
484
|
+
if (typeof res.sent === "boolean") {
|
|
485
|
+
return res.sent;
|
|
486
|
+
}
|
|
487
|
+
return res.headersSent === true;
|
|
488
|
+
}
|
|
489
|
+
__name(isHeadersSent, "isHeadersSent");
|
|
490
|
+
|
|
458
491
|
// src/render/streaming-error-handler.ts
|
|
459
492
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
460
493
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -490,21 +523,19 @@ var StreamingErrorHandler = class _StreamingErrorHandler {
|
|
|
490
523
|
*/
|
|
491
524
|
handleShellError(error, res, viewPath, isDevelopment) {
|
|
492
525
|
this.logger.error(`Shell error rendering ${viewPath}: ${error.message}`, error.stack);
|
|
493
|
-
|
|
526
|
+
const rawRes = getRawResponse(res);
|
|
527
|
+
if (isHeadersSent(res)) {
|
|
494
528
|
this.logger.error(`Cannot send error page for ${viewPath} - headers already sent (streaming started)`);
|
|
495
|
-
if (!
|
|
496
|
-
|
|
497
|
-
|
|
529
|
+
if (!rawRes.writableEnded) {
|
|
530
|
+
rawRes.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
|
|
531
|
+
rawRes.end();
|
|
498
532
|
}
|
|
499
533
|
return;
|
|
500
534
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
} else {
|
|
506
|
-
res.send(this.renderProductionErrorPage());
|
|
507
|
-
}
|
|
535
|
+
rawRes.statusCode = 500;
|
|
536
|
+
rawRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
537
|
+
const html = isDevelopment ? this.renderDevelopmentErrorPage(error, viewPath, "shell") : this.renderProductionErrorPage();
|
|
538
|
+
rawRes.end(html);
|
|
508
539
|
}
|
|
509
540
|
/**
|
|
510
541
|
* Handle error that occurred during streaming
|
|
@@ -660,7 +691,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
660
691
|
*
|
|
661
692
|
* @param viewComponent - The React component to render
|
|
662
693
|
* @param data - Data to pass to the component
|
|
663
|
-
* @param res -
|
|
694
|
+
* @param res - HTTP response object (Express or Fastify)
|
|
664
695
|
* @param context - Render context with Vite and manifest info
|
|
665
696
|
* @param head - Head data for SEO tags
|
|
666
697
|
*/
|
|
@@ -702,20 +733,21 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
702
733
|
const { PassThrough } = await import('stream');
|
|
703
734
|
const reactStream = new PassThrough();
|
|
704
735
|
let allReadyFired = false;
|
|
736
|
+
const rawRes = getRawResponse(res);
|
|
705
737
|
const { pipe, abort } = renderModule.renderComponentStream(viewComponent, data, {
|
|
706
738
|
onShellReady: /* @__PURE__ */ __name(() => {
|
|
707
739
|
shellReadyTime = Date.now();
|
|
708
|
-
if (!
|
|
709
|
-
|
|
710
|
-
|
|
740
|
+
if (!rawRes.headersSent) {
|
|
741
|
+
rawRes.statusCode = didError ? 500 : 200;
|
|
742
|
+
rawRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
711
743
|
}
|
|
712
744
|
let htmlStart = templateParts.htmlStart;
|
|
713
745
|
htmlStart = htmlStart.replace("<!--styles-->", stylesheetTags);
|
|
714
746
|
htmlStart = htmlStart.replace("<!--head-meta-->", headTags);
|
|
715
|
-
|
|
716
|
-
|
|
747
|
+
rawRes.write(htmlStart);
|
|
748
|
+
rawRes.write(templateParts.rootStart);
|
|
717
749
|
pipe(reactStream);
|
|
718
|
-
reactStream.pipe(
|
|
750
|
+
reactStream.pipe(rawRes, {
|
|
719
751
|
end: false
|
|
720
752
|
});
|
|
721
753
|
if (context.isDevelopment) {
|
|
@@ -740,11 +772,11 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
740
772
|
if (shellErrorOccurred) {
|
|
741
773
|
return;
|
|
742
774
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
775
|
+
rawRes.write(inlineScripts);
|
|
776
|
+
rawRes.write(clientScript);
|
|
777
|
+
rawRes.write(templateParts.rootEnd);
|
|
778
|
+
rawRes.write(templateParts.htmlEnd);
|
|
779
|
+
rawRes.end();
|
|
748
780
|
if (context.isDevelopment) {
|
|
749
781
|
const totalTime = Date.now() - startTime;
|
|
750
782
|
const streamTime = Date.now() - shellReadyTime;
|
|
@@ -756,7 +788,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
756
788
|
reactStream.on("error", (error) => {
|
|
757
789
|
reject(error);
|
|
758
790
|
});
|
|
759
|
-
|
|
791
|
+
rawRes.on("close", () => {
|
|
760
792
|
abort();
|
|
761
793
|
resolve();
|
|
762
794
|
});
|
|
@@ -1336,7 +1368,7 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1336
1368
|
if (isDevelopment) {
|
|
1337
1369
|
await this.setupDevelopmentMode();
|
|
1338
1370
|
} else {
|
|
1339
|
-
this.setupProductionMode();
|
|
1371
|
+
await this.setupProductionMode();
|
|
1340
1372
|
}
|
|
1341
1373
|
}
|
|
1342
1374
|
async setupDevelopmentMode() {
|
|
@@ -1378,18 +1410,38 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1378
1410
|
this.logger.warn(`Failed to setup Vite proxy: ${error.message}. Make sure http-proxy-middleware is installed.`);
|
|
1379
1411
|
}
|
|
1380
1412
|
}
|
|
1381
|
-
setupProductionMode() {
|
|
1413
|
+
async setupProductionMode() {
|
|
1382
1414
|
try {
|
|
1383
1415
|
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
1384
|
-
if (httpAdapter)
|
|
1385
|
-
|
|
1386
|
-
|
|
1416
|
+
if (!httpAdapter) return;
|
|
1417
|
+
const app = httpAdapter.getInstance();
|
|
1418
|
+
const { join: join2 } = __require("path");
|
|
1419
|
+
const staticPath = join2(process.cwd(), "dist/client");
|
|
1420
|
+
const adapterType = detectAdapterType(this.httpAdapterHost);
|
|
1421
|
+
if (adapterType === "fastify") {
|
|
1422
|
+
try {
|
|
1423
|
+
const fastifyStatic = await import('@fastify/static').catch(() => null);
|
|
1424
|
+
if (fastifyStatic) {
|
|
1425
|
+
await app.register(fastifyStatic.default, {
|
|
1426
|
+
root: staticPath,
|
|
1427
|
+
prefix: "/",
|
|
1428
|
+
index: false,
|
|
1429
|
+
maxAge: 31536e6
|
|
1430
|
+
});
|
|
1431
|
+
this.logger.log("\u2713 Static assets configured (dist/client) [Fastify]");
|
|
1432
|
+
} else {
|
|
1433
|
+
this.logger.warn("For Fastify static file serving, install @fastify/static: npm install @fastify/static");
|
|
1434
|
+
}
|
|
1435
|
+
} catch {
|
|
1436
|
+
this.logger.warn("For Fastify static file serving, install @fastify/static: npm install @fastify/static");
|
|
1437
|
+
}
|
|
1438
|
+
} else {
|
|
1387
1439
|
const express = __require("express");
|
|
1388
|
-
app.use(express.static(
|
|
1440
|
+
app.use(express.static(staticPath, {
|
|
1389
1441
|
index: false,
|
|
1390
1442
|
maxAge: "1y"
|
|
1391
1443
|
}));
|
|
1392
|
-
this.logger.log("\u2713 Static assets configured (dist/client)");
|
|
1444
|
+
this.logger.log("\u2713 Static assets configured (dist/client) [Express]");
|
|
1393
1445
|
}
|
|
1394
1446
|
} catch (error) {
|
|
1395
1447
|
this.logger.warn(`Failed to setup static assets: ${error.message}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestjs-ssr/react",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "React SSR for NestJS that respects Clean Architecture. Proper DI, SOLID principles, clear separation of concerns.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nestjs",
|
|
@@ -112,6 +112,8 @@
|
|
|
112
112
|
"@nestjs/common": "^11.0.0",
|
|
113
113
|
"@nestjs/core": "^11.0.0",
|
|
114
114
|
"@nestjs/platform-express": "^11.0.0",
|
|
115
|
+
"@nestjs/platform-fastify": "^11.0.0",
|
|
116
|
+
"@fastify/static": "^8.0.0 || ^7.0.0",
|
|
115
117
|
"http-proxy-middleware": "^3.0.0 || ^2.0.0",
|
|
116
118
|
"react": "^19.0.0",
|
|
117
119
|
"react-dom": "^19.0.0",
|
|
@@ -119,6 +121,15 @@
|
|
|
119
121
|
"vite": "^7.0.0 || ^6.0.0"
|
|
120
122
|
},
|
|
121
123
|
"peerDependenciesMeta": {
|
|
124
|
+
"@nestjs/platform-express": {
|
|
125
|
+
"optional": true
|
|
126
|
+
},
|
|
127
|
+
"@nestjs/platform-fastify": {
|
|
128
|
+
"optional": true
|
|
129
|
+
},
|
|
130
|
+
"@fastify/static": {
|
|
131
|
+
"optional": true
|
|
132
|
+
},
|
|
122
133
|
"http-proxy-middleware": {
|
|
123
134
|
"optional": true
|
|
124
135
|
}
|