@nifrajs/web-react 4.0.2 → 5.0.0

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.
Files changed (61) hide show
  1. package/dist/auth.d.ts.map +1 -1
  2. package/dist/auth.js +39 -18
  3. package/dist/auth.js.map +1 -1
  4. package/dist/react-dom-server.js +1 -1
  5. package/dist/route-props.d.ts.map +1 -1
  6. package/dist/route-props.js +1 -0
  7. package/dist/route-props.js.map +1 -1
  8. package/dist/server-components/client.d.ts +3 -0
  9. package/dist/server-components/client.d.ts.map +1 -0
  10. package/dist/server-components/client.js +167 -0
  11. package/dist/server-components/client.js.map +1 -0
  12. package/dist/server-components/flight.d.ts +4 -0
  13. package/dist/server-components/flight.d.ts.map +1 -0
  14. package/dist/server-components/flight.js +87 -0
  15. package/dist/server-components/flight.js.map +1 -0
  16. package/dist/server-components/guard.d.ts +8 -0
  17. package/dist/server-components/guard.d.ts.map +1 -0
  18. package/dist/server-components/guard.js +139 -0
  19. package/dist/server-components/guard.js.map +1 -0
  20. package/dist/server-components/jsx-dev-runtime.d.ts +8 -0
  21. package/dist/server-components/jsx-dev-runtime.d.ts.map +1 -0
  22. package/dist/server-components/jsx-dev-runtime.js +12 -0
  23. package/dist/server-components/jsx-dev-runtime.js.map +1 -0
  24. package/dist/server-components/jsx-runtime.d.ts +5 -0
  25. package/dist/server-components/jsx-runtime.d.ts.map +1 -0
  26. package/dist/server-components/jsx-runtime.js +14 -0
  27. package/dist/server-components/jsx-runtime.js.map +1 -0
  28. package/dist/server-components/nested.d.ts +11 -0
  29. package/dist/server-components/nested.d.ts.map +1 -0
  30. package/dist/server-components/nested.js +12 -0
  31. package/dist/server-components/nested.js.map +1 -0
  32. package/dist/server-components/nested.props.d.ts +5 -0
  33. package/dist/server-components/nested.props.d.ts.map +1 -0
  34. package/dist/server-components/nested.props.js +3 -0
  35. package/dist/server-components/nested.props.js.map +1 -0
  36. package/dist/server-components/outlet.d.ts +19 -0
  37. package/dist/server-components/outlet.d.ts.map +1 -0
  38. package/dist/server-components/outlet.js +76 -0
  39. package/dist/server-components/outlet.js.map +1 -0
  40. package/dist/server-components/rsc.d.ts +4 -0
  41. package/dist/server-components/rsc.d.ts.map +1 -0
  42. package/dist/server-components/rsc.js +33 -0
  43. package/dist/server-components/rsc.js.map +1 -0
  44. package/dist/server-components/server.d.ts +4 -0
  45. package/dist/server-components/server.d.ts.map +1 -0
  46. package/dist/server-components/server.js +34 -0
  47. package/dist/server-components/server.js.map +1 -0
  48. package/package.json +63 -9
  49. package/src/auth.ts +34 -18
  50. package/src/react-dom-server.ts +1 -1
  51. package/src/route-props.ts +1 -0
  52. package/src/server-components/client.ts +176 -0
  53. package/src/server-components/flight.ts +87 -0
  54. package/src/server-components/guard.ts +199 -0
  55. package/src/server-components/jsx-dev-runtime.ts +27 -0
  56. package/src/server-components/jsx-runtime.ts +19 -0
  57. package/src/server-components/nested.props.ts +2 -0
  58. package/src/server-components/nested.ts +18 -0
  59. package/src/server-components/outlet.ts +109 -0
  60. package/src/server-components/rsc.ts +39 -0
  61. package/src/server-components/server.ts +52 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-dev-runtime.d.ts","sourceRoot":"","sources":["../../src/server-components/jsx-dev-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAA0B,MAAM,uBAAuB,CAAA;AAGxE,OAAO,EAAE,QAAQ,EAAE,CAAA;AAEnB,wBAAgB,MAAM,CACpB,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,gBAAgB,EAAE,OAAO,EACzB,MAAM,CAAC,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EACvE,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAcT"}
