@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 +5 -5
- package/dist/cli/init.mjs +5 -5
- 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-DdE--mA2.d.mts → index-CiYcz-1T.d.mts} +89 -4
- package/dist/{index-BzOLOiIZ.d.ts → index-Dq2qZSge.d.ts} +89 -4
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +90 -36
- package/dist/index.mjs +61 -7
- package/dist/render/index.d.mts +3 -3
- package/dist/render/index.d.ts +3 -3
- package/dist/render/index.js +58 -24
- package/dist/render/index.mjs +41 -6
- 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 +69 -27
- 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/etc/react.api.md +250 -262
- package/package.json +1 -1
- package/src/templates/entry-client.tsx +69 -27
- package/src/templates/entry-server.tsx +25 -8
package/dist/cli/init.js
CHANGED
|
@@ -39,7 +39,7 @@ var main = citty.defineCommand({
|
|
|
39
39
|
default: "5173"
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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(
|
|
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
|
-
},
|
|
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(
|
|
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
|
-
},
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
5
|
-
import { R as RenderContext, H as HeadData, a as RenderResponse } from './render-response.interface-
|
|
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-
|
|
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-
|
|
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-
|
|
5
|
-
import { R as RenderContext, H as HeadData, a as RenderResponse } from './render-response.interface-
|
|
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';
|