@nestjs-ssr/react 0.3.4 → 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/client.d.mts +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/client.js +50 -8
- package/dist/client.mjs +50 -8
- package/dist/{index-BzOLOiIZ.d.ts → index-CSvZfKpi.d.ts} +161 -8
- package/dist/{index-DdE--mA2.d.mts → index-ZpkYrPcK.d.mts} +161 -8
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +137 -35
- package/dist/index.mjs +138 -36
- package/dist/render/index.d.mts +3 -3
- package/dist/render/index.d.ts +3 -3
- package/dist/render/index.js +118 -35
- package/dist/render/index.mjs +119 -36
- package/dist/{render-response.interface-CxbuKGnV.d.mts → render-response.interface-ClWJXKL4.d.mts} +19 -10
- package/dist/{render-response.interface-CxbuKGnV.d.ts → render-response.interface-ClWJXKL4.d.ts} +19 -10
- package/dist/templates/entry-client.tsx +23 -4
- package/dist/templates/entry-server.tsx +25 -8
- package/dist/{use-page-context-CGT9woWe.d.mts → use-page-context-CVC9DHcL.d.mts} +2 -1
- package/dist/{use-page-context-05ODF4zW.d.ts → use-page-context-DChgHhL9.d.ts} +2 -1
- package/package.json +12 -1
- package/src/templates/entry-client.tsx +23 -4
- package/src/templates/entry-server.tsx +25 -8
package/dist/render/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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 '../render-response.interface-
|
|
4
|
+
import '../render-response.interface-ClWJXKL4.js';
|
|
5
|
+
import 'http';
|
|
5
6
|
import 'vite';
|
|
6
|
-
import 'express';
|
|
7
7
|
import '@nestjs/core';
|
|
8
8
|
import 'rxjs';
|
|
9
9
|
import 'react/jsx-runtime';
|
package/dist/render/index.js
CHANGED
|
@@ -314,9 +314,13 @@ var StringRenderer = class _StringRenderer {
|
|
|
314
314
|
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
|
-
const { data: pageData, __context: pageContext } = data;
|
|
317
|
+
const { data: pageData, __context: pageContext, __layouts: layouts } = data;
|
|
318
318
|
const html = await renderModule.renderSegment(viewComponent, data);
|
|
319
319
|
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
320
|
+
const layoutMetadata = layouts ? layouts.map((l) => ({
|
|
321
|
+
name: l.layout.displayName || l.layout.name || "default",
|
|
322
|
+
props: l.props
|
|
323
|
+
})) : [];
|
|
320
324
|
if (context.isDevelopment) {
|
|
321
325
|
const duration = Date.now() - startTime;
|
|
322
326
|
this.logger.log(`[SSR] ${componentName} segment rendered in ${duration}ms`);
|
|
@@ -327,7 +331,8 @@ var StringRenderer = class _StringRenderer {
|
|
|
327
331
|
props: pageData,
|
|
328
332
|
swapTarget,
|
|
329
333
|
componentName,
|
|
330
|
-
context: pageContext
|
|
334
|
+
context: pageContext,
|
|
335
|
+
layouts: layoutMetadata
|
|
331
336
|
};
|
|
332
337
|
}
|
|
333
338
|
};
|
|
@@ -456,6 +461,39 @@ function ErrorPageProduction() {
|
|
|
456
461
|
}
|
|
457
462
|
__name(ErrorPageProduction, "ErrorPageProduction");
|
|
458
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
|
+
|
|
459
497
|
// src/render/streaming-error-handler.ts
|
|
460
498
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
461
499
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -491,21 +529,19 @@ exports.StreamingErrorHandler = class _StreamingErrorHandler {
|
|
|
491
529
|
*/
|
|
492
530
|
handleShellError(error, res, viewPath, isDevelopment) {
|
|
493
531
|
this.logger.error(`Shell error rendering ${viewPath}: ${error.message}`, error.stack);
|
|
494
|
-
|
|
532
|
+
const rawRes = getRawResponse(res);
|
|
533
|
+
if (isHeadersSent(res)) {
|
|
495
534
|
this.logger.error(`Cannot send error page for ${viewPath} - headers already sent (streaming started)`);
|
|
496
|
-
if (!
|
|
497
|
-
|
|
498
|
-
|
|
535
|
+
if (!rawRes.writableEnded) {
|
|
536
|
+
rawRes.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
|
|
537
|
+
rawRes.end();
|
|
499
538
|
}
|
|
500
539
|
return;
|
|
501
540
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
} else {
|
|
507
|
-
res.send(this.renderProductionErrorPage());
|
|
508
|
-
}
|
|
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);
|
|
509
545
|
}
|
|
510
546
|
/**
|
|
511
547
|
* Handle error that occurred during streaming
|
|
@@ -661,7 +697,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
661
697
|
*
|
|
662
698
|
* @param viewComponent - The React component to render
|
|
663
699
|
* @param data - Data to pass to the component
|
|
664
|
-
* @param res -
|
|
700
|
+
* @param res - HTTP response object (Express or Fastify)
|
|
665
701
|
* @param context - Render context with Vite and manifest info
|
|
666
702
|
* @param head - Head data for SEO tags
|
|
667
703
|
*/
|
|
@@ -703,20 +739,21 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
703
739
|
const { PassThrough } = await import('stream');
|
|
704
740
|
const reactStream = new PassThrough();
|
|
705
741
|
let allReadyFired = false;
|
|
742
|
+
const rawRes = getRawResponse(res);
|
|
706
743
|
const { pipe, abort } = renderModule.renderComponentStream(viewComponent, data, {
|
|
707
744
|
onShellReady: /* @__PURE__ */ __name(() => {
|
|
708
745
|
shellReadyTime = Date.now();
|
|
709
|
-
if (!
|
|
710
|
-
|
|
711
|
-
|
|
746
|
+
if (!rawRes.headersSent) {
|
|
747
|
+
rawRes.statusCode = didError ? 500 : 200;
|
|
748
|
+
rawRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
712
749
|
}
|
|
713
750
|
let htmlStart = templateParts.htmlStart;
|
|
714
751
|
htmlStart = htmlStart.replace("<!--styles-->", stylesheetTags);
|
|
715
752
|
htmlStart = htmlStart.replace("<!--head-meta-->", headTags);
|
|
716
|
-
|
|
717
|
-
|
|
753
|
+
rawRes.write(htmlStart);
|
|
754
|
+
rawRes.write(templateParts.rootStart);
|
|
718
755
|
pipe(reactStream);
|
|
719
|
-
reactStream.pipe(
|
|
756
|
+
reactStream.pipe(rawRes, {
|
|
720
757
|
end: false
|
|
721
758
|
});
|
|
722
759
|
if (context.isDevelopment) {
|
|
@@ -741,11 +778,11 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
741
778
|
if (shellErrorOccurred) {
|
|
742
779
|
return;
|
|
743
780
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
781
|
+
rawRes.write(inlineScripts);
|
|
782
|
+
rawRes.write(clientScript);
|
|
783
|
+
rawRes.write(templateParts.rootEnd);
|
|
784
|
+
rawRes.write(templateParts.htmlEnd);
|
|
785
|
+
rawRes.end();
|
|
749
786
|
if (context.isDevelopment) {
|
|
750
787
|
const totalTime = Date.now() - startTime;
|
|
751
788
|
const streamTime = Date.now() - shellReadyTime;
|
|
@@ -757,7 +794,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
757
794
|
reactStream.on("error", (error) => {
|
|
758
795
|
reject(error);
|
|
759
796
|
});
|
|
760
|
-
|
|
797
|
+
rawRes.on("close", () => {
|
|
761
798
|
abort();
|
|
762
799
|
resolve();
|
|
763
800
|
});
|
|
@@ -1078,11 +1115,13 @@ exports.RenderInterceptor = class RenderInterceptor {
|
|
|
1078
1115
|
renderService;
|
|
1079
1116
|
allowedHeaders;
|
|
1080
1117
|
allowedCookies;
|
|
1081
|
-
|
|
1118
|
+
contextFactory;
|
|
1119
|
+
constructor(reflector, renderService, allowedHeaders, allowedCookies, contextFactory) {
|
|
1082
1120
|
this.reflector = reflector;
|
|
1083
1121
|
this.renderService = renderService;
|
|
1084
1122
|
this.allowedHeaders = allowedHeaders;
|
|
1085
1123
|
this.allowedCookies = allowedCookies;
|
|
1124
|
+
this.contextFactory = contextFactory;
|
|
1086
1125
|
}
|
|
1087
1126
|
/**
|
|
1088
1127
|
* Resolve the layout hierarchy for a given route
|
|
@@ -1222,6 +1261,14 @@ exports.RenderInterceptor = class RenderInterceptor {
|
|
|
1222
1261
|
renderContext.cookies = cookies;
|
|
1223
1262
|
}
|
|
1224
1263
|
}
|
|
1264
|
+
if (this.contextFactory) {
|
|
1265
|
+
const customContext = await this.contextFactory({
|
|
1266
|
+
req: request
|
|
1267
|
+
});
|
|
1268
|
+
if (customContext) {
|
|
1269
|
+
Object.assign(renderContext, customContext);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1225
1272
|
const renderResponse = isRenderResponse(data) ? data : {
|
|
1226
1273
|
props: data
|
|
1227
1274
|
};
|
|
@@ -1268,12 +1315,15 @@ exports.RenderInterceptor = _ts_decorate6([
|
|
|
1268
1315
|
_ts_param3(2, common.Inject("ALLOWED_HEADERS")),
|
|
1269
1316
|
_ts_param3(3, common.Optional()),
|
|
1270
1317
|
_ts_param3(3, common.Inject("ALLOWED_COOKIES")),
|
|
1318
|
+
_ts_param3(4, common.Optional()),
|
|
1319
|
+
_ts_param3(4, common.Inject("CONTEXT_FACTORY")),
|
|
1271
1320
|
_ts_metadata5("design:type", Function),
|
|
1272
1321
|
_ts_metadata5("design:paramtypes", [
|
|
1273
1322
|
typeof core.Reflector === "undefined" ? Object : core.Reflector,
|
|
1274
1323
|
typeof exports.RenderService === "undefined" ? Object : exports.RenderService,
|
|
1275
1324
|
Array,
|
|
1276
|
-
Array
|
|
1325
|
+
Array,
|
|
1326
|
+
typeof ContextFactory === "undefined" ? Object : ContextFactory
|
|
1277
1327
|
])
|
|
1278
1328
|
], exports.RenderInterceptor);
|
|
1279
1329
|
function _ts_decorate7(decorators, target, key, desc) {
|
|
@@ -1324,7 +1374,7 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1324
1374
|
if (isDevelopment) {
|
|
1325
1375
|
await this.setupDevelopmentMode();
|
|
1326
1376
|
} else {
|
|
1327
|
-
this.setupProductionMode();
|
|
1377
|
+
await this.setupProductionMode();
|
|
1328
1378
|
}
|
|
1329
1379
|
}
|
|
1330
1380
|
async setupDevelopmentMode() {
|
|
@@ -1366,18 +1416,38 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1366
1416
|
this.logger.warn(`Failed to setup Vite proxy: ${error.message}. Make sure http-proxy-middleware is installed.`);
|
|
1367
1417
|
}
|
|
1368
1418
|
}
|
|
1369
|
-
setupProductionMode() {
|
|
1419
|
+
async setupProductionMode() {
|
|
1370
1420
|
try {
|
|
1371
1421
|
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
1372
|
-
if (httpAdapter)
|
|
1373
|
-
|
|
1374
|
-
|
|
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 {
|
|
1375
1445
|
const express = __require("express");
|
|
1376
|
-
app.use(express.static(
|
|
1446
|
+
app.use(express.static(staticPath, {
|
|
1377
1447
|
index: false,
|
|
1378
1448
|
maxAge: "1y"
|
|
1379
1449
|
}));
|
|
1380
|
-
this.logger.log("\u2713 Static assets configured (dist/client)");
|
|
1450
|
+
this.logger.log("\u2713 Static assets configured (dist/client) [Express]");
|
|
1381
1451
|
}
|
|
1382
1452
|
} catch (error) {
|
|
1383
1453
|
this.logger.warn(`Failed to setup static assets: ${error.message}`);
|
|
@@ -1517,6 +1587,12 @@ exports.RenderModule = class _RenderModule {
|
|
|
1517
1587
|
provide: "ALLOWED_COOKIES",
|
|
1518
1588
|
useValue: config?.allowedCookies || []
|
|
1519
1589
|
});
|
|
1590
|
+
if (config?.context) {
|
|
1591
|
+
providers.push({
|
|
1592
|
+
provide: "CONTEXT_FACTORY",
|
|
1593
|
+
useValue: config.context
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1520
1596
|
return {
|
|
1521
1597
|
global: true,
|
|
1522
1598
|
module: _RenderModule,
|
|
@@ -1635,6 +1711,13 @@ exports.RenderModule = class _RenderModule {
|
|
|
1635
1711
|
inject: [
|
|
1636
1712
|
"RENDER_CONFIG"
|
|
1637
1713
|
]
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
provide: "CONTEXT_FACTORY",
|
|
1717
|
+
useFactory: /* @__PURE__ */ __name((config) => config?.context, "useFactory"),
|
|
1718
|
+
inject: [
|
|
1719
|
+
"RENDER_CONFIG"
|
|
1720
|
+
]
|
|
1638
1721
|
}
|
|
1639
1722
|
];
|
|
1640
1723
|
return {
|
package/dist/render/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Injectable, Logger, Optional, Inject, Global, Module } from '@nestjs/common';
|
|
2
|
-
import { HttpAdapterHost, APP_INTERCEPTOR
|
|
2
|
+
import { Reflector, HttpAdapterHost, APP_INTERCEPTOR } from '@nestjs/core';
|
|
3
3
|
import { existsSync, readFileSync } from 'fs';
|
|
4
4
|
import { join, relative } from 'path';
|
|
5
5
|
import { uneval } from 'devalue';
|
|
@@ -308,9 +308,13 @@ var StringRenderer = class _StringRenderer {
|
|
|
308
308
|
throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
|
-
const { data: pageData, __context: pageContext } = data;
|
|
311
|
+
const { data: pageData, __context: pageContext, __layouts: layouts } = data;
|
|
312
312
|
const html = await renderModule.renderSegment(viewComponent, data);
|
|
313
313
|
const componentName = viewComponent.displayName || viewComponent.name || "Component";
|
|
314
|
+
const layoutMetadata = layouts ? layouts.map((l) => ({
|
|
315
|
+
name: l.layout.displayName || l.layout.name || "default",
|
|
316
|
+
props: l.props
|
|
317
|
+
})) : [];
|
|
314
318
|
if (context.isDevelopment) {
|
|
315
319
|
const duration = Date.now() - startTime;
|
|
316
320
|
this.logger.log(`[SSR] ${componentName} segment rendered in ${duration}ms`);
|
|
@@ -321,7 +325,8 @@ var StringRenderer = class _StringRenderer {
|
|
|
321
325
|
props: pageData,
|
|
322
326
|
swapTarget,
|
|
323
327
|
componentName,
|
|
324
|
-
context: pageContext
|
|
328
|
+
context: pageContext,
|
|
329
|
+
layouts: layoutMetadata
|
|
325
330
|
};
|
|
326
331
|
}
|
|
327
332
|
};
|
|
@@ -450,6 +455,39 @@ function ErrorPageProduction() {
|
|
|
450
455
|
}
|
|
451
456
|
__name(ErrorPageProduction, "ErrorPageProduction");
|
|
452
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
|
+
|
|
453
491
|
// src/render/streaming-error-handler.ts
|
|
454
492
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
455
493
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -485,21 +523,19 @@ var StreamingErrorHandler = class _StreamingErrorHandler {
|
|
|
485
523
|
*/
|
|
486
524
|
handleShellError(error, res, viewPath, isDevelopment) {
|
|
487
525
|
this.logger.error(`Shell error rendering ${viewPath}: ${error.message}`, error.stack);
|
|
488
|
-
|
|
526
|
+
const rawRes = getRawResponse(res);
|
|
527
|
+
if (isHeadersSent(res)) {
|
|
489
528
|
this.logger.error(`Cannot send error page for ${viewPath} - headers already sent (streaming started)`);
|
|
490
|
-
if (!
|
|
491
|
-
|
|
492
|
-
|
|
529
|
+
if (!rawRes.writableEnded) {
|
|
530
|
+
rawRes.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
|
|
531
|
+
rawRes.end();
|
|
493
532
|
}
|
|
494
533
|
return;
|
|
495
534
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
} else {
|
|
501
|
-
res.send(this.renderProductionErrorPage());
|
|
502
|
-
}
|
|
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);
|
|
503
539
|
}
|
|
504
540
|
/**
|
|
505
541
|
* Handle error that occurred during streaming
|
|
@@ -655,7 +691,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
655
691
|
*
|
|
656
692
|
* @param viewComponent - The React component to render
|
|
657
693
|
* @param data - Data to pass to the component
|
|
658
|
-
* @param res -
|
|
694
|
+
* @param res - HTTP response object (Express or Fastify)
|
|
659
695
|
* @param context - Render context with Vite and manifest info
|
|
660
696
|
* @param head - Head data for SEO tags
|
|
661
697
|
*/
|
|
@@ -697,20 +733,21 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
697
733
|
const { PassThrough } = await import('stream');
|
|
698
734
|
const reactStream = new PassThrough();
|
|
699
735
|
let allReadyFired = false;
|
|
736
|
+
const rawRes = getRawResponse(res);
|
|
700
737
|
const { pipe, abort } = renderModule.renderComponentStream(viewComponent, data, {
|
|
701
738
|
onShellReady: /* @__PURE__ */ __name(() => {
|
|
702
739
|
shellReadyTime = Date.now();
|
|
703
|
-
if (!
|
|
704
|
-
|
|
705
|
-
|
|
740
|
+
if (!rawRes.headersSent) {
|
|
741
|
+
rawRes.statusCode = didError ? 500 : 200;
|
|
742
|
+
rawRes.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
706
743
|
}
|
|
707
744
|
let htmlStart = templateParts.htmlStart;
|
|
708
745
|
htmlStart = htmlStart.replace("<!--styles-->", stylesheetTags);
|
|
709
746
|
htmlStart = htmlStart.replace("<!--head-meta-->", headTags);
|
|
710
|
-
|
|
711
|
-
|
|
747
|
+
rawRes.write(htmlStart);
|
|
748
|
+
rawRes.write(templateParts.rootStart);
|
|
712
749
|
pipe(reactStream);
|
|
713
|
-
reactStream.pipe(
|
|
750
|
+
reactStream.pipe(rawRes, {
|
|
714
751
|
end: false
|
|
715
752
|
});
|
|
716
753
|
if (context.isDevelopment) {
|
|
@@ -735,11 +772,11 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
735
772
|
if (shellErrorOccurred) {
|
|
736
773
|
return;
|
|
737
774
|
}
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
775
|
+
rawRes.write(inlineScripts);
|
|
776
|
+
rawRes.write(clientScript);
|
|
777
|
+
rawRes.write(templateParts.rootEnd);
|
|
778
|
+
rawRes.write(templateParts.htmlEnd);
|
|
779
|
+
rawRes.end();
|
|
743
780
|
if (context.isDevelopment) {
|
|
744
781
|
const totalTime = Date.now() - startTime;
|
|
745
782
|
const streamTime = Date.now() - shellReadyTime;
|
|
@@ -751,7 +788,7 @@ var StreamRenderer = class _StreamRenderer {
|
|
|
751
788
|
reactStream.on("error", (error) => {
|
|
752
789
|
reject(error);
|
|
753
790
|
});
|
|
754
|
-
|
|
791
|
+
rawRes.on("close", () => {
|
|
755
792
|
abort();
|
|
756
793
|
resolve();
|
|
757
794
|
});
|
|
@@ -1072,11 +1109,13 @@ var RenderInterceptor = class {
|
|
|
1072
1109
|
renderService;
|
|
1073
1110
|
allowedHeaders;
|
|
1074
1111
|
allowedCookies;
|
|
1075
|
-
|
|
1112
|
+
contextFactory;
|
|
1113
|
+
constructor(reflector, renderService, allowedHeaders, allowedCookies, contextFactory) {
|
|
1076
1114
|
this.reflector = reflector;
|
|
1077
1115
|
this.renderService = renderService;
|
|
1078
1116
|
this.allowedHeaders = allowedHeaders;
|
|
1079
1117
|
this.allowedCookies = allowedCookies;
|
|
1118
|
+
this.contextFactory = contextFactory;
|
|
1080
1119
|
}
|
|
1081
1120
|
/**
|
|
1082
1121
|
* Resolve the layout hierarchy for a given route
|
|
@@ -1216,6 +1255,14 @@ var RenderInterceptor = class {
|
|
|
1216
1255
|
renderContext.cookies = cookies;
|
|
1217
1256
|
}
|
|
1218
1257
|
}
|
|
1258
|
+
if (this.contextFactory) {
|
|
1259
|
+
const customContext = await this.contextFactory({
|
|
1260
|
+
req: request
|
|
1261
|
+
});
|
|
1262
|
+
if (customContext) {
|
|
1263
|
+
Object.assign(renderContext, customContext);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1219
1266
|
const renderResponse = isRenderResponse(data) ? data : {
|
|
1220
1267
|
props: data
|
|
1221
1268
|
};
|
|
@@ -1262,12 +1309,15 @@ RenderInterceptor = _ts_decorate6([
|
|
|
1262
1309
|
_ts_param3(2, Inject("ALLOWED_HEADERS")),
|
|
1263
1310
|
_ts_param3(3, Optional()),
|
|
1264
1311
|
_ts_param3(3, Inject("ALLOWED_COOKIES")),
|
|
1312
|
+
_ts_param3(4, Optional()),
|
|
1313
|
+
_ts_param3(4, Inject("CONTEXT_FACTORY")),
|
|
1265
1314
|
_ts_metadata5("design:type", Function),
|
|
1266
1315
|
_ts_metadata5("design:paramtypes", [
|
|
1267
1316
|
typeof Reflector === "undefined" ? Object : Reflector,
|
|
1268
1317
|
typeof RenderService === "undefined" ? Object : RenderService,
|
|
1269
1318
|
Array,
|
|
1270
|
-
Array
|
|
1319
|
+
Array,
|
|
1320
|
+
typeof ContextFactory === "undefined" ? Object : ContextFactory
|
|
1271
1321
|
])
|
|
1272
1322
|
], RenderInterceptor);
|
|
1273
1323
|
function _ts_decorate7(decorators, target, key, desc) {
|
|
@@ -1318,7 +1368,7 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1318
1368
|
if (isDevelopment) {
|
|
1319
1369
|
await this.setupDevelopmentMode();
|
|
1320
1370
|
} else {
|
|
1321
|
-
this.setupProductionMode();
|
|
1371
|
+
await this.setupProductionMode();
|
|
1322
1372
|
}
|
|
1323
1373
|
}
|
|
1324
1374
|
async setupDevelopmentMode() {
|
|
@@ -1360,18 +1410,38 @@ var ViteInitializerService = class _ViteInitializerService {
|
|
|
1360
1410
|
this.logger.warn(`Failed to setup Vite proxy: ${error.message}. Make sure http-proxy-middleware is installed.`);
|
|
1361
1411
|
}
|
|
1362
1412
|
}
|
|
1363
|
-
setupProductionMode() {
|
|
1413
|
+
async setupProductionMode() {
|
|
1364
1414
|
try {
|
|
1365
1415
|
const httpAdapter = this.httpAdapterHost.httpAdapter;
|
|
1366
|
-
if (httpAdapter)
|
|
1367
|
-
|
|
1368
|
-
|
|
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 {
|
|
1369
1439
|
const express = __require("express");
|
|
1370
|
-
app.use(express.static(
|
|
1440
|
+
app.use(express.static(staticPath, {
|
|
1371
1441
|
index: false,
|
|
1372
1442
|
maxAge: "1y"
|
|
1373
1443
|
}));
|
|
1374
|
-
this.logger.log("\u2713 Static assets configured (dist/client)");
|
|
1444
|
+
this.logger.log("\u2713 Static assets configured (dist/client) [Express]");
|
|
1375
1445
|
}
|
|
1376
1446
|
} catch (error) {
|
|
1377
1447
|
this.logger.warn(`Failed to setup static assets: ${error.message}`);
|
|
@@ -1511,6 +1581,12 @@ var RenderModule = class _RenderModule {
|
|
|
1511
1581
|
provide: "ALLOWED_COOKIES",
|
|
1512
1582
|
useValue: config?.allowedCookies || []
|
|
1513
1583
|
});
|
|
1584
|
+
if (config?.context) {
|
|
1585
|
+
providers.push({
|
|
1586
|
+
provide: "CONTEXT_FACTORY",
|
|
1587
|
+
useValue: config.context
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1514
1590
|
return {
|
|
1515
1591
|
global: true,
|
|
1516
1592
|
module: _RenderModule,
|
|
@@ -1629,6 +1705,13 @@ var RenderModule = class _RenderModule {
|
|
|
1629
1705
|
inject: [
|
|
1630
1706
|
"RENDER_CONFIG"
|
|
1631
1707
|
]
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
provide: "CONTEXT_FACTORY",
|
|
1711
|
+
useFactory: /* @__PURE__ */ __name((config) => config?.context, "useFactory"),
|
|
1712
|
+
inject: [
|
|
1713
|
+
"RENDER_CONFIG"
|
|
1714
|
+
]
|
|
1632
1715
|
}
|
|
1633
1716
|
];
|
|
1634
1717
|
return {
|
package/dist/{render-response.interface-CxbuKGnV.d.mts → render-response.interface-ClWJXKL4.d.mts}
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Contains safe request metadata that can be exposed to the client.
|
|
4
4
|
*
|
|
5
5
|
* Extend this interface to add app-specific properties (user, tenant, feature flags, etc.).
|
|
6
|
-
* Use module configuration to
|
|
6
|
+
* Use the `context` option in module configuration to enrich the context.
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* // Basic usage - use as-is
|
|
@@ -32,19 +32,28 @@
|
|
|
32
32
|
* theme?: string; // From cookie
|
|
33
33
|
* }
|
|
34
34
|
*
|
|
35
|
-
* // Configure module to
|
|
36
|
-
*
|
|
35
|
+
* // Configure module with context factory to enrich context
|
|
36
|
+
* RenderModule.forRoot({
|
|
37
37
|
* allowedCookies: ['theme', 'locale'],
|
|
38
38
|
* allowedHeaders: ['x-tenant-id'],
|
|
39
|
+
* context: ({ req }) => ({
|
|
40
|
+
* user: req.user, // From Passport JWT strategy
|
|
41
|
+
* tenant: req.tenant,
|
|
42
|
+
* featureFlags: req.featureFlags,
|
|
43
|
+
* }),
|
|
39
44
|
* })
|
|
40
45
|
*
|
|
41
|
-
* //
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
46
|
+
* // Or with async factory (use forRootAsync)
|
|
47
|
+
* RenderModule.forRootAsync({
|
|
48
|
+
* imports: [PermissionModule],
|
|
49
|
+
* inject: [PermissionService],
|
|
50
|
+
* useFactory: (permissionService) => ({
|
|
51
|
+
* context: async ({ req }) => ({
|
|
52
|
+
* user: req.user,
|
|
53
|
+
* permissions: await permissionService.getForUser(req.user),
|
|
54
|
+
* }),
|
|
55
|
+
* }),
|
|
56
|
+
* })
|
|
48
57
|
*/
|
|
49
58
|
interface RenderContext {
|
|
50
59
|
url: string;
|
package/dist/{render-response.interface-CxbuKGnV.d.ts → render-response.interface-ClWJXKL4.d.ts}
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Contains safe request metadata that can be exposed to the client.
|
|
4
4
|
*
|
|
5
5
|
* Extend this interface to add app-specific properties (user, tenant, feature flags, etc.).
|
|
6
|
-
* Use module configuration to
|
|
6
|
+
* Use the `context` option in module configuration to enrich the context.
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* // Basic usage - use as-is
|
|
@@ -32,19 +32,28 @@
|
|
|
32
32
|
* theme?: string; // From cookie
|
|
33
33
|
* }
|
|
34
34
|
*
|
|
35
|
-
* // Configure module to
|
|
36
|
-
*
|
|
35
|
+
* // Configure module with context factory to enrich context
|
|
36
|
+
* RenderModule.forRoot({
|
|
37
37
|
* allowedCookies: ['theme', 'locale'],
|
|
38
38
|
* allowedHeaders: ['x-tenant-id'],
|
|
39
|
+
* context: ({ req }) => ({
|
|
40
|
+
* user: req.user, // From Passport JWT strategy
|
|
41
|
+
* tenant: req.tenant,
|
|
42
|
+
* featureFlags: req.featureFlags,
|
|
43
|
+
* }),
|
|
39
44
|
* })
|
|
40
45
|
*
|
|
41
|
-
* //
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
46
|
+
* // Or with async factory (use forRootAsync)
|
|
47
|
+
* RenderModule.forRootAsync({
|
|
48
|
+
* imports: [PermissionModule],
|
|
49
|
+
* inject: [PermissionService],
|
|
50
|
+
* useFactory: (permissionService) => ({
|
|
51
|
+
* context: async ({ req }) => ({
|
|
52
|
+
* user: req.user,
|
|
53
|
+
* permissions: await permissionService.getForUser(req.user),
|
|
54
|
+
* }),
|
|
55
|
+
* }),
|
|
56
|
+
* })
|
|
48
57
|
*/
|
|
49
58
|
interface RenderContext {
|
|
50
59
|
url: string;
|