@@ -0,0 +1,12 @@
1
+ import { Fragment, jsxDEV as nativeJsxDEV } from "react/jsx-dev-runtime";
2
+ import { guardedClientElement } from "./guard.js";
3
+ export { Fragment };
4
+ export function jsxDEV(type, props, key, isStaticChildren, source, self) {
5
+ return (guardedClientElement(type, props, key) ??
6
+ nativeJsxDEV(
7
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The guard handles registered client references; other elements use the unchanged native React development JSX ABI.
8
+ type, props,
9
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The guard handles registered client references; other elements use the unchanged native React development JSX ABI.
10
+ key, isStaticChildren, source, self));
11
+ }
12
+ //# sourceMappingURL=jsx-dev-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-dev-runtime.js","sourceRoot":"","sources":["../../src/server-components/jsx-dev-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEjD,OAAO,EAAE,QAAQ,EAAE,CAAA;AAEnB,MAAM,UAAU,MAAM,CACpB,IAAa,EACb,KAAc,EACd,GAAY,EACZ,gBAAyB,EACzB,MAAuE,EACvE,IAAc;IAEd,OAAO,CACL,oBAAoB,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC;QACtC,YAAY;QACV,oLAAoL;QACpL,IAA0C,EAC1C,KAAK;QACL,oLAAoL;QACpL,GAAyB,EACzB,gBAAgB,EAChB,MAAM,EACN,IAAI,CACL,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Fragment } from "react/jsx-runtime";
2
+ export { Fragment };
3
+ export declare function jsx(type: unknown, props: unknown, key?: unknown): unknown;
4
+ export declare function jsxs(type: unknown, props: unknown, key?: unknown): unknown;
5
+ //# sourceMappingURL=jsx-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-runtime.d.ts","sourceRoot":"","sources":["../../src/server-components/jsx-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAwC,MAAM,mBAAmB,CAAA;AAGlF,OAAO,EAAE,QAAQ,EAAE,CAAA;AAEnB,wBAAgB,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAMzE;AACD,wBAAgB,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAM1E"}
@@ -0,0 +1,14 @@
1
+ import { Fragment, jsx as nativeJsx, jsxs as nativeJsxs } from "react/jsx-runtime";
2
+ import { guardedClientElement } from "./guard.js";
3
+ export { Fragment };
4
+ export function jsx(type, props, key) {
5
+ return (guardedClientElement(type, props, key) ??
6
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The guard handles registered client references; other elements use the unchanged native React JSX ABI.
7
+ nativeJsx(type, props, key));
8
+ }
9
+ export function jsxs(type, props, key) {
10
+ return (guardedClientElement(type, props, key) ??
11
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The guard handles registered client references; other elements use the unchanged native React JSX ABI.
12
+ nativeJsxs(type, props, key));
13
+ }
14
+ //# sourceMappingURL=jsx-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-runtime.js","sourceRoot":"","sources":["../../src/server-components/jsx-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,SAAS,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEjD,OAAO,EAAE,QAAQ,EAAE,CAAA;AAEnB,MAAM,UAAU,GAAG,CAAC,IAAa,EAAE,KAAc,EAAE,GAAa;IAC9D,OAAO,CACL,oBAAoB,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC;QACtC,wKAAwK;QACxK,SAAS,CAAC,IAAuC,EAAE,KAAK,EAAE,GAAyB,CAAC,CACrF,CAAA;AACH,CAAC;AACD,MAAM,UAAU,IAAI,CAAC,IAAa,EAAE,KAAc,EAAE,GAAa;IAC/D,OAAO,CACL,oBAAoB,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC;QACtC,wKAAwK;QACxK,UAAU,CAAC,IAAwC,EAAE,KAAK,EAAE,GAAyB,CAAC,CACvF,CAAA;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ /** The outer Flight root preserves this host; the controller owns the complete nested range. */
2
+ export declare function NestedServerComponent({ instance, html, }: {
3
+ readonly instance: string;
4
+ readonly html: string;
5
+ }): import("react").DOMElement<{
6
+ "data-nifra-component": string;
7
+ dangerouslySetInnerHTML: {
8
+ __html: string;
9
+ };
10
+ }, Element>;
11
+ //# sourceMappingURL=nested.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested.d.ts","sourceRoot":"","sources":["../../src/server-components/nested.ts"],"names":[],"mappings":"AAGA,gGAAgG;AAChG,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,IAAI,GACL,EAAE;IACD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;;;;;YAOA"}
@@ -0,0 +1,12 @@
1
+ "use client";
2
+ import { createElement, useRef } from "react";
3
+ /** The outer Flight root preserves this host; the controller owns the complete nested range. */
4
+ export function NestedServerComponent({ instance, html, }) {
5
+ const initial = useRef({ __html: html });
6
+ return createElement("nifra-component", {
7
+ "data-nifra-component": instance,
8
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: Parent rendering provides the verified server-owned nested range.
9
+ dangerouslySetInnerHTML: initial.current,
10
+ });
11
+ }
12
+ //# sourceMappingURL=nested.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested.js","sourceRoot":"","sources":["../../src/server-components/nested.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AACZ,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAE7C,gGAAgG;AAChG,MAAM,UAAU,qBAAqB,CAAC,EACpC,QAAQ,EACR,IAAI,GAIL;IACC,MAAM,OAAO,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IACxC,OAAO,aAAa,CAAC,iBAAiB,EAAE;QACtC,sBAAsB,EAAE,QAAQ;QAChC,0HAA0H;QAC1H,uBAAuB,EAAE,OAAO,CAAC,OAAO;KACzC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare const NestedServerComponent: import("@nifrajs/schema").NifraSchema<import("@sinclair/typebox").TObject<{
2
+ instance: import("@sinclair/typebox").TString;
3
+ html: import("@sinclair/typebox").TString;
4
+ }>>;
5
+ //# sourceMappingURL=nested.props.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested.props.d.ts","sourceRoot":"","sources":["../../src/server-components/nested.props.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,qBAAqB;;;GAAuD,CAAA"}
@@ -0,0 +1,3 @@
1
+ import { t } from "@nifrajs/schema";
2
+ export const NestedServerComponent = t.object({ instance: t.string(), html: t.string() });
3
+ //# sourceMappingURL=nested.props.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested.props.js","sourceRoot":"","sources":["../../src/server-components/nested.props.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAA;AACnC,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA"}
@@ -0,0 +1,19 @@
1
+ import { type ComponentClientAdapter, ComponentController, type ServerComponent as ComponentHandle, type ComponentKey, type ComponentState } from "@nifrajs/web/server-components/client";
2
+ export interface ServerComponentProps<Input, Props, View> {
3
+ readonly state: ComponentState;
4
+ readonly component: ComponentHandle<Input, Props, View>;
5
+ readonly instanceKey: ComponentKey;
6
+ readonly input: NoInfer<Input>;
7
+ readonly parent?: string;
8
+ readonly controller?: ComponentController | undefined;
9
+ readonly clientAdapter?: ComponentClientAdapter;
10
+ readonly onError?: (code: string) => void;
11
+ }
12
+ export declare function ServerComponent<Input, Props, View>(props: ServerComponentProps<Input, Props, View>): import("react").FunctionComponentElement<{
13
+ readonly state: ComponentState;
14
+ readonly record: import("@nifrajs/web/server-components/client").ComponentRecord;
15
+ readonly controller?: ComponentController | undefined;
16
+ readonly clientAdapter?: ComponentClientAdapter | undefined;
17
+ readonly onError?: ((code: string) => void) | undefined;
18
+ }>;
19
+ //# sourceMappingURL=outlet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outlet.d.ts","sourceRoot":"","sources":["../../src/server-components/outlet.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,EAC3B,mBAAmB,EACnB,KAAK,eAAe,IAAI,eAAe,EACvC,KAAK,YAAY,EACjB,KAAK,cAAc,EAEpB,MAAM,uCAAuC,CAAA;AAwB9C,MAAM,WAAW,oBAAoB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI;IACtD,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;IAC9B,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAA;IACvD,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAA;IACrD,QAAQ,CAAC,aAAa,CAAC,EAAE,sBAAsB,CAAA;IAC/C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC1C;AACD,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAChD,KAAK,EAAE,oBAAoB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC;oBAmB/B,cAAc;qBACb,OAAO,uCAAuC,EAAE,eAAe;0BAC1D,mBAAmB,GAAG,SAAS;6BAC5B,sBAAsB,GAAG,SAAS;uBACxC,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS;GANxD"}
@@ -0,0 +1,76 @@
1
+ import { ComponentController, resolveComponentRecord, } from "@nifrajs/web/server-components/client";
2
+ import { createElement, useLayoutEffect, useRef } from "react";
3
+ function lazyClient() {
4
+ let pending;
5
+ const client = () => {
6
+ if (typeof __NIFRA_COMPONENT_BROWSER__ === "boolean" && !__NIFRA_COMPONENT_BROWSER__)
7
+ throw new Error("component_contract");
8
+ pending ??= import("./client.js").then((module) => module.reactComponentClient());
9
+ return pending;
10
+ };
11
+ return {
12
+ name: "react",
13
+ prepare: async (...args) => (await client()).prepare(...args),
14
+ hydrate: async (...args) => (await client()).hydrate(...args),
15
+ create: async (...args) => {
16
+ const create = (await client()).create;
17
+ if (create === undefined)
18
+ throw new Error("component_contract");
19
+ return create(...args);
20
+ },
21
+ update: async (...args) => (await client()).update(...args),
22
+ };
23
+ }
24
+ export function ServerComponent(props) {
25
+ const record = resolveComponentRecord(props.state, props.component, props.instanceKey, props.input, props.parent ?? null);
26
+ return createElement(ComponentRange, {
27
+ key: record.instance,
28
+ state: props.state,
29
+ record,
30
+ controller: props.controller,
31
+ clientAdapter: props.clientAdapter,
32
+ onError: props.onError,
33
+ });
34
+ }
35
+ function ComponentRange(props) {
36
+ const record = props.record;
37
+ const host = useRef(null);
38
+ // React owns the shell; the independently hydrated native root is an unassociated child.
39
+ const initial = useRef({
40
+ __html: `<nifra-component data-nifra-component="${record.instance}">${record.html}</nifra-component>`,
41
+ });
42
+ const active = useRef(undefined);
43
+ const lifecycle = useRef(props);
44
+ lifecycle.current = props;
45
+ useLayoutEffect(() => {
46
+ const options = lifecycle.current;
47
+ const element = host.current?.firstElementChild;
48
+ if (!(element instanceof HTMLElement) || element.localName !== "nifra-component")
49
+ return;
50
+ const controller = options.controller ??
51
+ new ComponentController({
52
+ state: options.state,
53
+ adapter: options.clientAdapter ?? lazyClient(),
54
+ onError: (code) => lifecycle.current.onError?.(code),
55
+ });
56
+ active.current = controller;
57
+ void controller
58
+ .mount(element, record.instance)
59
+ .catch(() => lifecycle.current.onError?.("component_failed"));
60
+ return () => {
61
+ void (options.controller === undefined
62
+ ? controller.dispose()
63
+ : controller.unmount(record.instance)).catch(() => lifecycle.current.onError?.("component_failed"));
64
+ };
65
+ }, [record.instance]);
66
+ useLayoutEffect(() => {
67
+ if (active.current !== undefined)
68
+ void active.current.adopt(props.state).catch(() => props.onError?.("component_failed"));
69
+ }, [props.state, props.onError]);
70
+ return createElement("nifra-component-outlet", {
71
+ ref: host,
72
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: Record HTML passed the shared component policy before an outlet can resolve it.
73
+ dangerouslySetInnerHTML: initial.current,
74
+ });
75
+ }
76
+ //# sourceMappingURL=outlet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outlet.js","sourceRoot":"","sources":["../../src/server-components/outlet.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ComponentRenderContext, ServerComponentHandle } from "@nifrajs/web/server-components";
2
+ /** Export this renderer from the compiler's RSC entry, which owns React's server condition. */
3
+ export declare function createReactComponentRenderer(components: readonly ServerComponentHandle[]): (reference: string, props: unknown, context: ComponentRenderContext) => Promise<ReadableStream<Uint8Array>>;
4
+ //# sourceMappingURL=rsc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rsc.d.ts","sourceRoot":"","sources":["../../src/server-components/rsc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAA;AAMnG,+FAA+F;AAC/F,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,SAAS,qBAAqB,EAAE,IAKrF,WAAW,MAAM,EACjB,OAAO,OAAO,EACd,SAAS,sBAAsB,KAC9B,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAuBvC"}
@@ -0,0 +1,33 @@
1
+ import { guardComponentProps } from "@nifrajs/web/server-components/props";
2
+ import { renderToReadableStream } from "@vitejs/plugin-rsc/rsc/server";
3
+ import { createElement } from "react";
4
+ import { publicFlightStream } from "./flight.js";
5
+ /** Export this renderer from the compiler's RSC entry, which owns React's server condition. */
6
+ export function createReactComponentRenderer(components) {
7
+ const references = new Map(components.map((component) => [component.reference, component]));
8
+ if (references.size !== components.length || references.has(undefined))
9
+ throw new Error("component_contract");
10
+ return async (reference, props, context) => {
11
+ const component = references.get(reference);
12
+ if (component === undefined)
13
+ throw new Error("component_unavailable");
14
+ const projected = await guardComponentProps(component.props, props);
15
+ const stream = renderToReadableStream(
16
+ // biome-ignore lint/plugin/requireSafetyCommentForTypeAssertion: The renderer resolves an allowlisted definition and guards its public props before invoking that definition.
17
+ createElement(component.view, {
18
+ props: projected,
19
+ // biome-ignore lint/correctness/noChildrenProp: Server views receive verified record metadata through their declared children prop.
20
+ children: context.children ?? [],
21
+ }), {
22
+ signal: context.signal,
23
+ // React development debug metadata can contain server-only props; it has no public transport.
24
+ debugChannel: { writable: new WritableStream({ write() { } }) },
25
+ filterStackFrame: () => false,
26
+ onError() {
27
+ return "component_failed";
28
+ },
29
+ });
30
+ return publicFlightStream(stream, context.signal);
31
+ };
32
+ }
33
+ //# sourceMappingURL=rsc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rsc.js","sourceRoot":"","sources":["../../src/server-components/rsc.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAA;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAA;AACtE,OAAO,EAAsB,aAAa,EAAE,MAAM,OAAO,CAAA;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEhD,+FAA+F;AAC/F,MAAM,UAAU,4BAA4B,CAAC,UAA4C;IACvF,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IAC3F,IAAI,UAAU,CAAC,IAAI,KAAK,UAAU,CAAC,MAAM,IAAI,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAA;IACvC,OAAO,KAAK,EACV,SAAiB,EACjB,KAAc,EACd,OAA+B,EACM,EAAE;QACvC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAC3C,IAAI,SAAS,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAA;QACrE,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QACnE,MAAM,MAAM,GAAG,sBAAsB;QACnC,8KAA8K;QAC9K,aAAa,CAAC,SAAS,CAAC,IAA4D,EAAE;YACpF,KAAK,EAAE,SAAS;YAChB,oIAAoI;YACpI,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE;SACjC,CAAC,EACF;YACE,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,8FAA8F;YAC9F,YAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,cAAc,CAAa,EAAE,KAAK,KAAI,CAAC,EAAE,CAAC,EAAE;YAC1E,gBAAgB,EAAE,GAAG,EAAE,CAAC,KAAK;YAC7B,OAAO;gBACL,OAAO,kBAAkB,CAAA;YAC3B,CAAC;SACF,CACF,CAAA;QACD,OAAO,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACnD,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ComponentRenderContext, ServerComponentRenderAdapter } from "@nifrajs/web/server-components";
2
+ export type ReactFlightRenderer = (reference: string, props: unknown, context: ComponentRenderContext) => Promise<ReadableStream<Uint8Array>>;
3
+ export declare function reactServerComponents(renderFlight: ReactFlightRenderer): ServerComponentRenderAdapter;
4
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server-components/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,4BAA4B,EAC7B,MAAM,gCAAgC,CAAA;AAUvC,MAAM,MAAM,mBAAmB,GAAG,CAChC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,sBAAsB,KAC5B,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAA;AAExC,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,mBAAmB,GAChC,4BAA4B,CA8B9B"}
@@ -0,0 +1,34 @@
1
+ import { bytesStream, encodeComponentBytes, readComponentBytes, } from "@nifrajs/web/server-components/stream";
2
+ import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
3
+ import { renderToReadableStream } from "react-dom/server.edge";
4
+ export function reactServerComponents(renderFlight) {
5
+ return {
6
+ name: "react",
7
+ version: 1,
8
+ async render(_view, props, context) {
9
+ const flight = await readComponentBytes(await renderFlight(context.reference, props, context), context.signal);
10
+ const tree = await createFromReadableStream(bytesStream(flight));
11
+ let failed = false;
12
+ const options = {
13
+ identifierPrefix: context.instance + "-",
14
+ signal: context.signal,
15
+ onError() {
16
+ failed = true;
17
+ return "component_failed";
18
+ },
19
+ };
20
+ if (context.nonce !== undefined)
21
+ options.nonce = context.nonce;
22
+ const html = await renderToReadableStream(tree, options);
23
+ await html.allReady;
24
+ const bytes = await readComponentBytes(html, context.signal);
25
+ if (failed)
26
+ throw new Error("component_failed");
27
+ return {
28
+ html: new TextDecoder().decode(bytes),
29
+ payload: { flight: encodeComponentBytes(flight) },
30
+ };
31
+ },
32
+ };
33
+ }
34
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server-components/server.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AAEjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAQ9D,MAAM,UAAU,qBAAqB,CACnC,YAAiC;IAEjC,OAAO;QACL,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,CAAC;QACV,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO;YAChC,MAAM,MAAM,GAAG,MAAM,kBAAkB,CACrC,MAAM,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,EACrD,OAAO,CAAC,MAAM,CACf,CAAA;YACD,MAAM,IAAI,GAAG,MAAM,wBAAwB,CAAY,WAAW,CAAC,MAAM,CAAC,CAAC,CAAA;YAC3E,IAAI,MAAM,GAAG,KAAK,CAAA;YAClB,MAAM,OAAO,GAA8D;gBACzE,gBAAgB,EAAE,OAAO,CAAC,QAAQ,GAAG,GAAG;gBACxC,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO;oBACL,MAAM,GAAG,IAAI,CAAA;oBACb,OAAO,kBAAkB,CAAA;gBAC3B,CAAC;aACF,CAAA;YACD,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;gBAAE,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAA;YAC9D,MAAM,IAAI,GAAG,MAAM,sBAAsB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YACxD,MAAM,IAAI,CAAC,QAAQ,CAAA;YACnB,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;YAC5D,IAAI,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAA;YAC/C,OAAO;gBACL,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC;gBACrC,OAAO,EAAE,EAAE,MAAM,EAAE,oBAAoB,CAAC,MAAM,CAAC,EAAE;aAClD,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nifrajs/web-react",
3
- "version": "4.0.2",
3
+ "version": "5.0.0",
4
4
  "description": "React render adapter for @nifrajs/web - SSR + hydration (Bun-native JSX, no Babel).",
