ruact 0.0.19 → 0.0.20

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -1
  3. data/CONTRIBUTING.md +10 -3
  4. data/README.md +1 -1
  5. data/lib/generators/ruact/install/templates/AGENTS.md.tt +8 -8
  6. data/lib/generators/ruact/install/templates/initializer.rb.tt +4 -1
  7. data/lib/ruact/configuration.rb +6 -1
  8. data/lib/ruact/controller/delivery.rb +247 -0
  9. data/lib/ruact/controller/document_rendering.rb +15 -12
  10. data/lib/ruact/controller/streaming.rb +241 -0
  11. data/lib/ruact/controller.rb +29 -48
  12. data/lib/ruact/deferred_render.rb +208 -0
  13. data/lib/ruact/doctor.rb +18 -1
  14. data/lib/ruact/erb_preprocessor.rb +203 -22
  15. data/lib/ruact/flight/react_element.rb +17 -2
  16. data/lib/ruact/flight/renderer.rb +91 -41
  17. data/lib/ruact/flight/request.rb +71 -27
  18. data/lib/ruact/flight/serializer.rb +3 -10
  19. data/lib/ruact/html_converter.rb +23 -31
  20. data/lib/ruact/inline_script.rb +24 -0
  21. data/lib/ruact/load_async_note.rb +51 -0
  22. data/lib/ruact/markers.rb +95 -0
  23. data/lib/ruact/navigation_boundary.rb +2 -2
  24. data/lib/ruact/railtie.rb +12 -0
  25. data/lib/ruact/render_context.rb +61 -6
  26. data/lib/ruact/render_pipeline.rb +29 -21
  27. data/lib/ruact/request_kind.rb +36 -0
  28. data/lib/ruact/server.rb +3 -7
  29. data/lib/ruact/testing/flight_extractor.rb +21 -4
  30. data/lib/ruact/version.rb +1 -1
  31. data/lib/ruact/view_helper/suspense.rb +62 -0
  32. data/lib/ruact/view_helper.rb +24 -32
  33. data/lib/ruact.rb +4 -0
  34. data/vendor/javascript/vite-plugin-ruact/bootstrap.test.mjs +16 -3
  35. data/vendor/javascript/vite-plugin-ruact/page-mount.test.mjs +67 -0
  36. data/vendor/javascript/vite-plugin-ruact/page-stream.test.mjs +107 -0
  37. data/vendor/javascript/vite-plugin-ruact/runtime/bootstrap.jsx +23 -18
  38. data/vendor/javascript/vite-plugin-ruact/runtime/dev-overlay.js +39 -0
  39. data/vendor/javascript/vite-plugin-ruact/runtime/page-stream.js +88 -0
  40. data/vendor/javascript/vite-plugin-ruact/runtime/ruact-router.js +11 -17
  41. data/vendor/javascript/vite-plugin-ruact/runtime/suspense-boundary.js +1 -0
  42. metadata +14 -2
@@ -10,9 +10,8 @@
10
10
  * Also handles popstate for browser back/forward.
11
11
  */
12
12
 
13
- import { createFromReadableStream } from "virtual:ruact/flight-client";
14
13
  import { setBoundaryErrorHandler } from "./suspense-boundary.js";
15
- import { labelledStream } from "./transport.js";
14
+ import { openPage } from "./page-stream.js";
16
15
 
17
16
  // Errors already reported once (by a Suspense boundary) that navigate() must
18
17
  // not report again on their way out to a revalidate() caller.
@@ -27,12 +26,16 @@ let _currentAbort = null;
27
26
  * @param {function} opts.onNavigate - called with the new React element tree
28
27
  * @param {function} [opts.onError] - called with an Error when navigation fails,
29
28
  * or when a Suspense child's row is an error
29
+ * @param {function} [opts.onBoundaryError] - called, too, when a Suspense child's
30
+ * row is an error (the bootstrap's development
31
+ * overlay — Story 19-1); not for a navigation
30
32
  */
