@fluojs/react 0.1.0 → 0.2.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.
- package/README.ko.md +629 -16
- package/README.md +644 -25
- package/dist/decorators.d.ts +3 -3
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +10 -4
- package/dist/diagnostics.d.ts +110 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +180 -0
- package/dist/error-representation.d.ts +28 -0
- package/dist/error-representation.d.ts.map +1 -0
- package/dist/error-representation.js +28 -0
- package/dist/experimental/rsc-flight-response.d.ts.map +1 -1
- package/dist/experimental/rsc-flight-response.js +2 -6
- package/dist/experimental/server-functions-server.js +1 -1
- package/dist/experimental/server-functions-types.d.ts +1 -1
- package/dist/experimental/server-functions-types.d.ts.map +1 -1
- package/dist/index.d.ts +16 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/module.d.ts +8 -2
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +31 -8
- package/dist/page-catalog.d.ts +29 -0
- package/dist/page-catalog.d.ts.map +1 -0
- package/dist/page-catalog.js +46 -0
- package/dist/page-metadata.d.ts +58 -0
- package/dist/page-metadata.d.ts.map +1 -0
- package/dist/page-metadata.js +139 -0
- package/dist/page-renderer.d.ts +24 -0
- package/dist/page-renderer.d.ts.map +1 -0
- package/dist/page-renderer.js +18 -0
- package/dist/page-result.d.ts +16 -0
- package/dist/page-result.d.ts.map +1 -0
- package/dist/page-result.js +101 -0
- package/dist/render-diagnostics.d.ts +25 -0
- package/dist/render-diagnostics.d.ts.map +1 -0
- package/dist/render-diagnostics.js +94 -0
- package/dist/render-policy-metadata.d.ts +40 -0
- package/dist/render-policy-metadata.d.ts.map +1 -0
- package/dist/render-policy-metadata.js +136 -0
- package/dist/render-policy.d.ts +87 -0
- package/dist/render-policy.d.ts.map +1 -0
- package/dist/render-policy.js +203 -0
- package/dist/render-stream.d.ts +1 -1
- package/dist/render-stream.d.ts.map +1 -1
- package/dist/render-stream.js +80 -15
- package/dist/render.d.ts +12 -3
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +70 -76
- package/dist/server-entry.d.ts +13 -1
- package/dist/server-entry.d.ts.map +1 -1
- package/dist/server-entry.js +21 -11
- package/dist/typegen-artifact.d.ts +9 -0
- package/dist/typegen-artifact.d.ts.map +1 -0
- package/dist/typegen-artifact.js +116 -0
- package/dist/typegen.d.ts +41 -0
- package/dist/typegen.d.ts.map +1 -0
- package/dist/typegen.js +162 -0
- package/package.json +11 -6
package/README.md
CHANGED
|
@@ -4,19 +4,31 @@
|
|
|
4
4
|
|
|
5
5
|
Runtime-neutral React integration for fluo applications.
|
|
6
6
|
|
|
7
|
+
Preparing for the coordinated Node 24 release? Follow the [consumer migration guide](../../docs/getting-started/migrate-node24.md) before upgrading packages. React remains a `0.x` minor release, not a `1.0` graduation.
|
|
8
|
+
|
|
7
9
|
## Table of Contents
|
|
8
10
|
|
|
9
11
|
- [Installation](#installation)
|
|
10
12
|
- [When to Use](#when-to-use)
|
|
13
|
+
- [Zero-to-First-Page Workflow](#zero-to-first-page-workflow)
|
|
11
14
|
- [Stable SSR Mental Model](#stable-ssr-mental-model)
|
|
15
|
+
- [React User Concept Translation](#react-user-concept-translation)
|
|
12
16
|
- [Runtime and Peer Contract](#runtime-and-peer-contract)
|
|
13
17
|
- [Phase Boundaries](#phase-boundaries)
|
|
14
18
|
- [ReactModule Registration](#reactmodule-registration)
|
|
19
|
+
- [Application Page Renderer](#application-page-renderer)
|
|
20
|
+
- [Render Policy Decorators](#render-policy-decorators)
|
|
21
|
+
- [SSR Diagnostic Phases](#ssr-diagnostic-phases)
|
|
22
|
+
- [HTTP Error Documents](#http-error-documents)
|
|
15
23
|
- [Router and Path Decorators](#router-and-path-decorators)
|
|
24
|
+
- [Bootstrap-Resolved Page Catalog](#bootstrap-resolved-page-catalog)
|
|
25
|
+
- [Path-Only Page Type Generation](#path-only-page-type-generation)
|
|
26
|
+
- [Consumer Testing Loop](#consumer-testing-loop)
|
|
16
27
|
- [Web Streams SSR](#web-streams-ssr)
|
|
17
28
|
- [Hydration Asset Contract](#hydration-asset-contract)
|
|
18
29
|
- [Vite Asset Manifest Integration](#vite-asset-manifest-integration)
|
|
19
30
|
- [Client Navigation Runtime](#client-navigation-runtime)
|
|
31
|
+
- [Native Form Mutations](#native-form-mutations)
|
|
20
32
|
- [Experimental RSC Prototype](#experimental-rsc-prototype)
|
|
21
33
|
- [Experimental Server Functions](#experimental-server-functions)
|
|
22
34
|
- [RSC Graduation Policy](#rsc-graduation-policy)
|
|
@@ -27,9 +39,9 @@ Runtime-neutral React integration for fluo applications.
|
|
|
27
39
|
|
|
28
40
|
## Installation
|
|
29
41
|
|
|
30
|
-
The
|
|
31
|
-
|
|
32
|
-
release workflow.
|
|
42
|
+
The package has completed its initial `0.1.0` release and no longer uses the `0.0.0` bootstrap
|
|
43
|
+
placeholder. Future versions are recorded through committed Changesets and published only through
|
|
44
|
+
the canonical release workflow.
|
|
33
45
|
|
|
34
46
|
When the package is published, install it with React and React DOM as peers:
|
|
35
47
|
|
|
@@ -46,6 +58,36 @@ places React routers into ordinary module controller metadata, and `@Router(...)
|
|
|
46
58
|
DTO binding, versioning, guards, interceptors, headers, route validation, matching, and dispatch
|
|
47
59
|
continue to use the HTTP runtime contracts.
|
|
48
60
|
|
|
61
|
+
## Zero-to-First-Page Workflow
|
|
62
|
+
|
|
63
|
+
Use the official generated composition when you want streamed SSR, hydration, and Vite assets
|
|
64
|
+
without assembling every stable seam before editing the first page:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
fluo new my-react-app --starter react-vite-ssr
|
|
68
|
+
cd my-react-app
|
|
69
|
+
pnpm dev
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Open `/products/sku-42?preview=true` and edit `src/page.tsx`. The explicit `@Router(...)` / `@Path(...)`
|
|
73
|
+
handler remains in `src/app.ts` and returns `createElement(ProductPage)`, one `ReactElement`, so `@fluojs/http` still
|
|
74
|
+
owns matching, DTO binding and validation, middleware, guards, interceptors, request scopes, and
|
|
75
|
+
not-found behavior.
|
|
76
|
+
|
|
77
|
+
The generated wiring moves the incidental first-edit work into discoverable application files:
|
|
78
|
+
|
|
79
|
+
- `src/entry-server.tsx` owns the replaceable `ReactPageRenderer`, parses the application-loaded
|
|
80
|
+
manifest with `@fluojs/react/vite`, and returns `ReactServerEntry` with
|
|
81
|
+
`createReactServerEntry(...)`.
|
|
82
|
+
- `src/react-app.tsx` gives server rendering and hydration one document,
|
|
83
|
+
`ReactClientRouterProvider`, route snapshot, and stylesheet composition.
|
|
84
|
+
- `src/entry-client.tsx` hydrates that same tree, while `src/main.ts` and `src/load-manifest.ts` keep
|
|
85
|
+
filesystem loading and actionable build-output failures at the Node.js application boundary.
|
|
86
|
+
|
|
87
|
+
Advanced applications can replace the generated renderer or pass explicit hydration options to
|
|
88
|
+
`createReactServerEntry(...)`; the stable APIs below remain available. No runtime-neutral root export
|
|
89
|
+
imports Node.js, Vite, or browser code, and the starter does not add another route matcher.
|
|
90
|
+
|
|
49
91
|
## Stable SSR Mental Model
|
|
50
92
|
|
|
51
93
|
The stable `0.1.0` model is HTTP-first React SSR. `@Router(...)` and `@Path(...)` are lexical
|
|
@@ -58,8 +100,18 @@ validation, guards, interceptors, headers, module middleware, request scopes, an
|
|
|
58
100
|
This package is therefore **not** a Next.js App Router clone, React Server Components framework,
|
|
59
101
|
TanStack route tree, Angular `Routes[]` table, file-route scanner, or primary React-owned
|
|
60
102
|
`routes: []` configuration model. Treat React routers as page-shaped HTTP handlers. Route discovery
|
|
61
|
-
and dispatch stay in the existing fluo module/controller pipeline
|
|
62
|
-
|
|
103
|
+
and dispatch stay in the existing fluo module/controller pipeline. Page handlers may return ordinary
|
|
104
|
+
HTTP values, return one valid `ReactElement` for the configured application page renderer, or return
|
|
105
|
+
`createReactServerEntry(...)` explicitly when they need per-route SSR options.
|
|
106
|
+
|
|
107
|
+
## React User Concept Translation
|
|
108
|
+
|
|
109
|
+
If your starting vocabulary is pages, layouts, loaders, actions, navigation, pending UI, metadata,
|
|
110
|
+
hydration, or build assets, use the concise
|
|
111
|
+
[React concepts in fluo guide](../../docs/guides/react-user-concepts.md). It separates shipped
|
|
112
|
+
equivalents, intentionally different behavior, experimental surfaces, and unsupported concepts,
|
|
113
|
+
then follows one runnable path from `@Router(...)`/`@Path(...)` through the application renderer,
|
|
114
|
+
Vite assets, hydration, client navigation, and a native form mutation.
|
|
63
115
|
|
|
64
116
|
## Runtime and Peer Contract
|
|
65
117
|
|
|
@@ -133,8 +185,276 @@ sources, so `createHandlerMapping(...)` and `Dispatcher` still detect duplicate
|
|
|
133
185
|
module-level middleware, create request scopes, run guards and interceptors, and resolve route
|
|
134
186
|
versioning.
|
|
135
187
|
|
|
188
|
+
## Application Page Renderer
|
|
189
|
+
|
|
190
|
+
Register one application-owned `renderPage` callback when every page should share the same document
|
|
191
|
+
shell, providers, hydration assets, route snapshot wiring, and recoverable-render policy. The callback
|
|
192
|
+
implements `ReactPageRenderer`, receives one `ReactElement` plus the active `ReactRenderContext`, and
|
|
193
|
+
must return `ReactServerEntry`. `ReactModule.forRoot(...)` registers and exports it through
|
|
194
|
+
`REACT_PAGE_RENDERER`, so routers can inject the callback without creating a second response path.
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
import { Module } from '@fluojs/core';
|
|
198
|
+
import {
|
|
199
|
+
Path,
|
|
200
|
+
ReactModule,
|
|
201
|
+
Router,
|
|
202
|
+
createReactServerEntry,
|
|
203
|
+
type ReactPageRenderer,
|
|
204
|
+
type ReactRenderContext,
|
|
205
|
+
} from '@fluojs/react';
|
|
206
|
+
import {
|
|
207
|
+
ReactClientRouterProvider,
|
|
208
|
+
createReactRouteSnapshot,
|
|
209
|
+
} from '@fluojs/react/client';
|
|
210
|
+
|
|
211
|
+
const hydrationOptions = {
|
|
212
|
+
assetMap: { 'client.js': '/assets/client.123.js' },
|
|
213
|
+
bootstrapModules: ['/assets/client.123.js'],
|
|
214
|
+
} as const;
|
|
215
|
+
|
|
216
|
+
const renderPage: ReactPageRenderer = (page, context) => {
|
|
217
|
+
const initialSnapshot = createReactRouteSnapshot({
|
|
218
|
+
params: context.request.params,
|
|
219
|
+
url: context.request.url,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return createReactServerEntry(
|
|
223
|
+
<html lang="en">
|
|
224
|
+
<body>
|
|
225
|
+
<ReactClientRouterProvider initialSnapshot={initialSnapshot}>
|
|
226
|
+
{page}
|
|
227
|
+
</ReactClientRouterProvider>
|
|
228
|
+
</body>
|
|
229
|
+
</html>,
|
|
230
|
+
hydrationOptions,
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
@Router('/products')
|
|
235
|
+
class ProductRouter {
|
|
236
|
+
@Path('/:id')
|
|
237
|
+
show(_input: undefined, context: ReactRenderContext) {
|
|
238
|
+
return <main>Product {context.request.params.id}</main>;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
@Module({
|
|
243
|
+
imports: [ReactModule.forRoot({ controllers: [ProductRouter], renderPage })],
|
|
244
|
+
})
|
|
245
|
+
class AppModule {}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The root package does not import `@fluojs/react/client` or `@fluojs/react/vite`. Applications may
|
|
249
|
+
compose client helpers explicitly as above and may close over hydration options produced from an
|
|
250
|
+
already-loaded manifest by `createReactViteAssetManifest(...)`. The renderer does not discover
|
|
251
|
+
manifests, generate bundles, match routes, or bypass DTO binding, middleware, guards, interceptors,
|
|
252
|
+
request scopes, abort propagation, shell failure handling, or recoverable streaming behavior.
|
|
253
|
+
|
|
254
|
+
When `renderPage` is configured, the final value from an `@Path(...)` request is passed to that
|
|
255
|
+
callback only when React `isValidElement(...)` recognizes it as one `ReactElement`. Plain objects,
|
|
256
|
+
strings, arrays, `null`, and other ordinary values keep the normal HTTP response path. Explicit
|
|
257
|
+
`ReactServerEntry` values remain unchanged and do not pass through the configured callback. The
|
|
258
|
+
`REACT_PAGE_RENDERER` token remains available when another application provider needs to invoke the
|
|
259
|
+
same renderer explicitly.
|
|
260
|
+
|
|
261
|
+
Returning a `ReactElement` without configuring `renderPage` fails before response commit with
|
|
262
|
+
`ReactSsrDiagnosticError`, code `react-ssr-missing-page-renderer`, and an actionable message. Configure
|
|
263
|
+
`ReactModule.forRoot({ ..., renderPage })` or return `createReactServerEntry(...)` explicitly.
|
|
264
|
+
|
|
265
|
+
## Render Policy Decorators
|
|
266
|
+
|
|
267
|
+
Use `@PageLayout(...)`, `@SuspenseFallback(...)`, and `@PageMetadata(...)` when one React router
|
|
268
|
+
class or `@Path(...)` method needs route-local composition while the application keeps one
|
|
269
|
+
`renderPage` callback. Layout and fallback decorators accept component references, not pre-created
|
|
270
|
+
JSX elements. `PageMetadata` accepts a synchronous request-aware factory. Their metadata is resolved
|
|
271
|
+
after HTTP matching and passed only to the application page renderer; it never changes paths,
|
|
272
|
+
matching precedence, params, errors, or not-found behavior.
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
import { Suspense, createElement, type ReactElement } from 'react';
|
|
276
|
+
import {
|
|
277
|
+
PageLayout,
|
|
278
|
+
PageMetadata,
|
|
279
|
+
Path,
|
|
280
|
+
ReactModule,
|
|
281
|
+
Router,
|
|
282
|
+
SuspenseFallback,
|
|
283
|
+
createReactPageMetadataElements,
|
|
284
|
+
createReactServerEntry,
|
|
285
|
+
resolveReactPageMetadata,
|
|
286
|
+
type ReactPageLayoutProps,
|
|
287
|
+
type ReactPageRenderer,
|
|
288
|
+
type ReactSuspenseFallbackProps,
|
|
289
|
+
} from '@fluojs/react';
|
|
290
|
+
|
|
291
|
+
function ShopLayout({ children, context }: ReactPageLayoutProps) {
|
|
292
|
+
return <section data-request-path={context.request.path}>{children}</section>;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function ProductFallback({ context }: ReactSuspenseFallbackProps) {
|
|
296
|
+
return <p>Loading {context.request.params.id}…</p>;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const renderPage: ReactPageRenderer = (page, context, policies) => {
|
|
300
|
+
const metadata = resolveReactPageMetadata(policies, context);
|
|
301
|
+
const pageBoundary = policies.suspenseFallback === undefined
|
|
302
|
+
? page
|
|
303
|
+
: createElement(Suspense, {
|
|
304
|
+
fallback: createElement(policies.suspenseFallback, { context }),
|
|
305
|
+
}, page);
|
|
306
|
+
const composedPage = policies.layouts.reduceRight<ReactElement>(
|
|
307
|
+
(children, Layout) => createElement(Layout, { children, context }),
|
|
308
|
+
pageBoundary,
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
return createReactServerEntry(
|
|
312
|
+
<html lang="en">
|
|
313
|
+
<head>{createReactPageMetadataElements(metadata)}</head>
|
|
314
|
+
<body>{composedPage}</body>
|
|
315
|
+
</html>,
|
|
316
|
+
);
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
@PageLayout(ShopLayout)
|
|
320
|
+
@Router('/products')
|
|
321
|
+
class ProductRouter {
|
|
322
|
+
@PageMetadata(({ request }) => ({
|
|
323
|
+
meta: [{ content: `Product ${request.params.id}`, name: 'description' }],
|
|
324
|
+
title: `Product ${request.params.id}`,
|
|
325
|
+
}))
|
|
326
|
+
@SuspenseFallback(ProductFallback)
|
|
327
|
+
@Path('/:id')
|
|
328
|
+
show() {
|
|
329
|
+
return <ProductPage />;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
ReactModule.forRoot({ controllers: [ProductRouter], renderPage });
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Resolved layouts are ordered outermost to innermost: base class, derived class, base method, then
|
|
337
|
+
derived method. Layouts compose across inheritance. The nearest fallback wins, so a method fallback
|
|
338
|
+
overrides a class fallback and a derived declaration overrides a base declaration. A class or method
|
|
339
|
+
site may declare each policy kind once; same-site duplicates fail during bootstrap.
|
|
340
|
+
|
|
341
|
+
Metadata factories use the same broad-to-specific class and method order. The nearest defined title
|
|
342
|
+
wins. Later `<meta>` descriptors replace earlier descriptors with the same `name` or `property`
|
|
343
|
+
identity, and later `<link>` descriptors replace the same exact `rel` plus `href` pair. Unrelated
|
|
344
|
+
descriptors preserve declaration order. `resolveReactPageMetadata(...)` returns a frozen bounded
|
|
345
|
+
snapshot, and `createReactPageMetadataElements(...)` creates ordinary React title, meta, and link
|
|
346
|
+
elements so React owns text and attribute escaping. Neither helper discovers Vite assets, creates
|
|
347
|
+
script tags, accepts raw HTML, or serializes arbitrary inline data.
|
|
348
|
+
|
|
349
|
+
`ReactRenderContext` includes the active request-scope `container`. Policy components receive that
|
|
350
|
+
context as an explicit prop, but fluo does not instantiate React components through DI or resolve
|
|
351
|
+
tokens on their behalf. A policy without `renderPage`, a class policy outside `@Router(...)`, or a
|
|
352
|
+
method policy outside `@Path(...)` fails bootstrap with `ReactRenderPolicyConfigurationError` and a
|
|
353
|
+
stable value from `REACT_RENDER_POLICY_DIAGNOSTIC_CODES`.
|
|
354
|
+
|
|
355
|
+
Metadata factories receive `ReactPageMetadataContext`: the active `request`, optional `requestId`,
|
|
356
|
+
and the same request-scope `container` identity as the handler and renderer. The context intentionally
|
|
357
|
+
omits `response`, so metadata cannot change status, headers, or commit timing. fluo does not instantiate
|
|
358
|
+
factories through DI or resolve tokens for them. Load asynchronous data through the matched HTTP
|
|
359
|
+
handler before page composition; metadata factories remain synchronous.
|
|
360
|
+
|
|
361
|
+
`@SuspenseFallback(...)` supplies an ordinary React Suspense fallback for descendants that suspend
|
|
362
|
+
during SSR. It does not observe handler `await`, effects, event handlers, native form submission, or
|
|
363
|
+
full-document/client navigation pending state. HTTP pipeline errors, not-found/404 responses,
|
|
364
|
+
pre-commit shell failures, request aborts, and post-shell recoverable errors keep their separate
|
|
365
|
+
existing phases. The complete ordering, inheritance, duplicate, and phase decision is recorded in
|
|
366
|
+
the [React render policy decorator decision](../../docs/architecture/react-render-policy-decorators.md).
|
|
367
|
+
The follow-up [React page render policy decision](../../docs/architecture/react-page-render-policies.md)
|
|
368
|
+
accepts metadata but rejects a generic error-presentation decorator and page-local not-found
|
|
369
|
+
presentation. HTTP pipeline failures, shell failures, post-shell recoverable errors, request aborts,
|
|
370
|
+
client React errors, unmatched routes, and handler-thrown `NotFoundException` outcomes retain their
|
|
371
|
+
existing owners. No placeholder error or not-found export is provided.
|
|
372
|
+
|
|
373
|
+
## SSR Diagnostic Phases
|
|
374
|
+
|
|
375
|
+
Register `onDiagnostic` on `ReactModule.forRoot(...)` when application logging or diagnostics tooling
|
|
376
|
+
needs one stable event shape across HTTP and React rendering boundaries:
|
|
377
|
+
|
|
378
|
+
```tsx
|
|
379
|
+
ReactModule.forRoot({
|
|
380
|
+
controllers: [ProductRouter],
|
|
381
|
+
renderPage,
|
|
382
|
+
onDiagnostic(diagnostic) {
|
|
383
|
+
applicationDiagnostics.report(diagnostic);
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Each `ReactSsrDiagnostic` contains `code`, `phase`, the original `error`, the active `request`, and an
|
|
389
|
+
optional `requestId`. The callback is observational: an exception thrown by it does not replace the
|
|
390
|
+
request outcome. Stable phases and codes are:
|
|
391
|
+
|
|
392
|
+
| Phase | Code | Boundary |
|
|
393
|
+
| --- | --- | --- |
|
|
394
|
+
| `http-pipeline` | `react-ssr-http-pipeline-failure` | DTO binding, middleware, guards, interceptors, handlers, and other failures before React shell rendering. |
|
|
395
|
+
| `http-pipeline` | `react-ssr-missing-page-renderer` | A valid `ReactElement` reached an `@Path(...)` response without `renderPage` configuration. |
|
|
396
|
+
| `pre-commit-shell` | `react-ssr-pre-commit-shell-failure` | React shell creation or buffered stream collection failed before response commit. The original thrown `Error` identity is preserved. |
|
|
397
|
+
| `request-abort` | `react-ssr-request-abort` | The request signal or abort probe stopped React rendering; existing no-commit or committed-stream abort behavior remains unchanged. |
|
|
398
|
+
| `post-shell-recoverable` | `react-ssr-post-shell-recoverable-error` | React reported a recoverable render error after a shell could be written; status and headers are not rewritten. |
|
|
399
|
+
|
|
400
|
+
`REACT_SSR_DIAGNOSTIC_PHASES` and `REACT_SSR_DIAGNOSTIC_CODES` expose these values for comparisons.
|
|
401
|
+
The existing `onRecoverableError` entry hook also receives `code` and `phase` in
|
|
402
|
+
`ReactRecoverableErrorContext`.
|
|
403
|
+
|
|
404
|
+
## HTTP Error Documents
|
|
405
|
+
|
|
406
|
+
Use `createReactErrorRepresentationProvider(...)` when the application wants React to produce the
|
|
407
|
+
optional HTML bytes selected by the HTTP error representation contract:
|
|
408
|
+
|
|
409
|
+
```tsx
|
|
410
|
+
import { bootstrapApplication } from '@fluojs/runtime';
|
|
411
|
+
import {
|
|
412
|
+
createReactErrorRepresentationProvider,
|
|
413
|
+
createReactServerEntry,
|
|
414
|
+
} from '@fluojs/react';
|
|
415
|
+
|
|
416
|
+
const html = createReactErrorRepresentationProvider({
|
|
417
|
+
renderDocument({ json }) {
|
|
418
|
+
return createReactServerEntry(
|
|
419
|
+
<html lang="en">
|
|
420
|
+
<body><main>{json.error.status}: {json.error.message}</main></body>
|
|
421
|
+
</html>,
|
|
422
|
+
);
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const app = await bootstrapApplication({
|
|
427
|
+
errorRepresentation: { html },
|
|
428
|
+
rootModule: AppModule,
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
`@fluojs/http` performs route-miss conversion and `Accept` negotiation before this adapter runs.
|
|
433
|
+
The callback receives `HttpErrorRepresentationContext`, including canonical JSON and the active
|
|
434
|
+
request-scope container. It does not receive response mutation authority. The React entry is fully
|
|
435
|
+
buffered before HTTP applies status, `Content-Type`, `Vary`, `HEAD` suppression, and commit.
|
|
436
|
+
|
|
437
|
+
React escapes `json.error.message` in the example because it is a JSX text child. The application
|
|
438
|
+
still owns the complete trusted-HTML document contract: do not move request-derived or error-derived
|
|
439
|
+
content into `dangerouslySetInnerHTML` unless it has passed an application-approved sanitizer or
|
|
440
|
+
equivalent trusted-content boundary.
|
|
441
|
+
|
|
442
|
+
`ReactServerEntry.status` and `ReactServerEntry.headers` are ignored on this path. The helper never
|
|
443
|
+
calls `ReactPageRenderer`, `PageLayout`, `PageMetadata`, `SuspenseFallback`, or the page catalog, and
|
|
444
|
+
it never matches a URL. A render failure propagates to HTTP's one-shot canonical JSON fallback;
|
|
445
|
+
matched-page pre-commit shell failures remain a separate React SSR diagnostic phase and do not invoke
|
|
446
|
+
the provider. See the
|
|
447
|
+
[HTTP error representation decision](../../docs/architecture/http-error-representations.md).
|
|
448
|
+
|
|
136
449
|
## Router and Path Decorators
|
|
137
450
|
|
|
451
|
+
`@Path()` and `@Path(undefined)` use `''`, so `@Router('/cats')` serves GET `/cats`
|
|
452
|
+
and `@Router()` serves GET `/`. An explicit `'/'` has the same effective route but a
|
|
453
|
+
different raw path and never escapes the prefix. Omitted options leave the React metadata's
|
|
454
|
+
`options` property absent; `Path(undefined, options)` still records explicit options.
|
|
455
|
+
HTTP, React, and inspection metadata keep their existing contracts, including route conflicts
|
|
456
|
+
and grammar rejection. `PageLayout`, `SuspenseFallback`, and `PageMetadata` still require values.
|
|
457
|
+
|
|
138
458
|
`@Router(basePath)` marks a class as a React router and writes HTTP controller metadata equivalent
|
|
139
459
|
to `@Controller(basePath)`. It also stores React router marker metadata readable through
|
|
140
460
|
`getReactRouterMetadata(...)` for diagnostics and future rendering integration.
|
|
@@ -176,12 +496,155 @@ The [HTTP catch-all route grammar decision](../../docs/architecture/http-catch-a
|
|
|
176
496
|
defers wildcard adoption. React does not add its own syntax: page handlers should keep explicit
|
|
177
497
|
server routes, and any future catch-all must first become an approved `@fluojs/http` contract.
|
|
178
498
|
|
|
499
|
+
## Bootstrap-Resolved Page Catalog
|
|
500
|
+
|
|
501
|
+
Use `createReactPageCatalog(...)` after HTTP handler compilation when tooling needs a read-only list
|
|
502
|
+
of React pages. Pass the authoritative `HandlerDescriptor[]` from `createHandlerMapping(...)` or
|
|
503
|
+
`app.dispatcher.describeRoutes()`. The result contains only handlers marked by both `@Router(...)`
|
|
504
|
+
and `@Path(...)`, in descriptor registration order.
|
|
505
|
+
|
|
506
|
+
```ts
|
|
507
|
+
import { createHandlerMapping } from '@fluojs/http';
|
|
508
|
+
import { createReactPageCatalog } from '@fluojs/react';
|
|
509
|
+
|
|
510
|
+
const mapping = createHandlerMapping([{ controllerToken: ProductRouter }]);
|
|
511
|
+
const pages = createReactPageCatalog(mapping.descriptors);
|
|
512
|
+
|
|
513
|
+
console.log(pages[0]);
|
|
514
|
+
// {
|
|
515
|
+
// kind: 'react-page', method: 'GET', path: '/v2/products/:productId',
|
|
516
|
+
// version: '2', params: ['productId'], router: 'ProductRouter', handler: 'show'
|
|
517
|
+
// }
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
`getReactRouterMetadata(...)` and `getReactPathMetadata(...)` expose static values authored at the
|
|
521
|
+
decorator sites, such as the router base path, relative page path, and React options. The catalog is
|
|
522
|
+
different: its method, path, version, params, module, router, and handler fields are projected from
|
|
523
|
+
bootstrap-resolved HTTP descriptors, so controller composition and URI versioning are already
|
|
524
|
+
reflected. The returned array, entries, and `params` arrays are frozen defensive snapshots.
|
|
525
|
+
|
|
526
|
+
Catalog construction is observational only. It never calls or replaces the HTTP matcher and has no
|
|
527
|
+
role in route conflict detection, request dispatch, not-found behavior, or non-React handlers. It
|
|
528
|
+
does not create a route tree, client manifest, relative-route model, prefetch layer, or cache.
|
|
529
|
+
|
|
530
|
+
## Path-Only Page Type Generation
|
|
531
|
+
|
|
532
|
+
Use `fluo typegen` to bootstrap an application module, project its compiled React page catalog, and
|
|
533
|
+
write application-owned TypeScript route helpers. The output comes from the dedicated
|
|
534
|
+
`@fluojs/react/typegen` subpath; the runtime-neutral package root does not export the generator.
|
|
535
|
+
|
|
536
|
+
```bash
|
|
537
|
+
fluo typegen ./src/app.ts --output ./src/generated/react-pages.ts
|
|
538
|
+
fluo typegen ./src/admin.ts --export AdminModule --output ./src/generated/admin-pages.ts
|
|
539
|
+
fluo typegen ./src/app.ts --output ./src/generated/react-pages.ts --check
|
|
540
|
+
fluo typegen ./src/app.ts --output ./src/generated/react-pages.ts --watch
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
The command creates the output directory when needed, overwrites stale output deterministically,
|
|
544
|
+
and reports `CREATE`, `UPDATE`, or `UNCHANGED`. Atomic replacement prevents partial publication and
|
|
545
|
+
byte-identical runs do not rewrite the file. `--check` compares exact generated bytes without writes
|
|
546
|
+
and classifies a structurally corrupted current-version body as malformed rather than stale.
|
|
547
|
+
`--watch` installs its application-module watcher before startup generation, reruns once for changes
|
|
548
|
+
observed during that generation before reporting ready, and then serializes coalesced changes while
|
|
549
|
+
preserving the last valid artifact after generation failures. Native `.js` and `.mjs` dependencies
|
|
550
|
+
are evaluated from a current module graph for each generation. Every run closes its bootstrapped
|
|
551
|
+
application after reading `app.dispatcher.describeRoutes()`. Import the generated artifact from
|
|
552
|
+
application code:
|
|
553
|
+
|
|
554
|
+
```ts
|
|
555
|
+
import {
|
|
556
|
+
reactPageRoutes,
|
|
557
|
+
type ReactPageParams,
|
|
558
|
+
type ReactPagePath,
|
|
559
|
+
type ReactPageRouteId,
|
|
560
|
+
} from './generated/react-pages.js';
|
|
561
|
+
|
|
562
|
+
const productHref = reactPageRoutes['GET /products/:productId ProductRouter show'].href({
|
|
563
|
+
productId: 'desk/chair',
|
|
564
|
+
});
|
|
565
|
+
// /products/desk%2Fchair
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
Generated route ids use the stable catalog `id`. Static builders accept no parameters; dynamic
|
|
569
|
+
builders require every catalog path parameter and encode each value with `encodeURIComponent(...)`.
|
|
570
|
+
The artifact also exports `ReactPagePathById`, `ReactPageParamsById`, `ReactPagePath<RouteId>`,
|
|
571
|
+
`ReactPageParams<RouteId>`, `ReactPageRoute`, `ReactPageLinkProps`, and `ReactPageNavigator`.
|
|
572
|
+
|
|
573
|
+
Each generated route also binds the same href builder to declarative `Link` and programmatic
|
|
574
|
+
`push`/`replace` authoring. `link(...)` returns an ordinary `{ href: string }` object for the existing
|
|
575
|
+
real-anchor `Link`; `push(router, ...)` and `replace(router, ...)` resolve the same absolute href before
|
|
576
|
+
calling the existing `ReactRouter` string overload. Route identity and exact params therefore remain
|
|
577
|
+
visible at the callsite without a second runtime route table:
|
|
578
|
+
|
|
579
|
+
```tsx
|
|
580
|
+
import { Link, useRouter } from '@fluojs/react/client';
|
|
581
|
+
import { reactPageRoutes } from './generated/react-pages.js';
|
|
582
|
+
|
|
583
|
+
const productsRoute = reactPageRoutes['GET /products ProductRouter index'];
|
|
584
|
+
const productRoute = reactPageRoutes['GET /products/:productId ProductRouter show'];
|
|
585
|
+
|
|
586
|
+
function ProductNavigation({ productId }: { readonly productId: string }) {
|
|
587
|
+
const router = useRouter();
|
|
588
|
+
|
|
589
|
+
return (
|
|
590
|
+
<nav>
|
|
591
|
+
<Link {...productsRoute.link()}>Products</Link>
|
|
592
|
+
<Link {...productRoute.link({ productId })}>Current product</Link>
|
|
593
|
+
<button type="button" onClick={() => productRoute.push(router, { productId })}>Open</button>
|
|
594
|
+
<button type="button" onClick={() => productRoute.replace(router, { productId })}>Replace</button>
|
|
595
|
+
</nav>
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
Static `link`, `push`, and `replace` methods accept no params; parameterized methods require every
|
|
601
|
+
path param and reject missing or extra keys. The existing generated `href(...)` builders,
|
|
602
|
+
`<Link href={stringOrUrl}>`, and `router.push(...)` / `router.replace(...)` string or `URL` calls remain
|
|
603
|
+
supported. Generated methods only produce or pass absolute href strings into those existing APIs, so
|
|
604
|
+
real-anchor fallback, full-document HTTP navigation, matching, DTO binding, guards, interceptors, and
|
|
605
|
+
not-found behavior keep their current owners.
|
|
606
|
+
|
|
607
|
+
This contract is deliberately path-only. It does not generate query strings, fragments, relative
|
|
608
|
+
routes, optional parameters, or a client route tree. Typegen rejects every catalog entry with a
|
|
609
|
+
`version` because the compiled catalog does not identify whether version selection came from the
|
|
610
|
+
URI, a header, media type, or a custom strategy; emitting one absolute href would otherwise claim a
|
|
611
|
+
URL contract that may be false.
|
|
612
|
+
|
|
613
|
+
## Consumer Testing Loop
|
|
614
|
+
|
|
615
|
+
Use existing React, HTTP, testing, TypeScript, and browser seams as one layered consumer loop. No
|
|
616
|
+
React-specific testing helper is needed because the repeated setup is already isolated by ordinary
|
|
617
|
+
fixtures and the real dispatcher remains the subject of request tests.
|
|
618
|
+
|
|
619
|
+
1. **Render-policy units**: call pure policy and metadata helpers directly. Cover broad-to-specific
|
|
620
|
+
layout order, nearest fallback or metadata precedence, and configuration diagnostics without
|
|
621
|
+
bootstrapping a browser.
|
|
622
|
+
2. **Request dispatch**: build the authored module with `createTestApp({ rootModule })`, request the
|
|
623
|
+
explicit `@Path(...)` route through `app.request(...).send()`, and close the app in `finally`.
|
|
624
|
+
Include a direct `ReactElement` return and the missing-renderer diagnostic so the real HTTP
|
|
625
|
+
dispatcher, request scope, response ownership, and application renderer stay covered.
|
|
626
|
+
3. **Generated-route compilation**: generate from the bootstrapped `HandlerDescriptor` catalog, run
|
|
627
|
+
TypeScript over positive and negative consumer fixtures, and run `fluo typegen ... --check` in CI.
|
|
628
|
+
Positive fixtures use known route ids and complete params; negative fixtures cover unknown ids,
|
|
629
|
+
missing or extra params, and stale, malformed, missing, or unsupported-version artifacts.
|
|
630
|
+
4. **Hydration**: render the same application component on the server and hydrate it with
|
|
631
|
+
`hydrateRoot(...)`. The positive fixture expects interaction with no diagnostics. A deliberately
|
|
632
|
+
mismatched fixture captures `onRecoverableError` so mismatch reporting itself cannot regress.
|
|
633
|
+
5. **Production browser and progressive enhancement**: run Playwright against built assets, fail on
|
|
634
|
+
console/page errors, and use a separate `javaScriptEnabled: false` context to submit the native
|
|
635
|
+
form through the ordinary `POST` → `303` → `GET` flow.
|
|
636
|
+
|
|
637
|
+
Executable references are `src/render-policy.test.ts`, `src/direct-page-return.test.ts`,
|
|
638
|
+
`../cli/src/commands/typegen-navigation.test.ts`, `../../examples/react-vite-ssr/src/app.test.ts`,
|
|
639
|
+
`../../examples/react-vite-ssr/src/hydration.test.ts`, and
|
|
640
|
+
`../../examples/react-vite-ssr/tests/production-hydration.spec.ts`.
|
|
641
|
+
|
|
179
642
|
## Web Streams SSR
|
|
180
643
|
|
|
181
|
-
Return `
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
finalizes the HTML response.
|
|
644
|
+
Return one `ReactElement` through a configured application page renderer, or return
|
|
645
|
+
`createReactServerEntry(...)` explicitly, to stream HTML through the existing fluo HTTP dispatcher.
|
|
646
|
+
Guards, interceptors, module middleware, route headers, `@HttpCode(...)`, DTO binding, request scopes,
|
|
647
|
+
and duplicate route detection all run before `renderReactResponse(...)` finalizes the HTML response.
|
|
185
648
|
|
|
186
649
|
```tsx
|
|
187
650
|
import { HttpCode, RequestDto, FromPath } from '@fluojs/http';
|
|
@@ -216,6 +679,9 @@ when an adapter provides one, and throws shell render failures before response b
|
|
|
216
679
|
Recoverable Suspense errors are reported through `onRecoverableError` and do not rewrite an already
|
|
217
680
|
committed status. Call `renderReactResponse(entry, requestContext)` directly only when a custom
|
|
218
681
|
handler needs to finalize the response itself instead of returning the entry to the dispatcher.
|
|
682
|
+
On streaming hosts, an early response-sink close or a failed `write(...)` / `waitForDrain()` cancels
|
|
683
|
+
the unfinished React reader exactly once and releases its lock. Sink failures remain the reported
|
|
684
|
+
failure rather than being replaced by reader-cancellation cleanup.
|
|
219
685
|
|
|
220
686
|
## Hydration Asset Contract
|
|
221
687
|
|
|
@@ -415,6 +881,85 @@ matches an explicit `@Path(...)`/HTTP route or returns its normal not-found resp
|
|
|
415
881
|
deployment-level document rewrite may be configured separately, but it does not create a React route
|
|
416
882
|
grammar or change server DTO validation.
|
|
417
883
|
|
|
884
|
+
## Native Form Mutations
|
|
885
|
+
|
|
886
|
+
Use a native HTML form when a React page needs a mutation that remains functional before hydration or
|
|
887
|
+
with client JavaScript disabled. Submit to an ordinary `@Post(...)` route rather than creating a
|
|
888
|
+
React-owned action transport. The runnable `examples/react-vite-ssr/` slice uses
|
|
889
|
+
`multipart/form-data`, which lets the browser provide the multipart boundary while the existing
|
|
890
|
+
adapter and `@RequestDto(...)` body-binding path materialize the fields.
|
|
891
|
+
|
|
892
|
+
```tsx
|
|
893
|
+
function ProductForm({ name, sku }: { readonly name: string; readonly sku: string }) {
|
|
894
|
+
return (
|
|
895
|
+
<form action={`/products/${encodeURIComponent(sku)}`} encType="multipart/form-data" method="post">
|
|
896
|
+
<label htmlFor="product-name">Product name</label>
|
|
897
|
+
<input defaultValue={name} id="product-name" minLength={3} name="name" required />
|
|
898
|
+
<button type="submit">Save product</button>
|
|
899
|
+
</form>
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
The target remains a normal HTTP handler. DTO binding and validation run once at the request
|
|
905
|
+
boundary; module middleware, guards, interceptors, request-scoped providers, observers, and adapter
|
|
906
|
+
response writing keep their ordinary ordering and ownership.
|
|
907
|
+
|
|
908
|
+
```ts
|
|
909
|
+
import {
|
|
910
|
+
FromBody,
|
|
911
|
+
FromPath,
|
|
912
|
+
Post,
|
|
913
|
+
RequestDto,
|
|
914
|
+
type RequestContext,
|
|
915
|
+
UseGuards,
|
|
916
|
+
UseInterceptors,
|
|
917
|
+
} from '@fluojs/http';
|
|
918
|
+
import { Router } from '@fluojs/react';
|
|
919
|
+
import { IsString, MinLength } from '@fluojs/validation';
|
|
920
|
+
|
|
921
|
+
class RenameProductRequest {
|
|
922
|
+
@MinLength(3, {
|
|
923
|
+
code: 'PRODUCT_NAME_TOO_SHORT',
|
|
924
|
+
message: 'Product name must contain at least 3 characters.',
|
|
925
|
+
})
|
|
926
|
+
@IsString()
|
|
927
|
+
@FromBody('name')
|
|
928
|
+
name = '';
|
|
929
|
+
|
|
930
|
+
@IsString()
|
|
931
|
+
@FromPath('sku')
|
|
932
|
+
sku = '';
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
@Router('/products')
|
|
936
|
+
class ProductRouter {
|
|
937
|
+
@Post('/:sku')
|
|
938
|
+
@RequestDto(RenameProductRequest)
|
|
939
|
+
@UseGuards(EditorGuard)
|
|
940
|
+
@UseInterceptors(MutationAuditInterceptor)
|
|
941
|
+
rename(input: RenameProductRequest, context: RequestContext) {
|
|
942
|
+
productCatalog.rename(input.sku, input.name);
|
|
943
|
+
context.response.redirect(303, `/products/${encodeURIComponent(input.sku)}?updated=true`);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
An invalid submission keeps the canonical HTTP `400` validation envelope. Its `details` entries
|
|
949
|
+
contain the safe field, source, code, and application-authored validation message; they do not expose
|
|
950
|
+
the submitted value, stack, or internal exception. A successful mutation uses `303 See Other` so the
|
|
951
|
+
browser follows with `GET`, and that destination is matched again by the ordinary HTTP dispatcher.
|
|
952
|
+
Authorization and CSRF policy remain application responsibilities; use the same session/cookie,
|
|
953
|
+
guard, and middleware policies as non-React routes.
|
|
954
|
+
|
|
955
|
+
This recipe is intentionally different from React Router actions/fetchers, Astro Actions, and
|
|
956
|
+
Next.js Server Actions: fluo does not compile a function reference, own route matching, revalidate a
|
|
957
|
+
loader/client cache, or replace the document response. It is also separate from the experimental
|
|
958
|
+
fluo Server Functions transport. No stable submit-state helper is added in this phase because the
|
|
959
|
+
native form already supplies the complete fallback and `@fluojs/react/client` does not own mutation
|
|
960
|
+
routes or cache invalidation. Applications may add local pending UI after hydration only when the
|
|
961
|
+
real form action and native submission remain intact.
|
|
962
|
+
|
|
418
963
|
## Experimental RSC Prototype
|
|
419
964
|
|
|
420
965
|
> **Experimental contract:** `@fluojs/react/experimental/rsc` can change before an explicit
|
|
@@ -483,6 +1028,10 @@ metadata, middleware, guards, interceptors, request scopes, errors, and adapter
|
|
|
483
1028
|
the helper adds the fixed `text/x-component; charset=utf-8` content type and does not create a
|
|
484
1029
|
parallel router.
|
|
485
1030
|
|
|
1031
|
+
Streamed Flight payloads use the same lifecycle guarantee as stable SSR: an early response-sink
|
|
1032
|
+
close or a failed write/drain cancels the unfinished reader exactly once, releases its lock, and
|
|
1033
|
+
preserves the sink failure for the ordinary HTTP error pipeline.
|
|
1034
|
+
|
|
486
1035
|
```ts
|
|
487
1036
|
import { Controller, Get } from '@fluojs/http';
|
|
488
1037
|
import { createReactFlightResponse } from '@fluojs/react/experimental/rsc';
|
|
@@ -635,10 +1184,16 @@ This package currently does **not** provide:
|
|
|
635
1184
|
- a Next.js App Router, TanStack route tree, Angular `Routes[]`, file-route scanner, or React-owned
|
|
636
1185
|
`routes: []` table
|
|
637
1186
|
- automatic client bundle generation
|
|
1187
|
+
- href generation for versioned React pages; path-only typegen rejects versioned catalog entries
|
|
1188
|
+
until the catalog can distinguish URI versioning from non-path version strategies
|
|
638
1189
|
- filesystem scanning or automatic manifest file discovery; pass an already-loaded manifest value to
|
|
639
1190
|
`@fluojs/react/vite`
|
|
640
1191
|
- automatic serialization of arbitrary data into `bootstrapScriptContent`
|
|
641
1192
|
- Node-only `react-dom/server` pipeable stream root APIs such as `renderToPipeableStream(...)`
|
|
1193
|
+
- Next.js-style segment `loading`, `error`, `notFound`, template, or layout ancestry semantics;
|
|
1194
|
+
`@SuspenseFallback(...)` is SSR-descendant Suspense metadata only
|
|
1195
|
+
- a generic page error-presentation policy or page-local not-found renderer; optional HTML is a
|
|
1196
|
+
global/application HTTP representation and does not add page ancestry or matching semantics
|
|
642
1197
|
|
|
643
1198
|
## Public API
|
|
644
1199
|
|
|
@@ -646,8 +1201,48 @@ This package currently does **not** provide:
|
|
|
646
1201
|
- `Path` — method decorator that writes HTTP `GET` route metadata plus React render metadata.
|
|
647
1202
|
- `getReactRouterMetadata` — reads React router marker metadata from a router class.
|
|
648
1203
|
- `getReactPathMetadata` — reads React render metadata from a router method.
|
|
1204
|
+
- `createReactPageCatalog` — creates a frozen, read-only React page catalog from authoritative
|
|
1205
|
+
compiled HTTP descriptors without participating in matching or dispatch.
|
|
1206
|
+
- `ReactPageCatalogEntry` — type-only bootstrap-resolved page descriptor with effective HTTP
|
|
1207
|
+
method/path/version/params plus the originating router and handler.
|
|
1208
|
+
- `@fluojs/react/typegen` subpath — `generateReactPageTypes(...)`,
|
|
1209
|
+
`inspectReactPageTypeArtifact(...)`, `REACT_PAGE_TYPEGEN_ARTIFACT_VERSION`,
|
|
1210
|
+
`ReactPageTypeArtifactInspection`, `ReactPageTypegenError`, `REACT_PAGE_TYPEGEN_ERROR_CODES`, and
|
|
1211
|
+
`ReactPageTypegenErrorCode` for deterministic path-only declarations, versioned artifact checks,
|
|
1212
|
+
absolute href builders, route-bound `Link` props, and typed `push`/`replace` methods without
|
|
1213
|
+
widening the package root or adding a runtime route table.
|
|
649
1214
|
- `ReactModule` — runtime-neutral module facade whose `forRoot(...)` registers React routers through
|
|
650
1215
|
the existing fluo module/controller metadata path.
|
|
1216
|
+
- `REACT_PAGE_RENDERER` — dependency-injection token for the application page renderer registered by
|
|
1217
|
+
`ReactModule.forRoot({ renderPage })`.
|
|
1218
|
+
- `ReactPageRenderer` — type-only application callback that composes a `ReactElement` and active
|
|
1219
|
+
`ReactRenderContext` plus resolved `ReactRenderPolicies` into an existing `ReactServerEntry`.
|
|
1220
|
+
- `PageLayout`, `SuspenseFallback`, and `PageMetadata` — class-or-method decorators that record
|
|
1221
|
+
renderer-only component references or one synchronous metadata factory.
|
|
1222
|
+
- `resolveReactPageMetadata` — composes ordered metadata factories for the active request without
|
|
1223
|
+
exposing response mutation authority.
|
|
1224
|
+
- `createReactPageMetadataElements` — creates ordinary escaped React title, meta, and link elements
|
|
1225
|
+
from one resolved metadata snapshot.
|
|
1226
|
+
- `getReactRenderPolicies` — resolves inherited class/method policies in outer-to-inner order.
|
|
1227
|
+
- `REACT_RENDER_POLICY_DIAGNOSTIC_CODES` and `ReactRenderPolicyConfigurationError` — stable
|
|
1228
|
+
bootstrap diagnostics for duplicate, invalid-target, invalid-reference, and missing-renderer policy
|
|
1229
|
+
declarations.
|
|
1230
|
+
- `ReactPageLayout`, `ReactPageLayoutProps`, `ReactSuspenseFallback`,
|
|
1231
|
+
`ReactSuspenseFallbackProps`, `ReactRenderPolicies`, and `ReactRenderPolicyDiagnosticCode` —
|
|
1232
|
+
type-only render-policy composition contracts.
|
|
1233
|
+
- `ReactPageMetadata`, `ReactPageMetadataFactory`, `ReactPageMetadataContext`, `ReactPageMeta`, and
|
|
1234
|
+
`ReactPageLink` — type-only bounded page metadata contracts.
|
|
1235
|
+
- `REACT_SSR_DIAGNOSTIC_PHASES` and `REACT_SSR_DIAGNOSTIC_CODES` — stable machine-readable SSR
|
|
1236
|
+
lifecycle phase and diagnostic code constants.
|
|
1237
|
+
- `ReactSsrDiagnosticError` — typed pre-commit configuration/render failure with stable `code` and
|
|
1238
|
+
`phase` metadata.
|
|
1239
|
+
- `ReactSsrDiagnostic`, `ReactSsrDiagnosticCode`, `ReactSsrDiagnosticErrorOptions`,
|
|
1240
|
+
`ReactSsrDiagnosticHandler`, and `ReactSsrDiagnosticPhase` — type-only contracts for application
|
|
1241
|
+
diagnostics tooling.
|
|
1242
|
+
- `createReactErrorRepresentationProvider` — adapts an application React error document renderer to
|
|
1243
|
+
the HTTP-owned HTML provider seam while buffering before commit.
|
|
1244
|
+
- `ReactErrorDocumentRenderer` and `ReactErrorRepresentationProviderOptions` — type-only contracts
|
|
1245
|
+
for the application callback, optional availability constraint, and renderer override.
|
|
651
1246
|
- `createReactServerEntry` — creates a runtime-neutral React server entry returned by page handlers
|
|
652
1247
|
for Web Streams SSR.
|
|
653
1248
|
- `renderReactResponse` — renders one React server entry to a fluo HTML response with lazy
|
|
@@ -655,7 +1250,7 @@ This package currently does **not** provide:
|
|
|
655
1250
|
- `ReactAssetMap`, `ReactBootstrapAsset`, and `ReactBootstrapScriptDescriptor` — type-only contracts
|
|
656
1251
|
for build-produced asset maps and React DOM bootstrap script/module entries.
|
|
657
1252
|
- `ReactModuleOptions` — options accepted by `ReactModule.forRoot(...)`, including `controllers`,
|
|
658
|
-
`imports`, `providers`, `exports`,
|
|
1253
|
+
`imports`, `providers`, `exports`, module-level `middleware`, and optional `renderPage` registration.
|
|
659
1254
|
- `ReactServerEntry`, `ReactServerEntryOptions`, `ReactServerEntryHeaders`,
|
|
660
1255
|
`ReactRecoverableErrorHandler`, `ReactRecoverableErrorContext`, `ReactRenderContext`,
|
|
661
1256
|
`ReactReadableStream`, `ReactReadableStreamRenderer`, `ReactReadableStreamRenderOptions`, and
|
|
@@ -664,28 +1259,43 @@ This package currently does **not** provide:
|
|
|
664
1259
|
- `ReactRouterMetadata`, `ReactPathMetadata`, `ReactPathOptions` — type-only metadata contracts for
|
|
665
1260
|
diagnostics and future rendering integration.
|
|
666
1261
|
- `@fluojs/react/vite` subpath — `createReactViteAssetManifest(...)` plus
|
|
667
|
-
`ReactViteBuildManifest`, `ReactViteBuildManifestChunk`, `
|
|
668
|
-
`
|
|
669
|
-
`
|
|
670
|
-
|
|
1262
|
+
`ReactViteBuildManifest`, `ReactViteBuildManifestChunk`, `ReactViteManifestEntries`,
|
|
1263
|
+
`ReactViteManifestOptions`, `ReactViteManifestDiagnostic`, `ReactViteManifestDiagnosticCode`,
|
|
1264
|
+
`ReactViteAssetManifest`, `ReactViteAssetManifestResult`, `ReactViteHydrationOptions`,
|
|
1265
|
+
`ReactViteJavaScriptAssets`, `ReactViteBootstrapData`, and `ReactViteResolvedEntry` for parsing Vite
|
|
1266
|
+
manifests into the stable hydration asset contract without importing Vite from the root.
|
|
671
1267
|
- `@fluojs/react/client` subpath — `Link`, `ReactClientRouterProvider`,
|
|
672
|
-
`
|
|
673
|
-
`
|
|
674
|
-
navigation without widening the root package
|
|
675
|
-
|
|
676
|
-
`
|
|
677
|
-
|
|
678
|
-
`
|
|
679
|
-
|
|
680
|
-
|
|
1268
|
+
`ReactClientNavigationError`, `ReactClientRouterContextError`, `createReactRouteSnapshot(...)`,
|
|
1269
|
+
`useRouter()`, `usePathname()`, `useParams()`, `useSearchParams()`, `useNavigation()`, and
|
|
1270
|
+
`useRouterState()` for progressive HTTP-first browser navigation without widening the root package
|
|
1271
|
+
or adding a client route grammar. Type exports are `LinkProps`, `ReactClientNavigationErrorCode`,
|
|
1272
|
+
`ReactClientRouterProviderProps`, `ReactNavigationSnapshot`, `ReactNavigationStatus`,
|
|
1273
|
+
`ReactNavigationType`, `ReactReadonlySearchParams`, `ReactRouteSnapshot`,
|
|
1274
|
+
`ReactRouteSnapshotInput`, and `ReactRouter`.
|
|
1275
|
+
- `@fluojs/react/experimental/rsc` subpath — runtime exports are `REACT_RSC_DIAGNOSTIC_CODES`,
|
|
1276
|
+
`REACT_RSC_FLIGHT_CONTENT_TYPE`, `REACT_RSC_SUPPORTED_VERSION`,
|
|
1277
|
+
`REACT_SERVER_FUNCTION_ERROR_CODES`, `REACT_SERVER_FUNCTION_REQUEST_HEADER`,
|
|
1278
|
+
`ReactServerFunctionClientError`, `ReactServerFunctionConfigurationError`,
|
|
1279
|
+
`createReactFlightResponse(...)`, `createReactRscManifest(...)`,
|
|
1280
|
+
`createReactServerFunctionClient(...)`, `createReactServerFunctionRegistry(...)`, and
|
|
1281
|
+
`inspectReactRscEnvironment(...)`. Type exports are `ReactFlightPayload`, `ReactFlightResponse`,
|
|
1282
|
+
`ReactFlightResponseHeaders`, `ReactFlightResponseOptions`, `ReactRscBuildCapabilities`,
|
|
1283
|
+
`ReactRscClientReference`, `ReactRscClientReferenceManifest`, `ReactRscDiagnostic`,
|
|
1284
|
+
`ReactRscDiagnosticCode`, `ReactRscEnvironmentOptions`, `ReactRscManifest`,
|
|
1285
|
+
`ReactRscManifestInput`, `ReactRscManifestResult`, `ReactRscRuntimeCapabilities`,
|
|
1286
|
+
`ReactRscServerClientModuleMap`, `ReactRscSupportResult`, `ReactServerFunctionClient`,
|
|
1287
|
+
`ReactServerFunctionClientOptions`, `ReactServerFunctionErrorCode`, `ReactServerFunctionFetch`,
|
|
1288
|
+
`ReactServerFunctionHandler`, `ReactServerFunctionReference`, `ReactServerFunctionRegistry`,
|
|
1289
|
+
`ReactServerFunctionRegistryOptions`, `ReactServerFunctionResponse`, and
|
|
1290
|
+
`ReactServerFunctionValue`. None are re-exported from the root or stable client subpath.
|
|
681
1291
|
|
|
682
1292
|
## Related Packages
|
|
683
1293
|
|
|
684
1294
|
- `@fluojs/core`: Provides the standard `@Module` decorator used by the scaffold.
|
|
685
1295
|
- `@fluojs/http`: Provides the controller, route, DTO, guard, interceptor, header, and version
|
|
686
1296
|
metadata pipeline reused by `@Router(...)` and `@Path(...)`.
|
|
687
|
-
- `@fluojs/runtime`:
|
|
688
|
-
contracts without widening the root import boundary.
|
|
1297
|
+
- `@fluojs/runtime`: Registers the optional HTTP error representation together with the existing
|
|
1298
|
+
application bootstrap contracts without widening the React root import boundary.
|
|
689
1299
|
- `@fluojs/vite`: Owns Vite's TC39 decorator transform boundary. It does not parse React hydration
|
|
690
1300
|
manifests; use `@fluojs/react/vite` for React server/client asset mapping.
|
|
691
1301
|
- Application-selected Flight renderer: Encodes RSC payloads and consumes renderer-specific build
|
|
@@ -702,6 +1312,7 @@ This package currently does **not** provide:
|
|
|
702
1312
|
- `packages/react/src/experimental/rsc.test.ts`
|
|
703
1313
|
- `packages/react/src/experimental/rsc-diagnostics.test.ts`
|
|
704
1314
|
- `packages/react/src/experimental/rsc-flight.test.ts`
|
|
1315
|
+
- `packages/react/src/experimental/rsc-flight-stream-lifecycle.test.ts`
|
|
705
1316
|
- `packages/react/src/experimental/rsc-manifest.test.ts`
|
|
706
1317
|
- `packages/react/src/experimental/server-functions-server.ts`
|
|
707
1318
|
- `packages/react/src/experimental/server-functions-client.ts`
|
|
@@ -712,7 +1323,15 @@ This package currently does **not** provide:
|
|
|
712
1323
|
- `packages/react/src/decorators.ts`
|
|
713
1324
|
- `packages/react/src/server-entry.ts`
|
|
714
1325
|
- `packages/react/src/render.ts`
|
|
1326
|
+
- `packages/react/src/error-representation.ts`
|
|
1327
|
+
- `packages/react/src/error-representation.test.ts`
|
|
715
1328
|
- `packages/react/src/module.ts`
|
|
1329
|
+
- `packages/react/src/page-renderer.ts`
|
|
1330
|
+
- `packages/react/src/render-policy.ts`
|
|
1331
|
+
- `packages/react/src/render-policy-metadata.ts`
|
|
1332
|
+
- `packages/react/src/render-policy.test.ts`
|
|
1333
|
+
- `packages/react/src/page-metadata.ts`
|
|
1334
|
+
- `packages/react/src/page-metadata.test.ts`
|
|
716
1335
|
- `packages/react/src/render.test.ts`
|
|
717
1336
|
- `packages/react/src/dispatcher-ssr.test.ts`
|
|
718
1337
|
- `packages/react/src/hydration-assets.test.ts`
|