5
5
  "keywords": [
6
6
  "bun",
@@ -80,6 +80,46 @@
80
80
  "bun": "./src/fn.ts",
81
81
  "types": "./dist/fn.d.ts",
82
82
  "default": "./dist/fn.js"
83
+ },
84
+ "./server-components/guard": {
85
+ "bun": "./src/server-components/guard.ts",
86
+ "types": "./dist/server-components/guard.d.ts",
87
+ "default": "./dist/server-components/guard.js"
88
+ },
89
+ "./server-components/jsx-runtime": {
90
+ "bun": "./src/server-components/jsx-runtime.ts",
91
+ "types": "./dist/server-components/jsx-runtime.d.ts",
92
+ "default": "./dist/server-components/jsx-runtime.js"
93
+ },
94
+ "./server-components/jsx-dev-runtime": {
95
+ "bun": "./src/server-components/jsx-dev-runtime.ts",
96
+ "types": "./dist/server-components/jsx-dev-runtime.d.ts",
97
+ "default": "./dist/server-components/jsx-dev-runtime.js"
98
+ },
99
+ "./server-components": {
100
+ "bun": "./src/server-components/server.ts",
101
+ "types": "./dist/server-components/server.d.ts",
102
+ "default": "./dist/server-components/server.js"
103
+ },
104
+ "./server-components/rsc": {
105
+ "bun": "./src/server-components/rsc.ts",
106
+ "types": "./dist/server-components/rsc.d.ts",
107
+ "default": "./dist/server-components/rsc.js"
108
+ },
109
+ "./server-components/client": {
110
+ "bun": "./src/server-components/client.ts",
111
+ "types": "./dist/server-components/client.d.ts",
112
+ "default": "./dist/server-components/client.js"
113
+ },
114
+ "./server-components/outlet": {
115
+ "bun": "./src/server-components/outlet.ts",
116
+ "types": "./dist/server-components/outlet.d.ts",
117
+ "default": "./dist/server-components/outlet.js"
118
+ },
119
+ "./server-components/nested": {
120
+ "bun": "./src/server-components/nested.ts",
121
+ "types": "./dist/server-components/nested.d.ts",
122
+ "default": "./dist/server-components/nested.js"
83
123
  }