31
- export function setupRouter({ onNavigate, onError = null }) {
33
+ export function setupRouter({ onNavigate, onError = null, onBoundaryError = null }) {
32
34
  _onNavigate = onNavigate;
33
35
  _onError = onError;
34
36
  setBoundaryErrorHandler((error) => {
35
37
  console.error(error.message);
38
+ onBoundaryError?.(error);
36
39
  _onError?.(error);
37
40
  });
38
41
 
@@ -570,15 +573,11 @@ async function _processFlightResponse(response, {
570
573
  ? (() => { const u = new URL(response.url); return u.pathname + u.search + u.hash; })()
571
574
  : targetUrl;
572
575
 
573
- // Two readers of one body: React's client, and a drain that settles when the
574
- // last row is in — `revalidate()` resolves then, as it always has.
575
- const [forClient, forEnd] = response.body.tee();
576
- const streamEnd = _drain(forEnd);
577
- streamEnd.catch(() => {}); // awaited below; an abort must not be "unhandled" first
578
-
579
576
  // Resolves when row 0 arrives; deferred Suspense rows keep streaming into it.
580
- // A stream that ends without a root rejects (runtime/transport.js labels it).
581
- const root = await createFromReadableStream(labelledStream(forClient));
577
+ // `page.end` settles when the last row is in — `revalidate()` resolves then,
578
+ // as it always has.
579
+ const page = openPage(response.body);
580
+ const root = await page.root;
582
581
 
583
582
  // A newer navigation started while this one was in flight: it owns the page.
584
583
  if (signal?.aborted) return;
@@ -599,7 +598,7 @@ async function _processFlightResponse(response, {
599
598
  if (scroll) window.scrollTo(0, 0);
600
599
 
601
600
  try {
602
- await streamEnd;
601
+ await page.end;
603
602
  } catch (err) {
604
603
  // The page is on screen. A row that never arrived was reported by its
605
604
  // Suspense boundary; a revalidate() caller still learns it failed.
@@ -607,8 +606,3 @@ async function _processFlightResponse(response, {
607
606
  throw err;
608
607
  }
609
608
  }
610
-
611
- async function _drain(stream) {
612
- const reader = stream.getReader();
613
- while (!(await reader.read()).done) { /* the client has its own copy */ }
614
- }
@@ -27,6 +27,7 @@ export function setBoundaryErrorHandler(handler) {
27
27
  // runtime/transport.js labels a response cut short the same way.
28
28
  const DIGEST_MESSAGES = {
29
29
  "ruact:suspense-timeout": "Suspense timeout exceeded",
30
+ "ruact:suspense-error": "An error occurred in a <Suspense>",
30
31
  "ruact:connection-closed": "the response ended before the whole page arrived",
31
32
  };
32
33
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruact
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.19
4
+ version: 0.0.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luiz Garcia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-08 00:00:00.000000000 Z
11
+ date: 2026-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -76,10 +76,13 @@ files:
76
76
  - lib/ruact/component_contract.rb
77
77
  - lib/ruact/configuration.rb
78
78
  - lib/ruact/controller.rb
79
+ - lib/ruact/controller/delivery.rb
79
80
  - lib/ruact/controller/document_rendering.rb
80
81
  - lib/ruact/controller/format_negotiation.rb
81
82
  - lib/ruact/controller/page_rendering.rb
82
83
  - lib/ruact/controller/pages.rb
84
+ - lib/ruact/controller/streaming.rb
85
+ - lib/ruact/deferred_render.rb
83
86
  - lib/ruact/doctor.rb
84
87
  - lib/ruact/erb_preprocessor.rb
85
88
  - lib/ruact/erb_preprocessor_hook.rb
@@ -93,8 +96,11 @@ files:
93
96
  - lib/ruact/flight/serializer.rb
94
97
  - lib/ruact/flight/utf8.rb
95
98
  - lib/ruact/html_converter.rb
99
+ - lib/ruact/inline_script.rb
96
100
  - lib/ruact/layout_source.rb
101
+ - lib/ruact/load_async_note.rb
97
102
  - lib/ruact/manifest_resolver.rb
103
+ - lib/ruact/markers.rb
98
104
  - lib/ruact/navigation_boundary.rb
99
105
  - lib/ruact/packaging.rb
100
106
  - lib/ruact/query.rb
@@ -102,6 +108,7 @@ files:
102
108
  - lib/ruact/redirect_path.rb
103
109
  - lib/ruact/render_context.rb
104
110
  - lib/ruact/render_pipeline.rb
111
+ - lib/ruact/request_kind.rb
105
112
  - lib/ruact/routing.rb
106
113
  - lib/ruact/serializable.rb
107
114
  - lib/ruact/server.rb
@@ -134,6 +141,7 @@ files:
134
141
  - lib/ruact/validation_errors_collector.rb
135
142
  - lib/ruact/version.rb
136
143
  - lib/ruact/view_helper.rb
144
+ - lib/ruact/view_helper/suspense.rb
137
145
  - lib/ruact/views/layouts/ruact.html.erb
138
146
  - lib/rubocop/cop/ruact.rb
139
147
  - lib/rubocop/cop/ruact/no_extend_self.rb
@@ -154,11 +162,15 @@ files:
154
162
  - vendor/javascript/vite-plugin-ruact/manifest-http.test.mjs
155
163
  - vendor/javascript/vite-plugin-ruact/package-lock.json
156
164
  - vendor/javascript/vite-plugin-ruact/package.json
165
+ - vendor/javascript/vite-plugin-ruact/page-mount.test.mjs
166
+ - vendor/javascript/vite-plugin-ruact/page-stream.test.mjs
157
167
  - vendor/javascript/vite-plugin-ruact/registry.test.mjs
158
168
  - vendor/javascript/vite-plugin-ruact/ruact-router.test.mjs
159
169
  - vendor/javascript/vite-plugin-ruact/runtime/bootstrap.jsx
170
+ - vendor/javascript/vite-plugin-ruact/runtime/dev-overlay.js
160
171
  - vendor/javascript/vite-plugin-ruact/runtime/flight-modules.js
161
172
  - vendor/javascript/vite-plugin-ruact/runtime/flight-webpack-globals.js
173
+ - vendor/javascript/vite-plugin-ruact/runtime/page-stream.js
162
174
  - vendor/javascript/vite-plugin-ruact/runtime/root-boundary.js
163
175
  - vendor/javascript/vite-plugin-ruact/runtime/ruact-router.js
164
176
  - vendor/javascript/vite-plugin-ruact/runtime/suspense-boundary.js