@nestjs-ssr/react 0.3.4 → 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/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';
package/dist/index.js CHANGED
@@ -315,9 +315,13 @@ var StringRenderer = class _StringRenderer {
315
315
  throw new Error("Server bundle not found in manifest. Run `pnpm build:server` to generate the server bundle.");
316
316
  }
317
317
  }
318
- const { data: pageData, __context: pageContext } = data;
318
+ const { data: pageData, __context: pageContext, __layouts: layouts } = data;
319
319
  const html = await renderModule.renderSegment(viewComponent, data);
320
320
  const componentName = viewComponent.displayName || viewComponent.name || "Component";
321
+ const layoutMetadata = layouts ? layouts.map((l) => ({
322
+ name: l.layout.displayName || l.layout.name || "default",
323
+ props: l.props
324
+ })) : [];
321
325
  if (context.isDevelopment) {
322
326
  const duration = Date.now() - startTime;
323
327
  this.logger.log(`[SSR] ${componentName} segment rendered in ${duration}ms`);
@@ -328,7 +332,8 @@ var StringRenderer = class _StringRenderer {
328
332
  props: pageData,
329
333
  swapTarget,
330
334
  componentName,
331
- context: pageContext
335
+ context: pageContext,
336
+ layouts: layoutMetadata
332
337
  };
333
338
  }
334
339
  };
@@ -1097,11 +1102,13 @@ exports.RenderInterceptor = class RenderInterceptor {
1097
1102
  renderService;
1098
1103
  allowedHeaders;
1099
1104
  allowedCookies;
1100
- constructor(reflector, renderService, allowedHeaders, allowedCookies) {
1105
+ contextFactory;
1106
+ constructor(reflector, renderService, allowedHeaders, allowedCookies, contextFactory) {
1101
1107
  this.reflector = reflector;
1102
1108
  this.renderService = renderService;
1103
1109
  this.allowedHeaders = allowedHeaders;
1104
1110
  this.allowedCookies = allowedCookies;
1111
+ this.contextFactory = contextFactory;
1105
1112
  }
1106
1113
  /**
1107
1114
  * Resolve the layout hierarchy for a given route
@@ -1241,6 +1248,14 @@ exports.RenderInterceptor = class RenderInterceptor {
1241
1248
  renderContext.cookies = cookies;
1242
1249
  }
1243
1250
  }
1251
+ if (this.contextFactory) {
1252
+ const customContext = await this.contextFactory({
1253
+ req: request
1254
+ });
1255
+ if (customContext) {
1256
+ Object.assign(renderContext, customContext);
1257
+ }
1258
+ }
1244
1259
  const renderResponse = isRenderResponse(data) ? data : {
1245
1260
  props: data
1246
1261
  };
@@ -1287,12 +1302,15 @@ exports.RenderInterceptor = _ts_decorate6([
1287
1302
  _ts_param3(2, common.Inject("ALLOWED_HEADERS")),
1288
1303
  _ts_param3(3, common.Optional()),
1289
1304
  _ts_param3(3, common.Inject("ALLOWED_COOKIES")),
1305
+ _ts_param3(4, common.Optional()),
1306
+ _ts_param3(4, common.Inject("CONTEXT_FACTORY")),
1290
1307
  _ts_metadata5("design:type", Function),
1291
1308
  _ts_metadata5("design:paramtypes", [
1292
1309
  typeof core.Reflector === "undefined" ? Object : core.Reflector,
1293
1310
  typeof exports.RenderService === "undefined" ? Object : exports.RenderService,
1294
1311
  Array,
1295
- Array
1312
+ Array,
1313
+ typeof ContextFactory === "undefined" ? Object : ContextFactory
1296
1314
  ])
1297
1315
  ], exports.RenderInterceptor);
1298
1316
  function _ts_decorate7(decorators, target, key, desc) {
@@ -1536,6 +1554,12 @@ exports.RenderModule = class _RenderModule {
1536
1554
  provide: "ALLOWED_COOKIES",
1537
1555
  useValue: config?.allowedCookies || []
1538
1556
  });
1557
+ if (config?.context) {
1558
+ providers.push({
1559
+ provide: "CONTEXT_FACTORY",
1560
+ useValue: config.context
1561
+ });
1562
+ }
1539
1563
  return {
1540
1564
  global: true,
1541
1565
  module: _RenderModule,
@@ -1654,6 +1678,13 @@ exports.RenderModule = class _RenderModule {
1654
1678
  inject: [
1655
1679
  "RENDER_CONFIG"
1656
1680
  ]
1681
+ },
1682
+ {
1683
+ provide: "CONTEXT_FACTORY",
1684
+ useFactory: /* @__PURE__ */ __name((config) => config?.context, "useFactory"),
1685
+ inject: [
1686
+ "RENDER_CONFIG"
1687
+ ]
1657
1688
  }
1658
1689
  ];
1659
1690
  return {
@@ -1716,9 +1747,28 @@ var PageContext = getOrCreateContext();
1716
1747
  function registerPageContextState(setter) {
1717
1748
  }
1718
1749
  __name(registerPageContextState, "registerPageContextState");
1750
+ var segmentSetters = /* @__PURE__ */ new Set();
1751
+ function registerSegmentSetter(setter) {
1752
+ segmentSetters.add(setter);
1753
+ }
1754
+ __name(registerSegmentSetter, "registerSegmentSetter");
1755
+ function unregisterSegmentSetter(setter) {
1756
+ segmentSetters.delete(setter);
1757
+ }
1758
+ __name(unregisterSegmentSetter, "unregisterSegmentSetter");
1759
+ function broadcastToSegments(context) {
1760
+ segmentSetters.forEach((setter) => setter(context));
1761
+ }
1762
+ __name(broadcastToSegments, "broadcastToSegments");
1719
1763
  function PageContextProvider({ context: initialContext, children, isSegment = false }) {
1720
1764
  const [context, setContext] = React2.useState(initialContext);
1721
1765
  React2.useEffect(() => {
1766
+ if (!isSegment) {
1767
+ return void 0;
1768
+ } else {
1769
+ registerSegmentSetter(setContext);
1770
+ return () => unregisterSegmentSetter(setContext);
1771
+ }
1722
1772
  }, [
1723
1773
  isSegment
1724
1774
  ]);