84
124
  },
85
125
  "files": [
@@ -95,21 +135,27 @@
95
135
  "access": "public"
96
136
  },
97
137
  "dependencies": {
98
- "@nifrajs/core": "4.0.2",
99
- "@nifrajs/authjs": "4.0.2",
100
- "@nifrajs/i18n": "4.0.2",
101
- "@nifrajs/image": "4.0.2",
102
- "@nifrajs/web": "4.0.2"
138
+ "@nifrajs/core": "5.0.0",
139
+ "@nifrajs/authjs": "5.0.0",
140
+ "@nifrajs/i18n": "5.0.0",
141
+ "@nifrajs/image": "5.0.0",
142
+ "@nifrajs/web": "5.0.0",
143
+ "@nifrajs/schema": "5.0.0"
103
144
  },
104
145
  "peerDependencies": {
105
146
  "react": "^19.0.0",
106
- "react-dom": "^19.0.0"
147
+ "react-dom": "^19.0.0",
148
+ "@vitejs/plugin-rsc": "0.5.35",
149
+ "react-server-dom-webpack": "19.2.8"
107
150
  },
108
151
  "devDependencies": {
109
152
  "@types/react": "^19.0.0",
110
153
  "@types/react-dom": "^19.0.0",
111
154
  "react": "^19.0.0",
112
- "react-dom": "^19.0.0"
155
+ "react-dom": "^19.0.0",
156
+ "@vitejs/plugin-rsc": "0.5.35",
157
+ "react-server-dom-webpack": "19.2.8",
158
+ "vite": "8.2.1"
113
159
  },
