@nestjs-ssr/react 0.3.3 → 0.3.5

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/cli/init.js CHANGED
@@ -39,7 +39,7 @@ var main = citty.defineCommand({
39
39
  default: "5173"
40
40
  }
41
41
  },
42
- async run({ args }) {
42
+ run({ args }) {
43
43
  const cwd = process.cwd();
44
44
  const viewsDir = args.views;
45
45
  const vitePort = parseInt(args.port, 10) || 5173;
@@ -419,17 +419,17 @@ export default defineConfig({
419
419
  packageJson.scripts["start:dev"] = 'concurrently --raw -n vite,nest -c cyan,green "pnpm:dev:vite" "pnpm:dev:nest"';
420
420
  shouldUpdate = true;
421
421
  }
422
- const existingBuild = packageJson.scripts.build;
422
+ const existingBuild = packageJson.scripts["build"];
423
423
  const recommendedBuild = "nest build && pnpm build:client && pnpm build:server";
424
424
  if (!existingBuild) {
425
- packageJson.scripts.build = recommendedBuild;
425
+ packageJson.scripts["build"] = recommendedBuild;
426
426
  shouldUpdate = true;
427
427
  consola.consola.success("Created build script");
428
428
  } else if (existingBuild !== recommendedBuild) {
429
429
  if (!existingBuild.includes("build:client") || !existingBuild.includes("build:server")) {
430
430
  consola.consola.warn(`Found existing build script: "${existingBuild}"`);
431
431
  consola.consola.info(`Updating to: ${recommendedBuild}`);
432
- packageJson.scripts.build = recommendedBuild;
432
+ packageJson.scripts["build"] = recommendedBuild;
433
433
  shouldUpdate = true;
434
434
  } else {
435
435
  consola.consola.info("Build scripts already configured");
@@ -528,4 +528,4 @@ export default defineConfig({
528
528
  consola.consola.log(" Terminal 2: pnpm dev:nest");
529
529
  }
530
530
  });
531
- citty.runMain(main);
531
+ void citty.runMain(main);
package/dist/cli/init.mjs CHANGED
@@ -36,7 +36,7 @@ var main = defineCommand({
36
36
  default: "5173"
37
37
  }
38
38
  },
39
- async run({ args }) {
39
+ run({ args }) {
40
40
  const cwd = process.cwd();
41
41
  const viewsDir = args.views;
42
42
  const vitePort = parseInt(args.port, 10) || 5173;
@@ -416,17 +416,17 @@ export default defineConfig({
416
416
  packageJson.scripts["start:dev"] = 'concurrently --raw -n vite,nest -c cyan,green "pnpm:dev:vite" "pnpm:dev:nest"';
417
417
  shouldUpdate = true;
418
418
  }
419
- const existingBuild = packageJson.scripts.build;
419
+ const existingBuild = packageJson.scripts["build"];
420
420
  const recommendedBuild = "nest build && pnpm build:client && pnpm build:server";
421
421
  if (!existingBuild) {
422
- packageJson.scripts.build = recommendedBuild;
422
+ packageJson.scripts["build"] = recommendedBuild;
423
423
  shouldUpdate = true;
424
424
  consola.success("Created build script");
425
425
  } else if (existingBuild !== recommendedBuild) {
426
426
  if (!existingBuild.includes("build:client") || !existingBuild.includes("build:server")) {
427
427
  consola.warn(`Found existing build script: "${existingBuild}"`);
428
428
  consola.info(`Updating to: ${recommendedBuild}`);
429
- packageJson.scripts.build = recommendedBuild;
429
+ packageJson.scripts["build"] = recommendedBuild;
430
430
  shouldUpdate = true;
431
431
  } else {
432
432
  consola.info("Build scripts already configured");
@@ -525,4 +525,4 @@ export default defineConfig({
525
525
  consola.log(" Terminal 2: pnpm dev:nest");
526
526
  }
527
527
  });
528
- runMain(main);
528
+ void runMain(main);
package/dist/client.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- export { a as PageContextProvider, P as PageProps, c as createSSRHooks, j as updatePageContext, 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-CGT9woWe.mjs';
1
+ export { a as PageContextProvider, P as PageProps, c as createSSRHooks, j as updatePageContext, 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';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import React from 'react';
4
- export { R as RenderContext } from './render-response.interface-CxbuKGnV.mjs';
4
+ export { R as RenderContext } from './render-response.interface-ClWJXKL4.mjs';
5
5
 
6
6
  /**
7
7
  * Provider component for navigation state.
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { a as PageContextProvider, P as PageProps, c as createSSRHooks, j as updatePageContext, 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-05ODF4zW.js';
1
+ export { a as PageContextProvider, P as PageProps, c as createSSRHooks, j as updatePageContext, 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';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import React from 'react';
4
- export { R as RenderContext } from './render-response.interface-CxbuKGnV.js';
4
+ export { R as RenderContext } from './render-response.interface-ClWJXKL4.js';
5
5
 
6
6
  /**
7
7
  * Provider component for navigation state.
package/dist/client.js CHANGED
@@ -28,11 +28,31 @@ function updatePageContext(context) {
28
28
  setPageContextState?.(context);
29
29
  }
30
30
  __name(updatePageContext, "updatePageContext");
31
+ var segmentSetters = /* @__PURE__ */ new Set();
32
+ function registerSegmentSetter(setter) {
33
+ segmentSetters.add(setter);
34
+ }
35
+ __name(registerSegmentSetter, "registerSegmentSetter");
36
+ function unregisterSegmentSetter(setter) {
37
+ segmentSetters.delete(setter);
38
+ }
39
+ __name(unregisterSegmentSetter, "unregisterSegmentSetter");
40
+ function broadcastToSegments(context) {
41
+ segmentSetters.forEach((setter) => setter(context));
42
+ }
43
+ __name(broadcastToSegments, "broadcastToSegments");
31
44
  function PageContextProvider({ context: initialContext, children, isSegment = false }) {
32
45
  const [context, setContext] = React2.useState(initialContext);
33
46
  React2.useEffect(() => {
34
47
  if (!isSegment) {
35
- registerPageContextState(setContext);
48
+ registerPageContextState((newContext) => {
49
+ setContext(newContext);
50
+ broadcastToSegments(newContext);
51
+ });
52
+ return void 0;
53
+ } else {
54
+ registerSegmentSetter(setContext);
55
+ return () => unregisterSegmentSetter(setContext);
36
56
  }
37
57
  }, [
38
58
  isSegment
@@ -239,7 +259,7 @@ var useHeader = defaultHooks.useHeader;
239
259
  var useCookies = defaultHooks.useCookies;
240
260
  var useCookie = defaultHooks.useCookie;
241
261
  var rootRegistry = /* @__PURE__ */ new WeakMap();
242
- function hydrateSegment(outlet, componentName, props) {
262
+ function hydrateSegment(outlet, componentName, props, layouts) {
243
263
  const modules = window.__MODULES__;
244
264
  if (!modules) {
245
265
  console.warn("[navigation] Module registry not available for segment hydration. Make sure entry-client.tsx exports window.__MODULES__.");
@@ -254,10 +274,11 @@ function hydrateSegment(outlet, componentName, props) {
254
274
  return;
255
275
  }
256
276
  const context = window.__CONTEXT__ || {};
277
+ const composedElement = composeWithLayouts(ViewComponent, props, layouts || [], context, modules);
257
278
  const element = /* @__PURE__ */ React2__default.default.createElement(PageContextProvider, {
258
279
  context,
259
280
  isSegment: true
260
- }, /* @__PURE__ */ React2__default.default.createElement(ViewComponent, props));
281
+ }, composedElement);
261
282
  let wrapper = outlet.querySelector("[data-segment-root]");
262
283
  if (wrapper) {
263
284
  const existingRoot = rootRegistry.get(wrapper);
@@ -275,6 +296,27 @@ function hydrateSegment(outlet, componentName, props) {
275
296
  rootRegistry.set(wrapper, root);
276
297
  }
277
298
  __name(hydrateSegment, "hydrateSegment");
299
+ function composeWithLayouts(ViewComponent, props, layouts, context, modules) {
300
+ let result = /* @__PURE__ */ React2__default.default.createElement(ViewComponent, props);
301
+ for (let i = layouts.length - 1; i >= 0; i--) {
302
+ const { name: layoutName, props: layoutProps } = layouts[i];
303
+ const Layout = resolveComponent(layoutName, modules);
304
+ if (!Layout) {
305
+ console.warn(`[navigation] Layout "${layoutName}" not found for hydration`);
306
+ continue;
307
+ }
308
+ result = /* @__PURE__ */ React2__default.default.createElement("div", {
309
+ "data-layout": layoutName
310
+ }, /* @__PURE__ */ React2__default.default.createElement(Layout, {
311
+ context,
312
+ layoutProps
313
+ }, /* @__PURE__ */ React2__default.default.createElement("div", {
314
+ "data-outlet": layoutName
315
+ }, result)));
316
+ }
317
+ return result;
318
+ }
319
+ __name(composeWithLayouts, "composeWithLayouts");
278
320
  function resolveComponent(name, modules) {
279
321
  const componentMap = Object.entries(modules).filter(([path, module]) => {
280
322
  const filename = path.split("/").pop();
@@ -342,8 +384,12 @@ async function navigate(url, options = {}) {
342
384
  return;
343
385
  }
344
386
  const outlet = await swapContent(response.html, response.swapTarget);
387
+ if (response.context) {
388
+ updatePageContext(response.context);
389
+ window.__CONTEXT__ = response.context;
390
+ }
345
391
  if (outlet) {
346
- hydrateSegment(outlet, response.componentName, response.props);
392
+ hydrateSegment(outlet, response.componentName, response.props, response.layouts);
347
393
  }
348
394
  if (replace) {
349
395
  history.replaceState({
@@ -354,10 +400,6 @@ async function navigate(url, options = {}) {
354
400
  url
355
401
  }, "", url);
356
402
  }
357
- if (response.context) {
358
- updatePageContext(response.context);
359
- window.__CONTEXT__ = response.context;
360
- }
361
403
  if (response.head) {
362
404
  updateHead(response.head);
363
405
  }
package/dist/client.mjs CHANGED
@@ -22,11 +22,31 @@ function updatePageContext(context) {
22
22
  setPageContextState?.(context);
23
23
  }
24
24
  __name(updatePageContext, "updatePageContext");
25
+ var segmentSetters = /* @__PURE__ */ new Set();
26
+ function registerSegmentSetter(setter) {
27
+ segmentSetters.add(setter);
28
+ }
29
+ __name(registerSegmentSetter, "registerSegmentSetter");
30
+ function unregisterSegmentSetter(setter) {
31
+ segmentSetters.delete(setter);
32
+ }
33
+ __name(unregisterSegmentSetter, "unregisterSegmentSetter");
34
+ function broadcastToSegments(context) {
35
+ segmentSetters.forEach((setter) => setter(context));
36
+ }
37
+ __name(broadcastToSegments, "broadcastToSegments");
25
38
  function PageContextProvider({ context: initialContext, children, isSegment = false }) {
26
39
  const [context, setContext] = useState(initialContext);
27
40
  useEffect(() => {
28
41
  if (!isSegment) {
29
- registerPageContextState(setContext);
42
+ registerPageContextState((newContext) => {
43
+ setContext(newContext);
44
+ broadcastToSegments(newContext);
45
+ });
46
+ return void 0;
47
+ } else {
48
+ registerSegmentSetter(setContext);
49
+ return () => unregisterSegmentSetter(setContext);
30
50
  }
31
51
  }, [
32
52
  isSegment
@@ -233,7 +253,7 @@ var useHeader = defaultHooks.useHeader;
233
253
  var useCookies = defaultHooks.useCookies;
234
254
  var useCookie = defaultHooks.useCookie;
235
255
  var rootRegistry = /* @__PURE__ */ new WeakMap();
236
- function hydrateSegment(outlet, componentName, props) {
256
+ function hydrateSegment(outlet, componentName, props, layouts) {
237
257
  const modules = window.__MODULES__;
238
258
  if (!modules) {
239
259
  console.warn("[navigation] Module registry not available for segment hydration. Make sure entry-client.tsx exports window.__MODULES__.");
@@ -248,10 +268,11 @@ function hydrateSegment(outlet, componentName, props) {
248
268
  return;
249
269
  }
250
270
  const context = window.__CONTEXT__ || {};
271
+ const composedElement = composeWithLayouts(ViewComponent, props, layouts || [], context, modules);
251
272
  const element = /* @__PURE__ */ React2.createElement(PageContextProvider, {
252
273
  context,
253
274
  isSegment: true
254
- }, /* @__PURE__ */ React2.createElement(ViewComponent, props));
275
+ }, composedElement);
255
276
  let wrapper = outlet.querySelector("[data-segment-root]");
256
277
  if (wrapper) {
257
278
  const existingRoot = rootRegistry.get(wrapper);
@@ -269,6 +290,27 @@ function hydrateSegment(outlet, componentName, props) {
269
290
  rootRegistry.set(wrapper, root);
270
291
  }
271
292
  __name(hydrateSegment, "hydrateSegment");
293
+ function composeWithLayouts(ViewComponent, props, layouts, context, modules) {
294
+ let result = /* @__PURE__ */ React2.createElement(ViewComponent, props);
295
+ for (let i = layouts.length - 1; i >= 0; i--) {
296
+ const { name: layoutName, props: layoutProps } = layouts[i];
297
+ const Layout = resolveComponent(layoutName, modules);
298
+ if (!Layout) {
299
+ console.warn(`[navigation] Layout "${layoutName}" not found for hydration`);
300
+ continue;
301
+ }
302
+ result = /* @__PURE__ */ React2.createElement("div", {
303
+ "data-layout": layoutName
304
+ }, /* @__PURE__ */ React2.createElement(Layout, {
305
+ context,
306
+ layoutProps
307
+ }, /* @__PURE__ */ React2.createElement("div", {
308
+ "data-outlet": layoutName
309
+ }, result)));
310
+ }
311
+ return result;
312
+ }
313
+ __name(composeWithLayouts, "composeWithLayouts");
272
314
  function resolveComponent(name, modules) {
273
315
  const componentMap = Object.entries(modules).filter(([path, module]) => {
274
316
  const filename = path.split("/").pop();
@@ -336,8 +378,12 @@ async function navigate(url, options = {}) {
336
378
  return;
337
379
  }
338
380
  const outlet = await swapContent(response.html, response.swapTarget);
381
+ if (response.context) {
382
+ updatePageContext(response.context);
383
+ window.__CONTEXT__ = response.context;
384
+ }
339
385
  if (outlet) {
340
- hydrateSegment(outlet, response.componentName, response.props);
386
+ hydrateSegment(outlet, response.componentName, response.props, response.layouts);
341
387
  }
342
388
  if (replace) {
343
389
  history.replaceState({
@@ -348,10 +394,6 @@ async function navigate(url, options = {}) {
348
394
  url
349
395
  }, "", url);
350
396
  }
351
- if (response.context) {
352
- updatePageContext(response.context);
353
- window.__CONTEXT__ = response.context;
354
- }
355
397
  if (response.head) {
356
398
  updateHead(response.head);
357
399
  }
@@ -1,12 +1,45 @@
1
1
  import { DynamicModule, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
2
2
  import { ComponentType } from 'react';
3
- import { H as HeadData, R as RenderContext } from './render-response.interface-CxbuKGnV.mjs';
3
+ import { Request, Response } from 'express';
4
+ import { H as HeadData, R as RenderContext } from './render-response.interface-ClWJXKL4.mjs';
4
5
  import { ViteDevServer } from 'vite';
5
- import { Response } from 'express';
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
+ * Custom context properties that can be added via context factory.
12
+ * Allows any properties to be merged into RenderContext.
13
+ */
14
+ type CustomContextProperties = Record<string, unknown>;
15
+ /**
16
+ * Context factory function signature
17
+ * Called for each request to build custom context properties
18
+ *
19
+ * @param params - Object containing the Express request
20
+ * @returns Custom context properties to merge into RenderContext (sync or async)
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Simple: pass user from Passport
25
+ * context: ({ req }) => ({ user: req.user })
26
+ *
27
+ * // With CLS
28
+ * context: ({ req }) => ({
29
+ * user: req.user,
30
+ * tenant: cls.get('tenant'),
31
+ * })
32
+ *
33
+ * // Async with services
34
+ * context: async ({ req }) => ({
35
+ * user: req.user,
36
+ * permissions: await permissionService.getForUser(req.user),
37
+ * })
38
+ * ```
39
+ */
40
+ type ContextFactory = (params: {
41
+ req: Request;
42
+ }) => CustomContextProperties | Promise<CustomContextProperties>;
10
43
  /**
11
44
  * SSR rendering mode configuration
12
45
  */
@@ -173,6 +206,52 @@ interface RenderConfig {
173
206
  * ```
174
207
  */
175
208
  allowedCookies?: string[];
209
+ /**
210
+ * Context factory function to enrich RenderContext with custom properties
211
+ *
212
+ * This is called for each request and the result is merged into the base
213
+ * RenderContext (url, path, query, params, method, headers, cookies).
214
+ *
215
+ * Use this to add user data, feature flags, tenant info, or any other
216
+ * request-scoped data that should be available in React components via usePageContext().
217
+ *
218
+ * Similar to @nestjs/graphql context option - you choose how to get the data:
219
+ * - From request object (req.user from Passport)
220
+ * - From CLS (nestjs-cls)
221
+ * - From request-scoped services
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * // Simple: pass user from Passport JWT strategy
226
+ * RenderModule.forRoot({
227
+ * context: ({ req }) => ({
228
+ * user: req.user,
229
+ * }),
230
+ * })
231
+ *
232
+ * // With nestjs-cls
233
+ * RenderModule.forRoot({
234
+ * context: ({ req }) => ({
235
+ * user: req.user,
236
+ * tenant: cls.get('tenant'),
237
+ * featureFlags: cls.get('featureFlags'),
238
+ * }),
239
+ * })
240
+ *
241
+ * // Async with services (use forRootAsync)
242
+ * RenderModule.forRootAsync({
243
+ * imports: [PermissionModule],
244
+ * inject: [PermissionService],
245
+ * useFactory: (permissionService: PermissionService) => ({
246
+ * context: async ({ req }) => ({
247
+ * user: req.user,
248
+ * permissions: await permissionService.getForUser(req.user),
249
+ * }),
250
+ * }),
251
+ * })
252
+ * ```
253
+ */
254
+ context?: ContextFactory;
176
255
  }
177
256
  /**
178
257
  * Template parts for streaming SSR
@@ -206,6 +285,11 @@ interface SegmentResponse {
206
285
  componentName: string;
207
286
  /** Page context for updating hooks (path, params, query, etc.) */
208
287
  context?: RenderContext;
288
+ /** Layouts below the swap target that need to be composed on client */
289
+ layouts?: Array<{
290
+ name: string;
291
+ props?: any;
292
+ }>;
209
293
  }
210
294
 
211
295
  declare class RenderModule {
@@ -555,7 +639,8 @@ declare class RenderInterceptor implements NestInterceptor {
555
639
  private renderService;
556
640
  private allowedHeaders?;
557
641
  private allowedCookies?;
558
- constructor(reflector: Reflector, renderService: RenderService, allowedHeaders?: string[] | undefined, allowedCookies?: string[] | undefined);
642
+ private contextFactory?;
643
+ constructor(reflector: Reflector, renderService: RenderService, allowedHeaders?: string[] | undefined, allowedCookies?: string[] | undefined, contextFactory?: ContextFactory | undefined);
559
644
  /**
560
645
  * Resolve the layout hierarchy for a given route
561
646
  * Hierarchy: Root Layout → Controller Layout → Method Layout → Page
@@ -606,4 +691,4 @@ declare function ErrorPageDevelopment({ error, viewPath, phase, }: ErrorPageDeve
606
691
  */
607
692
  declare function ErrorPageProduction(): react_jsx_runtime.JSX.Element;
608
693
 
609
- export { ErrorPageDevelopment as E, RenderModule as R, StreamingErrorHandler as S, TemplateParserService as T, RenderService as a, RenderInterceptor as b, type RenderConfig as c, type SSRMode as d, ErrorPageProduction as e };
694
+ export { type ContextFactory as C, ErrorPageDevelopment as E, RenderModule as R, StreamingErrorHandler as S, TemplateParserService as T, RenderService as a, RenderInterceptor as b, type RenderConfig as c, type SSRMode as d, ErrorPageProduction as e };
@@ -1,12 +1,45 @@
1
1
  import { DynamicModule, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
2
2
  import { ComponentType } from 'react';
3
- import { H as HeadData, R as RenderContext } from './render-response.interface-CxbuKGnV.js';
3
+ import { Request, Response } from 'express';
4
+ import { H as HeadData, R as RenderContext } from './render-response.interface-ClWJXKL4.js';
4
5
  import { ViteDevServer } from 'vite';
5
- import { Response } from 'express';
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
+ * Custom context properties that can be added via context factory.
12
+ * Allows any properties to be merged into RenderContext.
13
+ */
14
+ type CustomContextProperties = Record<string, unknown>;
15
+ /**
16
+ * Context factory function signature
17
+ * Called for each request to build custom context properties
18
+ *
19
+ * @param params - Object containing the Express request
20
+ * @returns Custom context properties to merge into RenderContext (sync or async)
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Simple: pass user from Passport
25
+ * context: ({ req }) => ({ user: req.user })
26
+ *
27
+ * // With CLS
28
+ * context: ({ req }) => ({
29
+ * user: req.user,
30
+ * tenant: cls.get('tenant'),
31
+ * })
32
+ *
33
+ * // Async with services
34
+ * context: async ({ req }) => ({
35
+ * user: req.user,
36
+ * permissions: await permissionService.getForUser(req.user),
37
+ * })
38
+ * ```
39
+ */
40
+ type ContextFactory = (params: {
41
+ req: Request;
42
+ }) => CustomContextProperties | Promise<CustomContextProperties>;
10
43
  /**
11
44
  * SSR rendering mode configuration
12
45
  */
@@ -173,6 +206,52 @@ interface RenderConfig {
173
206
  * ```
174
207
  */
175
208
  allowedCookies?: string[];
209
+ /**
210
+ * Context factory function to enrich RenderContext with custom properties
211
+ *
212
+ * This is called for each request and the result is merged into the base
213
+ * RenderContext (url, path, query, params, method, headers, cookies).
214
+ *
215
+ * Use this to add user data, feature flags, tenant info, or any other
216
+ * request-scoped data that should be available in React components via usePageContext().
217
+ *
218
+ * Similar to @nestjs/graphql context option - you choose how to get the data:
219
+ * - From request object (req.user from Passport)
220
+ * - From CLS (nestjs-cls)
221
+ * - From request-scoped services
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * // Simple: pass user from Passport JWT strategy
226
+ * RenderModule.forRoot({
227
+ * context: ({ req }) => ({
228
+ * user: req.user,
229
+ * }),
230
+ * })
231
+ *
232
+ * // With nestjs-cls
233
+ * RenderModule.forRoot({
234
+ * context: ({ req }) => ({
235
+ * user: req.user,
236
+ * tenant: cls.get('tenant'),
237
+ * featureFlags: cls.get('featureFlags'),
238
+ * }),
239
+ * })
240
+ *
241
+ * // Async with services (use forRootAsync)
242
+ * RenderModule.forRootAsync({
243
+ * imports: [PermissionModule],
244
+ * inject: [PermissionService],
245
+ * useFactory: (permissionService: PermissionService) => ({
246
+ * context: async ({ req }) => ({
247
+ * user: req.user,
248
+ * permissions: await permissionService.getForUser(req.user),
249
+ * }),
250
+ * }),
251
+ * })
252
+ * ```
253
+ */
254
+ context?: ContextFactory;
176
255
  }
177
256
  /**
178
257
  * Template parts for streaming SSR
@@ -206,6 +285,11 @@ interface SegmentResponse {
206
285
  componentName: string;
207
286
  /** Page context for updating hooks (path, params, query, etc.) */
208
287
  context?: RenderContext;
288
+ /** Layouts below the swap target that need to be composed on client */
289
+ layouts?: Array<{
290
+ name: string;
291
+ props?: any;
292
+ }>;
209
293
  }
210
294
 
211
295
  declare class RenderModule {
@@ -555,7 +639,8 @@ declare class RenderInterceptor implements NestInterceptor {
555
639
  private renderService;
556
640
  private allowedHeaders?;
557
641
  private allowedCookies?;
558
- constructor(reflector: Reflector, renderService: RenderService, allowedHeaders?: string[] | undefined, allowedCookies?: string[] | undefined);
642
+ private contextFactory?;
643
+ constructor(reflector: Reflector, renderService: RenderService, allowedHeaders?: string[] | undefined, allowedCookies?: string[] | undefined, contextFactory?: ContextFactory | undefined);
559
644
  /**
560
645
  * Resolve the layout hierarchy for a given route
561
646
  * Hierarchy: Root Layout → Controller Layout → Method Layout → Page
@@ -606,4 +691,4 @@ declare function ErrorPageDevelopment({ error, viewPath, phase, }: ErrorPageDeve
606
691
  */
607
692
  declare function ErrorPageProduction(): react_jsx_runtime.JSX.Element;
608
693
 
609
- export { ErrorPageDevelopment as E, RenderModule as R, StreamingErrorHandler as S, TemplateParserService as T, RenderService as a, RenderInterceptor as b, type RenderConfig as c, type SSRMode as d, ErrorPageProduction as e };
694
+ export { type ContextFactory as C, ErrorPageDevelopment as E, RenderModule as R, StreamingErrorHandler as S, TemplateParserService as T, RenderService as a, RenderInterceptor as b, type RenderConfig as c, type SSRMode as d, ErrorPageProduction as e };
package/dist/index.d.mts CHANGED
@@ -1,11 +1,11 @@
1
- export { 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-DdE--mA2.mjs';
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-CiYcz-1T.mjs';
2
2
  import React, { ComponentType, ReactNode } from 'react';
3
- import { P as PageProps } from './use-page-context-CGT9woWe.mjs';
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-CGT9woWe.mjs';
5
- import { R as RenderContext, H as HeadData, a as RenderResponse } from './render-response.interface-CxbuKGnV.mjs';
3
+ import { P as PageProps } from './use-page-context-CVC9DHcL.mjs';
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
+ import { R as RenderContext, H as HeadData, a as RenderResponse } from './render-response.interface-ClWJXKL4.mjs';
6
6
  import '@nestjs/common';
7
- import 'vite';
8
7
  import 'express';
8
+ import 'vite';
9
9
  import '@nestjs/core';
10
10
  import 'rxjs';
11
11
  import 'react/jsx-runtime';
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- export { 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-BzOLOiIZ.js';
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-Dq2qZSge.js';
2
2
  import React, { ComponentType, ReactNode } from 'react';
3
- import { P as PageProps } from './use-page-context-05ODF4zW.js';
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-05ODF4zW.js';
5
- import { R as RenderContext, H as HeadData, a as RenderResponse } from './render-response.interface-CxbuKGnV.js';
3
+ import { P as PageProps } from './use-page-context-DChgHhL9.js';
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
+ import { R as RenderContext, H as HeadData, a as RenderResponse } from './render-response.interface-ClWJXKL4.js';
6
6
  import '@nestjs/common';
7
- import 'vite';
8
7
  import 'express';
8
+ import 'vite';
9
9
  import '@nestjs/core';
10
10
  import 'rxjs';
11
11
  import 'react/jsx-runtime';