@nitronjs/framework 0.3.9 → 0.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,90 +1,77 @@
1
- import React from "react";
2
- import { hydrateRoot } from "react-dom/client";
3
- // @ts-ignore — no type declarations for this package
4
- import { createFromReadableStream } from "react-server-dom-webpack/client.browser";
5
-
6
- declare global {
7
- interface Window {
8
- __NITRON_FLIGHT__?: string;
9
- __NITRON_RSC__?: {
10
- root: any;
11
- navigate: (payload: string) => void;
12
- };
13
- }
14
- }
15
-
16
- // Convert Flight payload string to a ReadableStream
17
- function payloadToStream(payload: string): ReadableStream<Uint8Array> {
18
- return new ReadableStream({
19
- start(controller) {
20
- controller.enqueue(new TextEncoder().encode(payload));
21
- controller.close();
22
- }
23
- });
24
- }
25
-
26
- // Navigate to a new page using RSC payload (called by spa.js)
27
- function navigateWithPayload(payload: string) {
28
- const rsc = window.__NITRON_RSC__;
29
-
30
- if (!rsc || !rsc.root) return;
31
-
32
- const stream = payloadToStream(payload);
33
- const response = createFromReadableStream(stream);
34
-
35
- function Root() {
36
- return React.use(response);
37
- }
38
-
39
- rsc.root.render(React.createElement(Root));
40
- }
41
-
42
- async function mount() {
43
- const payload = window.__NITRON_FLIGHT__;
44
-
45
- if (!payload) return;
46
-
47
- const stream = payloadToStream(payload);
48
- const rscResponse = createFromReadableStream(stream);
49
-
50
- // Wait for the RSC response to fully resolve before hydrating.
51
- //
52
- // Without this, hydrateRoot starts rendering AFTER the ReadableStream
53
- // has already closed. React then tries to look up component chunks
54
- // that no longer exist in the stream, causing "Connection closed" errors.
55
- //
56
- // By awaiting here, all component modules are loaded and ready BEFORE
57
- // React starts rendering. This prevents the race condition entirely.
58
- try {
59
- await rscResponse;
60
- }
61
- catch (err) {
62
- console.error('[rsc-consumer] RSC response failed to resolve:', err);
63
- return;
64
- }
65
-
66
- function Root(): React.ReactNode {
67
- return React.use(rscResponse) as React.ReactNode;
68
- }
69
-
70
- const container = document.getElementById("app");
71
-
72
- if (!container) return;
73
-
74
- const root = hydrateRoot(container, React.createElement(Root));
75
-
76
- // Expose RSC functions for SPA navigation
77
- window.__NITRON_RSC__ = {
78
- root,
79
- navigate: navigateWithPayload
80
- };
81
-
82
- delete window.__NITRON_FLIGHT__;
83
- }
84
-
85
- if (document.readyState === "loading") {
86
- document.addEventListener("DOMContentLoaded", mount);
87
- }
88
- else {
89
- mount();
90
- }
1
+ import React from "react";
2
+ import { hydrateRoot } from "react-dom/client";
3
+ // @ts-ignore — no type declarations for this package
4
+ import { createFromReadableStream } from "react-server-dom-webpack/client.browser";
5
+
6
+ declare global {
7
+ interface Window {
8
+ __NITRON_FLIGHT__?: string;
9
+ __NITRON_RSC__?: {
10
+ root: any;
11
+ navigate: (payload: string) => void;
12
+ };
13
+ }
14
+ }
15
+
16
+ function payloadToStream(payload: string): ReadableStream<Uint8Array> {
17
+ return new ReadableStream({
18
+ start(controller) {
19
+ controller.enqueue(new TextEncoder().encode(payload));
20
+ controller.close();
21
+ }
22
+ });
23
+ }
24
+
25
+ let rscResponse: any = null;
26
+
27
+ function Root(): React.ReactNode {
28
+ return React.use(rscResponse) as React.ReactNode;
29
+ }
30
+
31
+ function navigateWithPayload(payload: string) {
32
+ const rsc = window.__NITRON_RSC__;
33
+
34
+ if (!rsc || !rsc.root) return;
35
+
36
+ const stream = payloadToStream(payload);
37
+ rscResponse = createFromReadableStream(stream);
38
+
39
+ rsc.root.render(React.createElement(Root));
40
+ }
41
+
42
+ async function mount() {
43
+ const payload = window.__NITRON_FLIGHT__;
44
+
45
+ if (!payload) return;
46
+
47
+ const stream = payloadToStream(payload);
48
+ rscResponse = createFromReadableStream(stream);
49
+
50
+ try {
51
+ await rscResponse;
52
+ }
53
+ catch (err) {
54
+ console.error('[rsc-consumer] RSC response failed to resolve:', err);
55
+ return;
56
+ }
57
+
58
+ const container = document.getElementById("app");
59
+
60
+ if (!container) return;
61
+
62
+ const root = hydrateRoot(container, React.createElement(Root));
63
+
64
+ window.__NITRON_RSC__ = {
65
+ root,
66
+ navigate: navigateWithPayload
67
+ };
68
+
69
+ delete window.__NITRON_FLIGHT__;
70
+ }
71
+
72
+ if (document.readyState === "loading") {
73
+ document.addEventListener("DOMContentLoaded", mount);
74
+ }
75
+ else {
76
+ mount();
77
+ }
package/lib/View/View.js CHANGED
@@ -731,8 +731,8 @@ class View {
731
731
  runtimeScript += `</script>`;
732
732
 
733
733
  if (hasFlightPayload) {
734
- const escapedPayload = flightPayload.replace(/</g, "\\u003c").replace(/>/g, "\\u003e");
735
- runtimeScript += `<script${nonceAttr}>window.__NITRON_FLIGHT__=${JSON.stringify(escapedPayload)};</script>`;
734
+ const safePayload = JSON.stringify(flightPayload).replace(/</g, "\\u003c").replace(/>/g, "\\u003e");
735
+ runtimeScript += `<script${nonceAttr}>window.__NITRON_FLIGHT__=${safePayload};</script>`;
736
736
  }
737
737
 
738
738
  const refreshScript = this.#isDev
@@ -823,7 +823,7 @@ ${refreshScript}${vendorScript}${hmrScript}${consumerScript}${spaScript}${devInd
823
823
  images: hasWildcard ? ["*"] : urls,
824
824
  scripts: hasWildcard ? ["*"] : urls,
825
825
  connect: hasWildcard ? ["*"] : urls,
826
- frames: hasWildcard ? ["*"] : [],
826
+ frames: hasWildcard ? ["*"] : urls,
827
827
  };
828
828
  }
829
829
  else {
@@ -849,9 +849,9 @@ ${refreshScript}${vendorScript}${hmrScript}${consumerScript}${spaScript}${devInd
849
849
  const imgSrc = buildSrc(["'self'", "data:", "blob:"], whitelist.images);
850
850
  const scriptSrc = buildSrc(["'self'", `'nonce-${nonce}'`], whitelist.scripts);
851
851
  const connectSrcFinal = buildSrc([connectSrc], whitelist.connect);
852
- const frameSrc = whitelist.frames.length
853
- ? (whitelist.frames.includes("*") ? "*" : whitelist.frames.join(" "))
854
- : "'none'";
852
+ const frameSrc = whitelist.frames.length
853
+ ? buildSrc(["'self'"], whitelist.frames)
854
+ : "'self'";
855
855
 
856
856
  const csp = [
857
857
  "default-src 'self'",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitronjs/framework",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "NitronJS is a modern and extensible Node.js MVC framework built on Fastify. It focuses on clean architecture, modular structure, and developer productivity, offering built-in routing, middleware, configuration management, CLI tooling, and native React integration for scalable full-stack applications.",
5
5
  "bin": {
6
6
  "njs": "./cli/njs.js"