114
160
  "repository": {
115
161
  "type": "git",
@@ -119,5 +165,13 @@
119
165
  "bugs": {
120
166
  "url": "https://github.com/nifrajs/nifra/issues"
121
167
  },
122
- "homepage": "https://github.com/nifrajs/nifra#readme"
168
+ "homepage": "https://github.com/nifrajs/nifra#readme",
169
+ "peerDependenciesMeta": {
170
+ "@vitejs/plugin-rsc": {
171
+ "optional": true
172
+ },
173
+ "react-server-dom-webpack": {
174
+ "optional": true
175
+ }
176
+ }
123
177
  }
package/src/auth.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  useContext,
19
19
  useEffect,
20
20
  useMemo,
21
+ useRef,
21
22
  useState,
22
23
  } from "react"
23
24
 
@@ -55,7 +56,18 @@ export function AuthSessionProvider(props: AuthSessionProviderProps): ReactNode
55
56
  const [seed, setSeed] = useState(props.initialSession)
56
57
  const [status, setStatus] = useState<AuthStatus>(seededStatus(props.initialSession))
57
58
  const [session, setSession] = useState<Session | null>(props.initialSession ?? null)
59
+ // A read applies only while it holds the latest generation; a newer read, a sign-out, a new client
60
+ // or seed, and unmount each take one. The record is never replaced, so a sign-out that settles
61
+ // after a client or seed change still clears the session the server ended.
62
+ const reads = useRef({ generation: 0, signingOut: 0, live: true, client })
63
+ if (reads.current.client !== client) {
64
+ // A new client keeps what is shown (a parent may build one per render); only answers to the
65
+ // previous client's reads are dropped.
66
+ reads.current.client = client
67
+ reads.current.generation++
68
+ }
58
69
  if (seed !== props.initialSession) {
70
+ reads.current.generation++
59
71
  // A render-phase update: React re-runs this component before any child sees the old session.
60
72
  setSeed(props.initialSession)
61
73
  setStatus(seededStatus(props.initialSession))
@@ -63,40 +75,35 @@ export function AuthSessionProvider(props: AuthSessionProviderProps): ReactNode
63
75
  }
