@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.
@@ -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-BzOLOiIZ.js';
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-CxbuKGnV.js';
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';
@@ -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
- if (res.headersSent) {
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 (!res.writableEnded) {
497
- res.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
498
- res.end();
535
+ if (!rawRes.writableEnded) {
536
+ rawRes.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
537
+ rawRes.end();
499
538
  }
500
539
  return;
501
540
  }
502
- res.statusCode = 500;
503
- res.setHeader("Content-Type", "text/html; charset=utf-8");
504
- if (isDevelopment) {
505
- res.send(this.renderDevelopmentErrorPage(error, viewPath, "shell"));
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 - Express response object (required for streaming)
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 (!res.headersSent) {
710
- res.statusCode = didError ? 500 : 200;
711
- res.setHeader("Content-Type", "text/html; charset=utf-8");
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
- res.write(htmlStart);
717
- res.write(templateParts.rootStart);
753
+ rawRes.write(htmlStart);
754
+ rawRes.write(templateParts.rootStart);
718
755
  pipe(reactStream);
719
- reactStream.pipe(res, {
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
- res.write(inlineScripts);
745
- res.write(clientScript);
746
- res.write(templateParts.rootEnd);
747
- res.write(templateParts.htmlEnd);
748
- res.end();
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
- res.on("close", () => {
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
- constructor(reflector, renderService, allowedHeaders, allowedCookies) {
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
- const app = httpAdapter.getInstance();
1374
- const { join: join2 } = __require("path");
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(join2(process.cwd(), "dist/client"), {
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 {
@@ -1,5 +1,5 @@
1
1
  import { Injectable, Logger, Optional, Inject, Global, Module } from '@nestjs/common';
2
- import { HttpAdapterHost, APP_INTERCEPTOR, Reflector } from '@nestjs/core';
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
- if (res.headersSent) {
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 (!res.writableEnded) {
491
- res.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
492
- res.end();
529
+ if (!rawRes.writableEnded) {
530
+ rawRes.write(this.renderInlineErrorOverlay(error, viewPath, isDevelopment));
531
+ rawRes.end();
493
532
  }
494
533
  return;
495
534
  }
496
- res.statusCode = 500;
497
- res.setHeader("Content-Type", "text/html; charset=utf-8");
498
- if (isDevelopment) {
499
- res.send(this.renderDevelopmentErrorPage(error, viewPath, "shell"));
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 - Express response object (required for streaming)
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 (!res.headersSent) {
704
- res.statusCode = didError ? 500 : 200;
705
- res.setHeader("Content-Type", "text/html; charset=utf-8");
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
- res.write(htmlStart);
711
- res.write(templateParts.rootStart);
747
+ rawRes.write(htmlStart);
748
+ rawRes.write(templateParts.rootStart);
712
749
  pipe(reactStream);
713
- reactStream.pipe(res, {
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
- res.write(inlineScripts);
739
- res.write(clientScript);
740
- res.write(templateParts.rootEnd);
741
- res.write(templateParts.htmlEnd);
742
- res.end();
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
- res.on("close", () => {
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
- constructor(reflector, renderService, allowedHeaders, allowedCookies) {
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
- const app = httpAdapter.getInstance();
1368
- const { join: join2 } = __require("path");
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(join2(process.cwd(), "dist/client"), {
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 {
@@ -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 pass additional headers or cookies safely.
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 pass specific cookies/headers
36
- * ReactSSRModule.forRoot({
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
- * // Use in interceptor/controller
42
- * const context: AppRenderContext = {
43
- * ...baseContext,
44
- * user: req.user,
45
- * tenant: req.tenant,
46
- * featureFlags: await featureFlagService.getFlags(req),
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;
@@ -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 pass additional headers or cookies safely.
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 pass specific cookies/headers
36
- * ReactSSRModule.forRoot({
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
- * // Use in interceptor/controller
42
- * const context: AppRenderContext = {
43
- * ...baseContext,
44
- * user: req.user,
45
- * tenant: req.tenant,
46
- * featureFlags: await featureFlagService.getFlags(req),
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;