@fluojs/react 0.1.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/LICENSE +21 -0
- package/README.ko.md +715 -0
- package/README.md +729 -0
- package/dist/client/errors.d.ts +12 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/errors.js +19 -0
- package/dist/client/hooks.d.ts +38 -0
- package/dist/client/hooks.d.ts.map +1 -0
- package/dist/client/hooks.js +56 -0
- package/dist/client/link.d.ts +13 -0
- package/dist/client/link.d.ts.map +1 -0
- package/dist/client/link.js +39 -0
- package/dist/client/provider.d.ts +12 -0
- package/dist/client/provider.d.ts.map +1 -0
- package/dist/client/provider.js +54 -0
- package/dist/client/search-params.d.ts +17 -0
- package/dist/client/search-params.d.ts.map +1 -0
- package/dist/client/search-params.js +39 -0
- package/dist/client/snapshot.d.ts +15 -0
- package/dist/client/snapshot.d.ts.map +1 -0
- package/dist/client/snapshot.js +57 -0
- package/dist/client/store.d.ts +26 -0
- package/dist/client/store.d.ts.map +1 -0
- package/dist/client/store.js +138 -0
- package/dist/client/types.d.ts +63 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +1 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +5 -0
- package/dist/decorators.d.ts +80 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +194 -0
- package/dist/experimental/rsc-diagnostics.d.ts +13 -0
- package/dist/experimental/rsc-diagnostics.d.ts.map +1 -0
- package/dist/experimental/rsc-diagnostics.js +58 -0
- package/dist/experimental/rsc-flight-response.d.ts +15 -0
- package/dist/experimental/rsc-flight-response.d.ts.map +1 -0
- package/dist/experimental/rsc-flight-response.js +76 -0
- package/dist/experimental/rsc-manifest.d.ts +13 -0
- package/dist/experimental/rsc-manifest.d.ts.map +1 -0
- package/dist/experimental/rsc-manifest.js +88 -0
- package/dist/experimental/rsc-types.d.ts +118 -0
- package/dist/experimental/rsc-types.d.ts.map +1 -0
- package/dist/experimental/rsc-types.js +50 -0
- package/dist/experimental/rsc.d.ts +11 -0
- package/dist/experimental/rsc.d.ts.map +1 -0
- package/dist/experimental/rsc.js +8 -0
- package/dist/experimental/server-functions-client.d.ts +14 -0
- package/dist/experimental/server-functions-client.d.ts.map +1 -0
- package/dist/experimental/server-functions-client.js +120 -0
- package/dist/experimental/server-functions-configuration.d.ts +17 -0
- package/dist/experimental/server-functions-configuration.d.ts.map +1 -0
- package/dist/experimental/server-functions-configuration.js +68 -0
- package/dist/experimental/server-functions-errors.d.ts +19 -0
- package/dist/experimental/server-functions-errors.d.ts.map +1 -0
- package/dist/experimental/server-functions-errors.js +22 -0
- package/dist/experimental/server-functions-reference.d.ts +22 -0
- package/dist/experimental/server-functions-reference.d.ts.map +1 -0
- package/dist/experimental/server-functions-reference.js +76 -0
- package/dist/experimental/server-functions-serialization.d.ts +37 -0
- package/dist/experimental/server-functions-serialization.d.ts.map +1 -0
- package/dist/experimental/server-functions-serialization.js +158 -0
- package/dist/experimental/server-functions-server.d.ts +14 -0
- package/dist/experimental/server-functions-server.d.ts.map +1 -0
- package/dist/experimental/server-functions-server.js +176 -0
- package/dist/experimental/server-functions-types.d.ts +86 -0
- package/dist/experimental/server-functions-types.d.ts.map +1 -0
- package/dist/experimental/server-functions-types.js +35 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/module.d.ts +40 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +58 -0
- package/dist/render-stream.d.ts +27 -0
- package/dist/render-stream.d.ts.map +1 -0
- package/dist/render-stream.js +135 -0
- package/dist/render.d.ts +50 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +201 -0
- package/dist/server-entry.d.ts +87 -0
- package/dist/server-entry.d.ts.map +1 -0
- package/dist/server-entry.js +126 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/vite/asset-utils.d.ts +57 -0
- package/dist/vite/asset-utils.d.ts.map +1 -0
- package/dist/vite/asset-utils.js +184 -0
- package/dist/vite/create-asset-manifest.d.ts +9 -0
- package/dist/vite/create-asset-manifest.d.ts.map +1 -0
- package/dist/vite/create-asset-manifest.js +127 -0
- package/dist/vite/diagnostics.d.ts +27 -0
- package/dist/vite/diagnostics.d.ts.map +1 -0
- package/dist/vite/diagnostics.js +47 -0
- package/dist/vite/parse-manifest.d.ts +18 -0
- package/dist/vite/parse-manifest.d.ts.map +1 -0
- package/dist/vite/parse-manifest.js +168 -0
- package/dist/vite/types.d.ts +187 -0
- package/dist/vite/types.d.ts.map +1 -0
- package/dist/vite/types.js +59 -0
- package/dist/vite.d.ts +3 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +1 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+
# @fluojs/react
|
|
2
|
+
|
|
3
|
+
<p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
|
|
4
|
+
|
|
5
|
+
Runtime-neutral React integration for fluo applications.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [When to Use](#when-to-use)
|
|
11
|
+
- [Stable SSR Mental Model](#stable-ssr-mental-model)
|
|
12
|
+
- [Runtime and Peer Contract](#runtime-and-peer-contract)
|
|
13
|
+
- [Phase Boundaries](#phase-boundaries)
|
|
14
|
+
- [ReactModule Registration](#reactmodule-registration)
|
|
15
|
+
- [Router and Path Decorators](#router-and-path-decorators)
|
|
16
|
+
- [Web Streams SSR](#web-streams-ssr)
|
|
17
|
+
- [Hydration Asset Contract](#hydration-asset-contract)
|
|
18
|
+
- [Vite Asset Manifest Integration](#vite-asset-manifest-integration)
|
|
19
|
+
- [Client Navigation Runtime](#client-navigation-runtime)
|
|
20
|
+
- [Experimental RSC Prototype](#experimental-rsc-prototype)
|
|
21
|
+
- [Experimental Server Functions](#experimental-server-functions)
|
|
22
|
+
- [RSC Graduation Policy](#rsc-graduation-policy)
|
|
23
|
+
- [Current Limitations](#current-limitations)
|
|
24
|
+
- [Public API](#public-api)
|
|
25
|
+
- [Related Packages](#related-packages)
|
|
26
|
+
- [Example Sources](#example-sources)
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
The first public release target for this package is `0.1.0`. The manifest starts at
|
|
31
|
+
`0.0.0` so Changesets can publish the initial `0.1.0` version through the canonical
|
|
32
|
+
release workflow.
|
|
33
|
+
|
|
34
|
+
When the package is published, install it with React and React DOM as peers:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install @fluojs/react react react-dom
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## When to Use
|
|
41
|
+
|
|
42
|
+
Use this package when React page handlers need a lexical distinction from API controllers while
|
|
43
|
+
still participating in fluo's module graph and HTTP metadata pipeline. `ReactModule.forRoot(...)`
|
|
44
|
+
places React routers into ordinary module controller metadata, and `@Router(...)` plus
|
|
45
|
+
`@Path(...)` are React facades over `@fluojs/http` controller and `GET` route metadata, so request
|
|
46
|
+
DTO binding, versioning, guards, interceptors, headers, route validation, matching, and dispatch
|
|
47
|
+
continue to use the HTTP runtime contracts.
|
|
48
|
+
|
|
49
|
+
## Stable SSR Mental Model
|
|
50
|
+
|
|
51
|
+
The stable `0.1.0` model is HTTP-first React SSR. `@Router(...)` and `@Path(...)` are lexical
|
|
52
|
+
React facades over `@fluojs/http` metadata: they mark classes and methods as React page surfaces for
|
|
53
|
+
readability and diagnostics, then write the same controller and `GET` route metadata that the HTTP
|
|
54
|
+
runtime already understands. URL matching remains owned by `@fluojs/http`, not React, so React page
|
|
55
|
+
paths inherit the HTTP route grammar, conflict detection, versioning, DTO materialization,
|
|
56
|
+
validation, guards, interceptors, headers, module middleware, request scopes, and request lifecycle.
|
|
57
|
+
|
|
58
|
+
This package is therefore **not** a Next.js App Router clone, React Server Components framework,
|
|
59
|
+
TanStack route tree, Angular `Routes[]` table, file-route scanner, or primary React-owned
|
|
60
|
+
`routes: []` configuration model. Treat React routers as page-shaped HTTP handlers. Route discovery
|
|
61
|
+
and dispatch stay in the existing fluo module/controller pipeline, and page handlers return either
|
|
62
|
+
ordinary values or `createReactServerEntry(...)` when they want streamed HTML.
|
|
63
|
+
|
|
64
|
+
## Runtime and Peer Contract
|
|
65
|
+
|
|
66
|
+
The root `@fluojs/react` import is runtime-neutral. Importing it must not eagerly load Node.js
|
|
67
|
+
built-ins, Vite, `react-dom/server`, React Server Components packages, or Server Functions code.
|
|
68
|
+
|
|
69
|
+
`react` and `react-dom` are declared as peer dependencies so applications own the React runtime
|
|
70
|
+
version. The package root exposes SSR helpers but resolves `react-dom/server` lazily only when a
|
|
71
|
+
React server entry is rendered.
|
|
72
|
+
|
|
73
|
+
## Phase Boundaries
|
|
74
|
+
|
|
75
|
+
`@fluojs/react` uses explicit subpath boundaries so the stable root stays small and runtime-neutral:
|
|
76
|
+
|
|
77
|
+
- **root `@fluojs/react`** — stable `0.1.0` SSR MVP contracts: `ReactModule.forRoot(...)`,
|
|
78
|
+
`@Router(...)`, `@Path(...)`, metadata readers, `createReactServerEntry(...)`,
|
|
79
|
+
`renderReactResponse(...)`, Web Streams SSR, and explicit hydration asset options.
|
|
80
|
+
- **`@fluojs/react/vite`** — Vite build manifest parsing, React server/client entry selection,
|
|
81
|
+
deterministic stylesheet and JavaScript ordering, manifest diagnostics, and hydration option
|
|
82
|
+
creation. The root package still accepts explicit asset options without discovering or scanning
|
|
83
|
+
manifests.
|
|
84
|
+
- **`@fluojs/react/client`** — progressive anchors, HTTP-first full-document navigation, and
|
|
85
|
+
hydration-safe URL, path-param, and navigation lifecycle hooks. Browser APIs remain isolated from
|
|
86
|
+
the runtime-neutral root.
|
|
87
|
+
- **`@fluojs/react/experimental/rsc`** — an explicitly unstable React Server Components prototype
|
|
88
|
+
for exact-version compatibility diagnostics, client-reference/server-module manifest seams, and
|
|
89
|
+
Flight payload responses plus signed Server Function transport through ordinary fluo HTTP
|
|
90
|
+
handlers. It does not export from the stable root or `@fluojs/react/client`.
|
|
91
|
+
|
|
92
|
+
## ReactModule Registration
|
|
93
|
+
|
|
94
|
+
Use `ReactModule.forRoot({ controllers: [...] })` inside an ordinary fluo module import to register
|
|
95
|
+
React routers. The returned module is a normal fluo module definition: it may include `imports`,
|
|
96
|
+
`providers`, `exports`, and `middleware`, and those fields keep the same visibility and lifecycle
|
|
97
|
+
rules as `@Module(...)` metadata.
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { Module, Inject } from '@fluojs/core';
|
|
101
|
+
import { ReactModule, Router, Path } from '@fluojs/react';
|
|
102
|
+
|
|
103
|
+
class DashboardPresenter {
|
|
104
|
+
render() {
|
|
105
|
+
return { page: 'dashboard' };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@Inject(DashboardPresenter)
|
|
110
|
+
@Router('/dashboard')
|
|
111
|
+
class DashboardRouter {
|
|
112
|
+
constructor(private readonly presenter: DashboardPresenter) {}
|
|
113
|
+
|
|
114
|
+
@Path('/')
|
|
115
|
+
index() {
|
|
116
|
+
return this.presenter.render();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@Module({
|
|
121
|
+
imports: [
|
|
122
|
+
ReactModule.forRoot({
|
|
123
|
+
controllers: [DashboardRouter],
|
|
124
|
+
providers: [DashboardPresenter],
|
|
125
|
+
}),
|
|
126
|
+
],
|
|
127
|
+
})
|
|
128
|
+
class AppModule {}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`ReactModule` does not own URL matching. Routers registered through it become ordinary HTTP handler
|
|
132
|
+
sources, so `createHandlerMapping(...)` and `Dispatcher` still detect duplicate routes, apply
|
|
133
|
+
module-level middleware, create request scopes, run guards and interceptors, and resolve route
|
|
134
|
+
versioning.
|
|
135
|
+
|
|
136
|
+
## Router and Path Decorators
|
|
137
|
+
|
|
138
|
+
`@Router(basePath)` marks a class as a React router and writes HTTP controller metadata equivalent
|
|
139
|
+
to `@Controller(basePath)`. It also stores React router marker metadata readable through
|
|
140
|
+
`getReactRouterMetadata(...)` for diagnostics and future rendering integration.
|
|
141
|
+
|
|
142
|
+
`@Path(path, options?)` marks a method as a React page route and writes HTTP `GET` route metadata
|
|
143
|
+
equivalent to `@Get(path)`. It also stores React render metadata readable through
|
|
144
|
+
`getReactPathMetadata(...)`. The optional `options` object is metadata only in this phase; it does
|
|
145
|
+
not change HTTP matching or dispatch.
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { Router, Path } from '@fluojs/react';
|
|
149
|
+
import { FromPath, Optional, RequestDto, FromQuery } from '@fluojs/http';
|
|
150
|
+
|
|
151
|
+
class DashboardEditRequest {
|
|
152
|
+
@FromPath('id')
|
|
153
|
+
id = '';
|
|
154
|
+
|
|
155
|
+
@Optional()
|
|
156
|
+
@FromQuery('tab')
|
|
157
|
+
tab?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@Router('/dashboard')
|
|
161
|
+
class DashboardRouter {
|
|
162
|
+
@Path('/:id/edit')
|
|
163
|
+
@RequestDto(DashboardEditRequest)
|
|
164
|
+
edit(input: DashboardEditRequest) {
|
|
165
|
+
return { page: 'dashboard-edit', input };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
React page paths use the exact `@fluojs/http` route grammar: literal segments and full-segment
|
|
171
|
+
`:param` placeholders only. Wildcards, catch-all routes, optional segments, regex-like tokens,
|
|
172
|
+
mixed literal/parameter segments such as `user-:id`, and suffix params such as `:id.json` are not
|
|
173
|
+
supported.
|
|
174
|
+
|
|
175
|
+
The [HTTP catch-all route grammar decision](../../docs/architecture/http-catch-all-route-grammar.md)
|
|
176
|
+
defers wildcard adoption. React does not add its own syntax: page handlers should keep explicit
|
|
177
|
+
server routes, and any future catch-all must first become an approved `@fluojs/http` contract.
|
|
178
|
+
|
|
179
|
+
## Web Streams SSR
|
|
180
|
+
|
|
181
|
+
Return `createReactServerEntry(...)` from a React page handler to stream HTML through the existing
|
|
182
|
+
fluo HTTP dispatcher. Guards, interceptors, module middleware, route headers, `@HttpCode(...)`, DTO
|
|
183
|
+
binding, request scopes, and duplicate route detection all run before `renderReactResponse(...)`
|
|
184
|
+
finalizes the HTML response.
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { HttpCode, RequestDto, FromPath } from '@fluojs/http';
|
|
188
|
+
import { Router, Path, createReactServerEntry } from '@fluojs/react';
|
|
189
|
+
|
|
190
|
+
class DashboardRequest {
|
|
191
|
+
@FromPath('id')
|
|
192
|
+
id = '';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@Router('/dashboard')
|
|
196
|
+
class DashboardRouter {
|
|
197
|
+
@HttpCode(206)
|
|
198
|
+
@Path('/:id')
|
|
199
|
+
@RequestDto(DashboardRequest)
|
|
200
|
+
show(input: DashboardRequest) {
|
|
201
|
+
return createReactServerEntry(<main>Dashboard {input.id}</main>, {
|
|
202
|
+
headers: { 'x-react-page': 'dashboard' },
|
|
203
|
+
onRecoverableError(error, context) {
|
|
204
|
+
// Report through your application logger; the response status is already committed.
|
|
205
|
+
void error;
|
|
206
|
+
void context;
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The renderer uses `react-dom/server` `renderToReadableStream(...)` by default, preserves Suspense
|
|
214
|
+
streaming, writes `Content-Type: text/html; charset=utf-8`, forwards `RequestContext.request.signal`
|
|
215
|
+
when an adapter provides one, and throws shell render failures before response bytes are committed.
|
|
216
|
+
Recoverable Suspense errors are reported through `onRecoverableError` and do not rewrite an already
|
|
217
|
+
committed status. Call `renderReactResponse(entry, requestContext)` directly only when a custom
|
|
218
|
+
handler needs to finalize the response itself instead of returning the entry to the dispatcher.
|
|
219
|
+
|
|
220
|
+
## Hydration Asset Contract
|
|
221
|
+
|
|
222
|
+
`createReactServerEntry(...)` accepts explicit React DOM hydration asset options and forwards them to
|
|
223
|
+
the Web Streams renderer:
|
|
224
|
+
|
|
225
|
+
- `bootstrapScripts` emits classic bootstrap `<script>` tags. Duplicate entries with the same `src`
|
|
226
|
+
are removed before React DOM receives the option.
|
|
227
|
+
- `bootstrapModules` emits module bootstrap `<script type="module">` tags with the same duplicate
|
|
228
|
+
removal rule.
|
|
229
|
+
- `bootstrapScriptContent` emits trusted inline script content exactly as provided. fluo does not
|
|
230
|
+
serialize arbitrary values into this string; only use it for trusted build-time data or content that
|
|
231
|
+
your application has escaped through an approved safety contract.
|
|
232
|
+
- `nonce` applies the CSP nonce that React DOM attaches to emitted bootstrap scripts.
|
|
233
|
+
- `identifierPrefix` is forwarded to React DOM so server-rendered `useId()` output and client
|
|
234
|
+
hydration share the same prefix.
|
|
235
|
+
- `assetMap` is a defensive snapshot of trusted build-produced logical asset names to public URLs.
|
|
236
|
+
fluo passes the snapshot to custom renderers and expects applications to pass the same data to the
|
|
237
|
+
server root component and client `hydrateRoot(...)` call when it affects markup.
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
const assetMap = {
|
|
241
|
+
'main.js': '/assets/main.123.js',
|
|
242
|
+
'styles.css': '/assets/styles.123.css',
|
|
243
|
+
} as const;
|
|
244
|
+
|
|
245
|
+
return createReactServerEntry(<App assetMap={assetMap} />, {
|
|
246
|
+
assetMap,
|
|
247
|
+
bootstrapModules: [assetMap['main.js']],
|
|
248
|
+
bootstrapScriptContent: 'window.__FLUO_ASSET_MAP__ = {"main.js":"/assets/main.123.js"};',
|
|
249
|
+
identifierPrefix: 'fluo-',
|
|
250
|
+
nonce: cspNonce,
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
CSS/JS ordering remains caller-owned when applications pass hydration options manually.
|
|
255
|
+
`@fluojs/react/vite` preserves manifest stylesheet order for the app-rendered document head before
|
|
256
|
+
hydration scripts, then preserves manifest JavaScript order plus the caller order of
|
|
257
|
+
`bootstrapScripts` and `bootstrapModules` after duplicate removal. This package does not discover
|
|
258
|
+
Vite manifests from the filesystem, generate client bundles, or serialize untrusted user data into
|
|
259
|
+
inline scripts.
|
|
260
|
+
|
|
261
|
+
## Vite Asset Manifest Integration
|
|
262
|
+
|
|
263
|
+
Use `@fluojs/react/vite` when a Vite-built React application needs to turn a loaded Vite manifest
|
|
264
|
+
into the stable hydration options accepted by `createReactServerEntry(...)`.
|
|
265
|
+
|
|
266
|
+
```tsx
|
|
267
|
+
import { createReactViteAssetManifest } from '@fluojs/react/vite';
|
|
268
|
+
import { createReactServerEntry } from '@fluojs/react';
|
|
269
|
+
|
|
270
|
+
const assets = createReactViteAssetManifest({
|
|
271
|
+
base: '/assets/',
|
|
272
|
+
entries: {
|
|
273
|
+
client: 'src/entry-client.tsx',
|
|
274
|
+
server: 'src/entry-server.tsx',
|
|
275
|
+
},
|
|
276
|
+
manifest: viteManifest,
|
|
277
|
+
nonce: cspNonce,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (!assets.ok) {
|
|
281
|
+
throw new Error(assets.diagnostics.map((diagnostic) => diagnostic.message).join('\n'));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return createReactServerEntry(<App assetMap={assets.manifest.assetMap} />, {
|
|
285
|
+
...assets.manifest.hydrationOptions,
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
`createReactViteAssetManifest(...)` accepts an already-loaded manifest value. It does not read the
|
|
290
|
+
filesystem, run Vite, create client bundles, or add a root dependency on Vite. The parsed manifest
|
|
291
|
+
schema is compatible with Vite's client manifest shape:
|
|
292
|
+
|
|
293
|
+
| Field | Required | Meaning |
|
|
294
|
+
| --- | --- | --- |
|
|
295
|
+
| `file` | yes | JavaScript output file for a chunk. Selected server/client entries must resolve to `.js`, `.mjs`, or `.cjs`. |
|
|
296
|
+
| `src` / `name` | no | Secondary lookup keys for the explicit `entries.server` and `entries.client` selectors. |
|
|
297
|
+
| `isEntry` / `isDynamicEntry` | no | Vite entry markers retained for diagnostics and future compatibility. |
|
|
298
|
+
| `imports` | no | Static imported chunk ids. Imported chunks are ordered before the client entry. |
|
|
299
|
+
| `css` | no | Stylesheet files emitted for a chunk. The returned `manifest.css` keeps dependency order and removes duplicates. |
|
|
300
|
+
| `assets` | no | Static asset files copied into the returned `assetMap`. |
|
|
301
|
+
|
|
302
|
+
The successful result contains:
|
|
303
|
+
|
|
304
|
+
- `manifest.hydrationOptions` — `assetMap`, `bootstrapModules`, `bootstrapScripts`, trusted
|
|
305
|
+
`bootstrapScriptContent`, `identifierPrefix`, and `nonce` shaped for `createReactServerEntry(...)`.
|
|
306
|
+
- `manifest.css` — stylesheet URLs for the application-rendered document head before hydration
|
|
307
|
+
scripts.
|
|
308
|
+
- `manifest.js.modules` and `manifest.js.scripts` — module scripts derived from the Vite client
|
|
309
|
+
import graph and caller-provided classic scripts.
|
|
310
|
+
- `manifest.assetMap` — a defensive snapshot of manifest keys, source names, emitted files, CSS, and
|
|
311
|
+
static assets mapped to public URLs.
|
|
312
|
+
- `manifest.serverEntry` and `manifest.clientEntry` — resolved React server/client entries.
|
|
313
|
+
|
|
314
|
+
Expected manifest failures return diagnostics instead of throwing. Stable diagnostic codes are:
|
|
315
|
+
|
|
316
|
+
- `react-vite-manifest-missing-server-entry`
|
|
317
|
+
- `react-vite-manifest-missing-client-entry`
|
|
318
|
+
- `react-vite-manifest-malformed`
|
|
319
|
+
- `react-vite-manifest-unsupported-output-shape`
|
|
320
|
+
|
|
321
|
+
`@fluojs/react/vite` is separate from `@fluojs/vite`. Use `@fluojs/vite` in `vite.config.ts` for the
|
|
322
|
+
TC39 decorator transform used by fluo applications. Use `@fluojs/react/vite` in React SSR code to
|
|
323
|
+
parse React build assets and feed the existing hydration contract. Neither package owns file routes,
|
|
324
|
+
React-only route grammar, Next.js route segment conventions, RSC bundler behavior, or URL matching.
|
|
325
|
+
The runnable `examples/react-vite-ssr/` application shows this boundary with generated assets,
|
|
326
|
+
streamed Suspense content, direct React DOM hydration, and the client navigation subpath.
|
|
327
|
+
|
|
328
|
+
## Client Navigation Runtime
|
|
329
|
+
|
|
330
|
+
Use `@fluojs/react/client` in hydrated React pages that need navigation controls and URL state without
|
|
331
|
+
introducing a client-owned route table. Create the initial snapshot from the active fluo HTTP request,
|
|
332
|
+
then pass that same snapshot to `ReactClientRouterProvider` during server rendering and hydration.
|
|
333
|
+
This request-scoped snapshot prevents server/client URL state drift and carries path params produced by
|
|
334
|
+
the existing HTTP route match; the client runtime never derives or validates route params itself.
|
|
335
|
+
|
|
336
|
+
```tsx
|
|
337
|
+
import {
|
|
338
|
+
Link,
|
|
339
|
+
ReactClientRouterProvider,
|
|
340
|
+
createReactRouteSnapshot,
|
|
341
|
+
useNavigation,
|
|
342
|
+
usePathname,
|
|
343
|
+
useRouter,
|
|
344
|
+
useSearchParams,
|
|
345
|
+
} from '@fluojs/react/client';
|
|
346
|
+
|
|
347
|
+
function DashboardNav() {
|
|
348
|
+
const navigation = useNavigation();
|
|
349
|
+
const pathname = usePathname();
|
|
350
|
+
const router = useRouter();
|
|
351
|
+
const searchParams = useSearchParams();
|
|
352
|
+
|
|
353
|
+
return (
|
|
354
|
+
<nav aria-label="Dashboard">
|
|
355
|
+
<Link href="/dashboard/42/edit?tab=profile">Edit</Link>
|
|
356
|
+
<button type="button" onClick={() => router.push('/dashboard/42/edit?tab=profile')}>
|
|
357
|
+
Open editor
|
|
358
|
+
</button>
|
|
359
|
+
<output>{pathname} · {searchParams.get('tab')} · {navigation.status}</output>
|
|
360
|
+
</nav>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const initialSnapshot = createReactRouteSnapshot({
|
|
365
|
+
url: requestContext.request.url,
|
|
366
|
+
params: requestContext.request.params,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
const app = (
|
|
370
|
+
<ReactClientRouterProvider initialSnapshot={initialSnapshot}>
|
|
371
|
+
<DashboardNav />
|
|
372
|
+
</ReactClientRouterProvider>
|
|
373
|
+
);
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
The navigation contract is deliberately HTTP-first:
|
|
377
|
+
|
|
378
|
+
- `Link` always renders a real `<a href>` so pre-hydration clicks, disabled JavaScript, modified
|
|
379
|
+
clicks, downloads, explicit targets, and cross-origin destinations keep native browser behavior.
|
|
380
|
+
After hydration, an unmodified primary click to a same-origin HTTP(S) URL delegates to
|
|
381
|
+
`router.push(...)`.
|
|
382
|
+
- `router.push(href)` uses `window.location.assign(...)`; `router.replace(href)` uses
|
|
383
|
+
`window.location.replace(...)`. A same-origin destination that changes the pathname or search
|
|
384
|
+
(query string) performs full-document navigation, so fluo HTTP route matching, `@RequestDto`
|
|
385
|
+
binding and validation, guards, interceptors, redirects, not-found responses, non-HTML responses,
|
|
386
|
+
and server failures remain authoritative.
|
|
387
|
+
- A fragment-only destination that keeps the current pathname and search is a same-document
|
|
388
|
+
exception. The browser does not issue a new HTTP request; it emits `hashchange`, and a matching
|
|
389
|
+
event completes the requested `push` or `replace` lifecycle while updating the route snapshot URL
|
|
390
|
+
and hash. Because no server request occurs, fluo HTTP route matching, `@RequestDto` binding and
|
|
391
|
+
validation, guards, and interceptors do not run for that fragment change.
|
|
392
|
+
- If the normalized destination is an identical URL to the current route snapshot, the router does
|
|
393
|
+
not call `window.location.assign(...)` or `window.location.replace(...)`. It exposes `skipped`
|
|
394
|
+
with the requested navigation type and destination instead.
|
|
395
|
+
- `router.back()` delegates to `window.history.back()`. `router.refresh()` uses
|
|
396
|
+
`window.location.reload()` as the documented revalidation mechanism. It does not imply an RSC,
|
|
397
|
+
loader, or client-data cache.
|
|
398
|
+
- `usePathname()`, `useSearchParams()`, `useParams()`, and `useRouterState()` read the provider's
|
|
399
|
+
immutable route snapshot. `popstate` and `hashchange` update URL-derived fields. If a history event
|
|
400
|
+
changes the pathname without a new server document, stale path params are cleared rather than
|
|
401
|
+
guessed from a client route grammar.
|
|
402
|
+
- `useNavigation()` exposes `idle`, `navigating`, `refreshing`, `complete`, `error`, and `skipped`.
|
|
403
|
+
Full-document path/search transitions normally leave the current document while `navigating` or
|
|
404
|
+
`refreshing`, and the destination document starts from a new server-owned `idle` snapshot.
|
|
405
|
+
Fragment-only transitions complete in the current document after the matching `hashchange`.
|
|
406
|
+
- Router methods reject cross-origin or non-HTTP(S) destinations with
|
|
407
|
+
`ReactClientNavigationError`. Use a normal anchor for those destinations.
|
|
408
|
+
|
|
409
|
+
This phase intentionally omits `prefetch`: without an owned client data or render cache, prefetching
|
|
410
|
+
would promise behavior the package cannot yet revalidate or consume consistently.
|
|
411
|
+
|
|
412
|
+
Client navigation does not require a catch-all route. `Link` remains a real anchor, so hydration
|
|
413
|
+
gaps and disabled JavaScript fall back to ordinary full-document browser navigation. The server then
|
|
414
|
+
matches an explicit `@Path(...)`/HTTP route or returns its normal not-found response. An intentional
|
|
415
|
+
deployment-level document rewrite may be configured separately, but it does not create a React route
|
|
416
|
+
grammar or change server DTO validation.
|
|
417
|
+
|
|
418
|
+
## Experimental RSC Prototype
|
|
419
|
+
|
|
420
|
+
> **Experimental contract:** `@fluojs/react/experimental/rsc` can change before an explicit
|
|
421
|
+
> graduation issue makes any RSC API stable. Do not import these APIs from the root package or treat
|
|
422
|
+
> them as semver-stable React framework internals.
|
|
423
|
+
|
|
424
|
+
The prototype supports **exactly** `react@19.2.6`, `react-dom@19.2.6`, and a matching Flight renderer
|
|
425
|
+
version. Version ranges and canary versions are not supported. If an application selects
|
|
426
|
+
`react-server-dom-webpack`, pin it to the same exact version; this package does not install, import,
|
|
427
|
+
or wrap that renderer. The stable root peer range remains broader for applications that do not use
|
|
428
|
+
the experimental RSC subpath.
|
|
429
|
+
|
|
430
|
+
Call `inspectReactRscEnvironment(...)` before enabling an RSC endpoint. It reports stable diagnostics
|
|
431
|
+
when any React version differs, the runtime does not provide Web `ReadableStream`, or the
|
|
432
|
+
application-owned build adapter does not provide both the client-reference manifest and explicit
|
|
433
|
+
server-to-client module map.
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
import {
|
|
437
|
+
REACT_RSC_SUPPORTED_VERSION,
|
|
438
|
+
inspectReactRscEnvironment,
|
|
439
|
+
} from '@fluojs/react/experimental/rsc';
|
|
440
|
+
|
|
441
|
+
const support = inspectReactRscEnvironment({
|
|
442
|
+
reactVersion: '19.2.6',
|
|
443
|
+
reactDomVersion: '19.2.6',
|
|
444
|
+
flightRendererVersion: REACT_RSC_SUPPORTED_VERSION,
|
|
445
|
+
runtime: { name: 'node', webStreams: typeof ReadableStream !== 'undefined' },
|
|
446
|
+
build: {
|
|
447
|
+
name: 'application-rsc-build',
|
|
448
|
+
clientReferenceManifest: true,
|
|
449
|
+
serverClientModuleMap: true,
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
`createReactRscManifest(...)` defines the initial bundler-neutral module graph seam. Client-reference
|
|
455
|
+
keys resolve to `{ id, chunks, name, async? }` metadata. Each server module id maps export names back
|
|
456
|
+
to those client-reference keys. The helper returns a defensive snapshot, rejects unknown mapping
|
|
457
|
+
targets with diagnostics, and never scans files or generates bundles. Translating this seam into a
|
|
458
|
+
specific React Flight renderer manifest remains the application build adapter's responsibility.
|
|
459
|
+
|
|
460
|
+
```ts
|
|
461
|
+
import { createReactRscManifest } from '@fluojs/react/experimental/rsc';
|
|
462
|
+
|
|
463
|
+
const manifest = createReactRscManifest({
|
|
464
|
+
clientReferences: {
|
|
465
|
+
Counter: {
|
|
466
|
+
id: 'client:counter',
|
|
467
|
+
chunks: ['assets/counter.js'],
|
|
468
|
+
name: 'Counter',
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
serverClientModuleMap: {
|
|
472
|
+
'server:dashboard': {
|
|
473
|
+
Counter: 'Counter',
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
The application also owns Flight encoding. Return `createReactFlightResponse(...)` from an ordinary
|
|
480
|
+
fluo HTTP controller or React `@Path(...)` handler after the selected renderer has produced encoded
|
|
481
|
+
text, bytes, or a Web `ReadableStream<Uint8Array>`. The existing dispatcher still owns route
|
|
482
|
+
metadata, middleware, guards, interceptors, request scopes, errors, and adapter response writing;
|
|
483
|
+
the helper adds the fixed `text/x-component; charset=utf-8` content type and does not create a
|
|
484
|
+
parallel router.
|
|
485
|
+
|
|
486
|
+
```ts
|
|
487
|
+
import { Controller, Get } from '@fluojs/http';
|
|
488
|
+
import { createReactFlightResponse } from '@fluojs/react/experimental/rsc';
|
|
489
|
+
|
|
490
|
+
@Controller('/rsc')
|
|
491
|
+
class RscController {
|
|
492
|
+
@Get('/dashboard')
|
|
493
|
+
dashboard() {
|
|
494
|
+
const payload = applicationFlightRenderer.render({ page: 'dashboard' });
|
|
495
|
+
return createReactFlightResponse(payload);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
The RSC manifest and Flight response helpers do not provide a Flight encoder/decoder, a Webpack or
|
|
501
|
+
Vite RSC plugin, automatic module graph discovery, client bundle generation, file routes, route
|
|
502
|
+
segments, or a React-owned URL matcher. The Server Function transport below is a separate opt-in
|
|
503
|
+
prototype on the same unstable subpath.
|
|
504
|
+
|
|
505
|
+
## Experimental Server Functions
|
|
506
|
+
|
|
507
|
+
`createReactServerFunctionRegistry(...)` defines server-only actions, issues HMAC-SHA-256 references,
|
|
508
|
+
and validates calls. It deliberately does not create a router. Mount `registry.invoke(context)` on an
|
|
509
|
+
explicit ordinary fluo `@Post(...)` route so module middleware, route/controller guards,
|
|
510
|
+
interceptors, request-scoped providers, request observers, error envelopes, and adapter response
|
|
511
|
+
writing remain in the existing HTTP lifecycle.
|
|
512
|
+
|
|
513
|
+
```ts
|
|
514
|
+
import {
|
|
515
|
+
BadRequestException,
|
|
516
|
+
Controller,
|
|
517
|
+
Post,
|
|
518
|
+
type RequestContext,
|
|
519
|
+
UnauthorizedException,
|
|
520
|
+
} from '@fluojs/http';
|
|
521
|
+
import { createReactServerFunctionRegistry } from '@fluojs/react/experimental/rsc';
|
|
522
|
+
|
|
523
|
+
const actions = createReactServerFunctionRegistry({
|
|
524
|
+
actions: {
|
|
525
|
+
updateProfile(args, context) {
|
|
526
|
+
const subject = context.principal?.subject;
|
|
527
|
+
if (subject === undefined) {
|
|
528
|
+
throw new UnauthorizedException();
|
|
529
|
+
}
|
|
530
|
+
const name = args[0];
|
|
531
|
+
if (typeof name !== 'string') {
|
|
532
|
+
throw new BadRequestException('Profile name must be a string.');
|
|
533
|
+
}
|
|
534
|
+
return { name, subject, updated: true };
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
allowedOrigins: ['https://app.example.com'],
|
|
538
|
+
crypto: globalThis.crypto,
|
|
539
|
+
secret: serverFunctionSecret,
|
|
540
|
+
maxBodyBytes: 64 * 1024,
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
export const updateProfileReference = await actions.createReference('updateProfile');
|
|
544
|
+
|
|
545
|
+
@Controller('/_fluo')
|
|
546
|
+
class ReactActionController {
|
|
547
|
+
@Post('/actions')
|
|
548
|
+
invoke(_input: undefined, context: RequestContext) {
|
|
549
|
+
return actions.invoke(context);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
Transfer the issued reference to trusted client bootstrap data instead of importing the server
|
|
555
|
+
action module into a client bundle. Then create the callable with an explicit endpoint and
|
|
556
|
+
application-owned fetch implementation:
|
|
557
|
+
|
|
558
|
+
```ts
|
|
559
|
+
import { createReactServerFunctionClient } from '@fluojs/react/experimental/rsc';
|
|
560
|
+
|
|
561
|
+
const updateProfile = createReactServerFunctionClient({
|
|
562
|
+
endpoint: '/_fluo/actions',
|
|
563
|
+
fetch: globalThis.fetch,
|
|
564
|
+
reference: bootstrap.serverFunctions.updateProfile,
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
await updateProfile('Ada');
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
Treat every argument as untrusted. Authorization belongs inside each action or in guards surrounding
|
|
571
|
+
the explicit endpoint; a valid reference is not authorization. Action handlers receive the active
|
|
572
|
+
`RequestContext`, so they may resolve request-scoped providers through `context.container` and keep
|
|
573
|
+
request-local state isolated. Server Functions are mutation-oriented integration points, not query
|
|
574
|
+
loaders, a client data cache, or a bypass around DTO validation, guards, middleware, interceptors,
|
|
575
|
+
request scopes, or observability.
|
|
576
|
+
|
|
577
|
+
The transport security contract is intentionally explicit:
|
|
578
|
+
|
|
579
|
+
- Action ids match `[A-Za-z0-9_-]{1,128}`. References expose the id but protect its integrity with
|
|
580
|
+
HMAC-SHA-256 and an application-owned secret of at least 32 bytes. Rotating the secret invalidates
|
|
581
|
+
previously issued references; references do not encrypt action names.
|
|
582
|
+
- Requests must use `application/json`, send `x-fluo-react-action: 1`, and include an exact HTTP(S)
|
|
583
|
+
`Origin` from `allowedOrigins`. The custom header makes browser cross-origin calls non-simple, while
|
|
584
|
+
the origin allowlist supplies the CSRF policy.
|
|
585
|
+
- `maxBodyBytes` defaults to 64 KiB. The registry uses exact `rawBody` bytes when the adapter exposes
|
|
586
|
+
them and otherwise measures the normalized body. Configure the platform adapter's pre-parse body
|
|
587
|
+
limit to the same or lower value so oversized network bodies are rejected before JSON parsing.
|
|
588
|
+
- Arguments and results allow only `null`, booleans, finite numbers, strings, dense arrays, and plain
|
|
589
|
+
objects. Circular values, sparse arrays, symbol/non-enumerable properties, prototype-sensitive
|
|
590
|
+
keys, class instances, `undefined`, `bigint`, and non-finite numbers are rejected. Nesting defaults
|
|
591
|
+
to 32 levels and cannot be configured above 128.
|
|
592
|
+
- Results default to a 1 MiB serialized limit. Client responses also default to 1 MiB.
|
|
593
|
+
|
|
594
|
+
Expected failures use the existing fluo HTTP error envelope with stable codes:
|
|
595
|
+
|
|
596
|
+
| Condition | Status | Code |
|
|
597
|
+
| --- | ---: | --- |
|
|
598
|
+
| malformed request shape | 400 | `REACT_SERVER_FUNCTION_INVALID_REQUEST` |
|
|
599
|
+
| unsafe arguments | 400 | `REACT_SERVER_FUNCTION_ARGUMENT_SERIALIZATION_FAILED` |
|
|
600
|
+
| missing/invalid request marker | 403 | `REACT_SERVER_FUNCTION_CSRF_REJECTED` |
|
|
601
|
+
| missing/unapproved origin | 403 | `REACT_SERVER_FUNCTION_ORIGIN_REJECTED` |
|
|
602
|
+
| invalid, tampered, or retired action reference | 404 | `REACT_SERVER_FUNCTION_ACTION_NOT_FOUND` |
|
|
603
|
+
| oversized request | 413 | `REACT_SERVER_FUNCTION_PAYLOAD_TOO_LARGE` |
|
|
604
|
+
| non-JSON content type | 415 | `REACT_SERVER_FUNCTION_UNSUPPORTED_MEDIA_TYPE` |
|
|
605
|
+
| unsafe action result | 500 | `REACT_SERVER_FUNCTION_RESULT_SERIALIZATION_FAILED` |
|
|
606
|
+
| oversized action result | 500 | `REACT_SERVER_FUNCTION_RESULT_TOO_LARGE` |
|
|
607
|
+
|
|
608
|
+
This prototype does not scan for `"use server"`, transform modules, discover exports, generate
|
|
609
|
+
references automatically, encrypt action ids, or provide database mutation conventions. The stable
|
|
610
|
+
root and `@fluojs/react/client` remain isolated from all Server Function code.
|
|
611
|
+
|
|
612
|
+
## RSC Graduation Policy
|
|
613
|
+
|
|
614
|
+
`@fluojs/react/rsc` is not currently exported. The canonical
|
|
615
|
+
[React RSC graduation policy](../../docs/contracts/react-rsc-graduation.md) records the evidence that
|
|
616
|
+
must exist before the experimental APIs can become stable: exact React/renderer compatibility,
|
|
617
|
+
versioned manifest and Server Function transport contracts, browser/server bundle separation,
|
|
618
|
+
SSR/CSR/prerendering behavior, hydration mismatch and error recovery, safe transfer rules for
|
|
619
|
+
private/auth/cookie-bearing data, HTTP route ownership, #2506 navigation isolation, executable
|
|
620
|
+
runtime/bundler coverage, bilingual docs, and Changesets release evidence.
|
|
621
|
+
|
|
622
|
+
When every gate is approved, `@fluojs/react/rsc` becomes the canonical implementation while
|
|
623
|
+
`@fluojs/react/experimental/rsc` remains a tested re-export for the documented deprecation window.
|
|
624
|
+
The stable package root remains RSC-free. The policy currently marks graduation as blocked, so this
|
|
625
|
+
documentation change neither adds the stable subpath nor starts the deprecation window.
|
|
626
|
+
|
|
627
|
+
## Current Limitations
|
|
628
|
+
|
|
629
|
+
This package currently does **not** provide:
|
|
630
|
+
|
|
631
|
+
- a stable RSC root or `@fluojs/react/rsc` subpath; RSC is available only from the explicitly unstable
|
|
632
|
+
`@fluojs/react/experimental/rsc` prototype
|
|
633
|
+
- automatic `"use server"` transforms/export discovery or a built-in Flight renderer/build plugin
|
|
634
|
+
- SPA document swapping, a client data/loader cache, and navigation prefetching
|
|
635
|
+
- a Next.js App Router, TanStack route tree, Angular `Routes[]`, file-route scanner, or React-owned
|
|
636
|
+
`routes: []` table
|
|
637
|
+
- automatic client bundle generation
|
|
638
|
+
- filesystem scanning or automatic manifest file discovery; pass an already-loaded manifest value to
|
|
639
|
+
`@fluojs/react/vite`
|
|
640
|
+
- automatic serialization of arbitrary data into `bootstrapScriptContent`
|
|
641
|
+
- Node-only `react-dom/server` pipeable stream root APIs such as `renderToPipeableStream(...)`
|
|
642
|
+
|
|
643
|
+
## Public API
|
|
644
|
+
|
|
645
|
+
- `Router` — class decorator that writes HTTP controller metadata plus React router marker metadata.
|
|
646
|
+
- `Path` — method decorator that writes HTTP `GET` route metadata plus React render metadata.
|
|
647
|
+
- `getReactRouterMetadata` — reads React router marker metadata from a router class.
|
|
648
|
+
- `getReactPathMetadata` — reads React render metadata from a router method.
|
|
649
|
+
- `ReactModule` — runtime-neutral module facade whose `forRoot(...)` registers React routers through
|
|
650
|
+
the existing fluo module/controller metadata path.
|
|
651
|
+
- `createReactServerEntry` — creates a runtime-neutral React server entry returned by page handlers
|
|
652
|
+
for Web Streams SSR.
|
|
653
|
+
- `renderReactResponse` — renders one React server entry to a fluo HTML response with lazy
|
|
654
|
+
`react-dom/server` loading.
|
|
655
|
+
- `ReactAssetMap`, `ReactBootstrapAsset`, and `ReactBootstrapScriptDescriptor` — type-only contracts
|
|
656
|
+
for build-produced asset maps and React DOM bootstrap script/module entries.
|
|
657
|
+
- `ReactModuleOptions` — options accepted by `ReactModule.forRoot(...)`, including `controllers`,
|
|
658
|
+
`imports`, `providers`, `exports`, and module-level `middleware`.
|
|
659
|
+
- `ReactServerEntry`, `ReactServerEntryOptions`, `ReactServerEntryHeaders`,
|
|
660
|
+
`ReactRecoverableErrorHandler`, `ReactRecoverableErrorContext`, `ReactRenderContext`,
|
|
661
|
+
`ReactReadableStream`, `ReactReadableStreamRenderer`, `ReactReadableStreamRenderOptions`, and
|
|
662
|
+
`RenderReactResponseOptions` — type-only contracts for streamed React SSR and hydration assets.
|
|
663
|
+
- `ReactScaffoldPhase` — type-only planning marker for the `0.1.0` scaffold surface.
|
|
664
|
+
- `ReactRouterMetadata`, `ReactPathMetadata`, `ReactPathOptions` — type-only metadata contracts for
|
|
665
|
+
diagnostics and future rendering integration.
|
|
666
|
+
- `@fluojs/react/vite` subpath — `createReactViteAssetManifest(...)` plus
|
|
667
|
+
`ReactViteBuildManifest`, `ReactViteBuildManifestChunk`, `ReactViteManifestOptions`,
|
|
668
|
+
`ReactViteManifestDiagnostic`, `ReactViteAssetManifest`, `ReactViteHydrationOptions`,
|
|
669
|
+
`ReactViteJavaScriptAssets`, `ReactViteBootstrapData`, and `ReactViteResolvedEntry` for parsing
|
|
670
|
+
Vite manifests into the stable hydration asset contract without importing Vite from the root.
|
|
671
|
+
- `@fluojs/react/client` subpath — `Link`, `ReactClientRouterProvider`,
|
|
672
|
+
`createReactRouteSnapshot(...)`, `useRouter()`, `usePathname()`, `useParams()`,
|
|
673
|
+
`useSearchParams()`, `useNavigation()`, and `useRouterState()` for progressive HTTP-first browser
|
|
674
|
+
navigation without widening the root package or adding a client route grammar.
|
|
675
|
+
- `@fluojs/react/experimental/rsc` subpath — `inspectReactRscEnvironment(...)`,
|
|
676
|
+
`createReactRscManifest(...)`, `createReactFlightResponse(...)`, exact-version and Flight content
|
|
677
|
+
type constants, diagnostics, client-reference/server-module mapping types, signed
|
|
678
|
+
`createReactServerFunctionRegistry(...)`, explicit `createReactServerFunctionClient(...)`, stable
|
|
679
|
+
Server Function error codes, and related transport types without any root or stable-client
|
|
680
|
+
re-export.
|
|
681
|
+
|
|
682
|
+
## Related Packages
|
|
683
|
+
|
|
684
|
+
- `@fluojs/core`: Provides the standard `@Module` decorator used by the scaffold.
|
|
685
|
+
- `@fluojs/http`: Provides the controller, route, DTO, guard, interceptor, header, and version
|
|
686
|
+
metadata pipeline reused by `@Router(...)` and `@Path(...)`.
|
|
687
|
+
- `@fluojs/runtime`: Future React integration work is expected to compose with runtime bootstrap
|
|
688
|
+
contracts without widening the root import boundary.
|
|
689
|
+
- `@fluojs/vite`: Owns Vite's TC39 decorator transform boundary. It does not parse React hydration
|
|
690
|
+
manifests; use `@fluojs/react/vite` for React server/client asset mapping.
|
|
691
|
+
- Application-selected Flight renderer: Encodes RSC payloads and consumes renderer-specific build
|
|
692
|
+
manifests. The experimental subpath models the compatibility, manifest, and HTTP response seams
|
|
693
|
+
without importing a renderer package.
|
|
694
|
+
|
|
695
|
+
## Example Sources
|
|
696
|
+
|
|
697
|
+
- `packages/react/src/index.ts`
|
|
698
|
+
- `packages/react/src/vite.ts`
|
|
699
|
+
- `packages/react/src/client.ts`
|
|
700
|
+
- `packages/react/src/client.test.ts`
|
|
701
|
+
- `packages/react/src/experimental/rsc.ts`
|
|
702
|
+
- `packages/react/src/experimental/rsc.test.ts`
|
|
703
|
+
- `packages/react/src/experimental/rsc-diagnostics.test.ts`
|
|
704
|
+
- `packages/react/src/experimental/rsc-flight.test.ts`
|
|
705
|
+
- `packages/react/src/experimental/rsc-manifest.test.ts`
|
|
706
|
+
- `packages/react/src/experimental/server-functions-server.ts`
|
|
707
|
+
- `packages/react/src/experimental/server-functions-client.ts`
|
|
708
|
+
- `packages/react/src/experimental/server-functions-dispatch.test.ts`
|
|
709
|
+
- `packages/react/src/experimental/server-functions-security.test.ts`
|
|
710
|
+
- `packages/react/src/experimental/server-functions-client.test.ts`
|
|
711
|
+
- `packages/react/src/vite/create-asset-manifest.ts`
|
|
712
|
+
- `packages/react/src/decorators.ts`
|
|
713
|
+
- `packages/react/src/server-entry.ts`
|
|
714
|
+
- `packages/react/src/render.ts`
|
|
715
|
+
- `packages/react/src/module.ts`
|
|
716
|
+
- `packages/react/src/render.test.ts`
|
|
717
|
+
- `packages/react/src/dispatcher-ssr.test.ts`
|
|
718
|
+
- `packages/react/src/hydration-assets.test.ts`
|
|
719
|
+
- `packages/react/src/vite.test.ts`
|
|
720
|
+
- `packages/react/src/lifecycle-pipeline.test.ts`
|
|
721
|
+
- `packages/react/src/module.test.ts`
|
|
722
|
+
- `packages/react/src/decorators.test.ts`
|
|
723
|
+
- `packages/react/src/index.test.ts`
|
|
724
|
+
- `examples/react-stable-ssr/README.md`
|
|
725
|
+
- `examples/react-stable-ssr/src/app.test.ts`
|
|
726
|
+
- `examples/react-vite-ssr/README.md`
|
|
727
|
+
- `examples/react-vite-ssr/src/app.test.ts`
|
|
728
|
+
- `examples/react-vite-ssr/src/hydration.test.ts`
|
|
729
|
+
- `examples/react-vite-ssr/tests/production-hydration.spec.ts`
|