64
76
  const refresh = useMemo(() => {
65
77
  return async (): Promise<void> => {
78
+ const state = reads.current
79
+ // A read made while signing out could answer with the session being ended.
80
+ if (state.signingOut > 0 || state.client !== client) return
81
+ const generation = ++state.generation
66
82
  try {
67
83
  const next = await client.getSession()
84
+ if (!state.live || state.generation !== generation) return
68
85
  setSession(next)
69
86
  setStatus(next === null ? "unauthenticated" : "authenticated")
70
87
  } catch {
71
88
  // A failed session read must fail closed as anonymous; leaving "loading" can strand the
72
89
  // subtree in an auth-pending state and make protected UI appear available by omission.
90
+ if (!state.live || state.generation !== generation) return
73
91
  setSession(null)
74
92
  setStatus("unauthenticated")
75
93
  }
76
94
  }
77
95
  }, [client])
78
96
  useEffect(() => {
79
- let live = true
97
+ const state = reads.current
98
+ state.live = true
80
99
  if (props.initialSession === undefined) {
81
- void client.getSession().then(
82
- (next) => {
83
- if (!live) return
84
- setSession(next)
85
- setStatus(next === null ? "unauthenticated" : "authenticated")
86
- },
87
- () => {
88
- // Treat an unavailable session endpoint as anonymous rather than keeping auth state
89
- // pending indefinitely or exposing stale authenticated UI.
90
- if (!live) return
91
- setSession(null)
92
- setStatus("unauthenticated")
93
- },
94
- )
100
+ void refresh()
95
101
  }
96
102
  return () => {
97
- live = false
103
+ state.live = false
104
+ state.generation++
98
105
  }
99
- }, [client, props.initialSession])
106
+ }, [refresh, props.initialSession])
100
107
  const value = useMemo<AuthSession>(
101
108
  () => ({
102
109
  status,
@@ -104,7 +111,16 @@ export function AuthSessionProvider(props: AuthSessionProviderProps): ReactNode
104
111
  refresh,
105
112
  signIn: (providerId, options) => client.signIn(providerId, options),
106
113
  signOut: async (options) => {
107
- await client.signOut(options)
114
+ const state = reads.current
115
+ state.signingOut++
116
+ state.generation++
117
+ try {
118
+ await client.signOut(options)
119
+ } finally {
120
+ state.signingOut--
121
+ state.generation++
122
+ }
123
+ if (!state.live) return
108
124
  // The server ended the session: the subtree must not go on showing it, whether a redirect is
109
125
  // pending or `redirect: false` keeps the page.
110
126
  setSession(null)
@@ -124,7 +124,7 @@ export function assertSingleReactCore(
124
124
  rendererReact = realpath(resolve("react", dirnamePortable(reactDomServerPath)))
125
125
  // The `react` the app's route components import - the core they call hooks on.
126
126
  componentsReact = realpath(resolve("react", appRoot()))
127
- // @nifra-gate-reviewed: not a security gate. If either `react` cannot be resolved we cannot prove
127
+ // nifra-expect NF-S001: not a security gate. If either `react` cannot be resolved we cannot prove
128
128
  // a duplicate exists, so we skip the assertion rather than block a valid render.
129
129
  } catch {
130
130
  return
@@ -17,6 +17,7 @@ export function routeProps(
17
17
  const rawSearch = q === -1 ? "" : state.path.slice(q)
18
18
  return {
19
19
  data: state.data,
20
+ ...(state.serverComponents === undefined ? {} : { serverComponents: state.serverComponents }),
20
21
  // Each layout's own loader data. Without it a layout renders `data: null` on the client while the
21
22
  // server rendered it with its data - a hydration mismatch on the first paint.
22
23
  ...(state.layoutData !== undefined ? { layoutData: state.layoutData } : {}),