@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.
Files changed (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.ko.md +715 -0
  3. package/README.md +729 -0
  4. package/dist/client/errors.d.ts +12 -0
  5. package/dist/client/errors.d.ts.map +1 -0
  6. package/dist/client/errors.js +19 -0
  7. package/dist/client/hooks.d.ts +38 -0
  8. package/dist/client/hooks.d.ts.map +1 -0
  9. package/dist/client/hooks.js +56 -0
  10. package/dist/client/link.d.ts +13 -0
  11. package/dist/client/link.d.ts.map +1 -0
  12. package/dist/client/link.js +39 -0
  13. package/dist/client/provider.d.ts +12 -0
  14. package/dist/client/provider.d.ts.map +1 -0
  15. package/dist/client/provider.js +54 -0
  16. package/dist/client/search-params.d.ts +17 -0
  17. package/dist/client/search-params.d.ts.map +1 -0
  18. package/dist/client/search-params.js +39 -0
  19. package/dist/client/snapshot.d.ts +15 -0
  20. package/dist/client/snapshot.d.ts.map +1 -0
  21. package/dist/client/snapshot.js +57 -0
  22. package/dist/client/store.d.ts +26 -0
  23. package/dist/client/store.d.ts.map +1 -0
  24. package/dist/client/store.js +138 -0
  25. package/dist/client/types.d.ts +63 -0
  26. package/dist/client/types.d.ts.map +1 -0
  27. package/dist/client/types.js +1 -0
  28. package/dist/client.d.ts +9 -0
  29. package/dist/client.d.ts.map +1 -0
  30. package/dist/client.js +5 -0
  31. package/dist/decorators.d.ts +80 -0
  32. package/dist/decorators.d.ts.map +1 -0
  33. package/dist/decorators.js +194 -0
  34. package/dist/experimental/rsc-diagnostics.d.ts +13 -0
  35. package/dist/experimental/rsc-diagnostics.d.ts.map +1 -0
  36. package/dist/experimental/rsc-diagnostics.js +58 -0
  37. package/dist/experimental/rsc-flight-response.d.ts +15 -0
  38. package/dist/experimental/rsc-flight-response.d.ts.map +1 -0
  39. package/dist/experimental/rsc-flight-response.js +76 -0
  40. package/dist/experimental/rsc-manifest.d.ts +13 -0
  41. package/dist/experimental/rsc-manifest.d.ts.map +1 -0
  42. package/dist/experimental/rsc-manifest.js +88 -0
  43. package/dist/experimental/rsc-types.d.ts +118 -0
  44. package/dist/experimental/rsc-types.d.ts.map +1 -0
  45. package/dist/experimental/rsc-types.js +50 -0
  46. package/dist/experimental/rsc.d.ts +11 -0
  47. package/dist/experimental/rsc.d.ts.map +1 -0
  48. package/dist/experimental/rsc.js +8 -0
  49. package/dist/experimental/server-functions-client.d.ts +14 -0
  50. package/dist/experimental/server-functions-client.d.ts.map +1 -0
  51. package/dist/experimental/server-functions-client.js +120 -0
  52. package/dist/experimental/server-functions-configuration.d.ts +17 -0
  53. package/dist/experimental/server-functions-configuration.d.ts.map +1 -0
  54. package/dist/experimental/server-functions-configuration.js +68 -0
  55. package/dist/experimental/server-functions-errors.d.ts +19 -0
  56. package/dist/experimental/server-functions-errors.d.ts.map +1 -0
  57. package/dist/experimental/server-functions-errors.js +22 -0
  58. package/dist/experimental/server-functions-reference.d.ts +22 -0
  59. package/dist/experimental/server-functions-reference.d.ts.map +1 -0
  60. package/dist/experimental/server-functions-reference.js +76 -0
  61. package/dist/experimental/server-functions-serialization.d.ts +37 -0
  62. package/dist/experimental/server-functions-serialization.d.ts.map +1 -0
  63. package/dist/experimental/server-functions-serialization.js +158 -0
  64. package/dist/experimental/server-functions-server.d.ts +14 -0
  65. package/dist/experimental/server-functions-server.d.ts.map +1 -0
  66. package/dist/experimental/server-functions-server.js +176 -0
  67. package/dist/experimental/server-functions-types.d.ts +86 -0
  68. package/dist/experimental/server-functions-types.d.ts.map +1 -0
  69. package/dist/experimental/server-functions-types.js +35 -0
  70. package/dist/index.d.ts +10 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +4 -0
  73. package/dist/module.d.ts +40 -0
  74. package/dist/module.d.ts.map +1 -0
  75. package/dist/module.js +58 -0
  76. package/dist/render-stream.d.ts +27 -0
  77. package/dist/render-stream.d.ts.map +1 -0
  78. package/dist/render-stream.js +135 -0
  79. package/dist/render.d.ts +50 -0
  80. package/dist/render.d.ts.map +1 -0
  81. package/dist/render.js +201 -0
  82. package/dist/server-entry.d.ts +87 -0
  83. package/dist/server-entry.d.ts.map +1 -0
  84. package/dist/server-entry.js +126 -0
  85. package/dist/types.d.ts +11 -0
  86. package/dist/types.d.ts.map +1 -0
  87. package/dist/types.js +1 -0
  88. package/dist/vite/asset-utils.d.ts +57 -0
  89. package/dist/vite/asset-utils.d.ts.map +1 -0
  90. package/dist/vite/asset-utils.js +184 -0
  91. package/dist/vite/create-asset-manifest.d.ts +9 -0
  92. package/dist/vite/create-asset-manifest.d.ts.map +1 -0
  93. package/dist/vite/create-asset-manifest.js +127 -0
  94. package/dist/vite/diagnostics.d.ts +27 -0
  95. package/dist/vite/diagnostics.d.ts.map +1 -0
  96. package/dist/vite/diagnostics.js +47 -0
  97. package/dist/vite/parse-manifest.d.ts +18 -0
  98. package/dist/vite/parse-manifest.d.ts.map +1 -0
  99. package/dist/vite/parse-manifest.js +168 -0
  100. package/dist/vite/types.d.ts +187 -0
  101. package/dist/vite/types.d.ts.map +1 -0
  102. package/dist/vite/types.js +59 -0
  103. package/dist/vite.d.ts +3 -0
  104. package/dist/vite.d.ts.map +1 -0
  105. package/dist/vite.js +1 -0
  106. package/package.json +68 -0
package/README.ko.md ADDED
@@ -0,0 +1,715 @@
1
+ # @fluojs/react
2
+
3
+ <p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
4
+
5
+ fluo 애플리케이션을 위한 런타임 중립 React 통합입니다.
6
+
7
+ ## 목차
8
+
9
+ - [설치](#설치)
10
+ - [사용 시점](#사용-시점)
11
+ - [Stable SSR Mental Model](#stable-ssr-mental-model)
12
+ - [런타임 및 피어 계약](#런타임-및-피어-계약)
13
+ - [Phase Boundaries](#phase-boundaries)
14
+ - [ReactModule Registration](#reactmodule-registration)
15
+ - [Router 및 Path Decorators](#router-및-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
+ - [현재 제한 사항](#현재-제한-사항)
24
+ - [Public API](#public-api)
25
+ - [관련 패키지](#관련-패키지)
26
+ - [예제 소스](#예제-소스)
27
+
28
+ ## 설치
29
+
30
+ 이 패키지의 첫 공개 릴리스 목표는 `0.1.0`입니다. manifest는 `0.0.0`에서 시작해
31
+ Changesets가 표준 릴리스 워크플로를 통해 초기 `0.1.0` 버전을 게시할 수 있게 합니다.
32
+
33
+ 패키지가 게시되면 React와 React DOM을 peer로 함께 설치합니다.
34
+
35
+ ```bash
36
+ npm install @fluojs/react react react-dom
37
+ ```
38
+
39
+ ## 사용 시점
40
+
41
+ React page handler가 API controller와 구문상 구분되어야 하지만 fluo module graph 및 HTTP metadata
42
+ pipeline에는 그대로 참여해야 할 때 이 패키지를 사용하세요. `ReactModule.forRoot(...)`는 React
43
+ router를 일반 module controller metadata에 배치하고, `@Router(...)`와 `@Path(...)`는 `@fluojs/http`
44
+ controller 및 `GET` route metadata 위의 React facade이므로 request DTO binding, versioning,
45
+ guards, interceptors, headers, route validation, matching, dispatch는 계속 HTTP runtime contract를 사용합니다.
46
+
47
+ ## Stable SSR Mental Model
48
+
49
+ 안정 `0.1.0` 모델은 HTTP-first React SSR입니다. `@Router(...)`와 `@Path(...)`는
50
+ `@fluojs/http` metadata 위의 lexical React facade입니다. 즉 class와 method를 readability 및
51
+ diagnostics를 위한 React page surface로 표시한 뒤, HTTP runtime이 이미 이해하는 controller와 `GET`
52
+ route metadata를 기록합니다. URL matching은 React가 아니라 `@fluojs/http`가 계속 소유하므로 React
53
+ page path는 HTTP route grammar, conflict detection, versioning, DTO materialization, validation,
54
+ guards, interceptors, headers, module middleware, request scope, request lifecycle을 그대로 상속합니다.
55
+
56
+ 따라서 이 패키지는 Next.js App Router clone, React Server Components framework, TanStack route tree,
57
+ Angular `Routes[]` table, file-route scanner, primary React-owned `routes: []` configuration model이
58
+ **아닙니다**. React router는 page 형태를 가진 HTTP handler로 이해하세요. Route discovery와 dispatch는
59
+ 기존 fluo module/controller pipeline에 남아 있고, page handler는 일반 값을 반환하거나 streamed HTML이
60
+ 필요할 때 `createReactServerEntry(...)`를 반환합니다.
61
+
62
+ ## 런타임 및 피어 계약
63
+
64
+ 루트 `@fluojs/react` import는 런타임 중립입니다. import 시점에 Node.js built-in, Vite,
65
+ `react-dom/server`, React Server Components 패키지, Server Functions 코드를 eager load하면 안 됩니다.
66
+
67
+ `react`와 `react-dom`은 애플리케이션이 React 런타임 버전을 소유하도록 peer dependencies로 선언합니다.
68
+ 패키지 루트는 SSR helper를 노출하지만 React server entry를 렌더링할 때만 `react-dom/server`를 lazy
69
+ resolve합니다.
70
+
71
+ ## Phase Boundaries
72
+
73
+ `@fluojs/react`는 안정 root가 작고 runtime-neutral 상태로 남도록 명시적인 subpath boundary를 사용합니다.
74
+
75
+ - **root `@fluojs/react`** — 안정 `0.1.0` SSR MVP 계약입니다. `ReactModule.forRoot(...)`,
76
+ `@Router(...)`, `@Path(...)`, metadata reader, `createReactServerEntry(...)`,
77
+ `renderReactResponse(...)`, Web Streams SSR, 명시적인 hydration asset option을 포함합니다.
78
+ - **`@fluojs/react/vite`** — Vite build manifest parsing, React server/client entry selection,
79
+ deterministic stylesheet 및 JavaScript ordering, manifest diagnostics, hydration option 생성을 위한
80
+ 경계입니다. Root package는 여전히 manifest discovery나 scanning 없이 명시적인 asset option만 받습니다.
81
+ - **`@fluojs/react/client`** — progressive anchor, HTTP-first full-document navigation,
82
+ hydration-safe URL/path-param/navigation lifecycle hook을 위한 경계입니다. Browser API는
83
+ runtime-neutral root에서 계속 분리됩니다.
84
+ - **`@fluojs/react/experimental/rsc`** — exact-version compatibility diagnostics,
85
+ client-reference/server-module manifest seam, 일반 fluo HTTP handler를 통한 Flight payload response를
86
+ 제공하고 일반 fluo HTTP handler 위에 signed Server Function transport를 추가하는 명시적으로 불안정한
87
+ React Server Components prototype입니다. Stable root나 `@fluojs/react/client`에서는 export하지 않습니다.
88
+
89
+ ## ReactModule Registration
90
+
91
+ 일반 fluo module import 안에서 `ReactModule.forRoot({ controllers: [...] })`를 사용해 React router를
92
+ 등록합니다. 반환되는 module은 일반 fluo module definition입니다. `imports`, `providers`, `exports`,
93
+ `middleware`를 포함할 수 있으며, 각 필드는 `@Module(...)` metadata와 같은 visibility 및 lifecycle rule을
94
+ 유지합니다.
95
+
96
+ ```tsx
97
+ import { Module, Inject } from '@fluojs/core';
98
+ import { ReactModule, Router, Path } from '@fluojs/react';
99
+
100
+ class DashboardPresenter {
101
+ render() {
102
+ return { page: 'dashboard' };
103
+ }
104
+ }
105
+
106
+ @Inject(DashboardPresenter)
107
+ @Router('/dashboard')
108
+ class DashboardRouter {
109
+ constructor(private readonly presenter: DashboardPresenter) {}
110
+
111
+ @Path('/')
112
+ index() {
113
+ return this.presenter.render();
114
+ }
115
+ }
116
+
117
+ @Module({
118
+ imports: [
119
+ ReactModule.forRoot({
120
+ controllers: [DashboardRouter],
121
+ providers: [DashboardPresenter],
122
+ }),
123
+ ],
124
+ })
125
+ class AppModule {}
126
+ ```
127
+
128
+ `ReactModule`은 URL matching을 소유하지 않습니다. 이 module을 통해 등록한 router는 일반 HTTP handler
129
+ source가 되므로 `createHandlerMapping(...)`과 `Dispatcher`가 계속 duplicate route detection,
130
+ module-level middleware, request scope 생성, guard 및 interceptor 실행, route versioning을 담당합니다.
131
+
132
+ ## Router 및 Path Decorators
133
+
134
+ `@Router(basePath)`는 class를 React router로 표시하고 `@Controller(basePath)`와 동등한 HTTP
135
+ controller metadata를 기록합니다. 또한 diagnostics 및 향후 rendering integration을 위해
136
+ `getReactRouterMetadata(...)`로 읽을 수 있는 React router marker metadata를 저장합니다.
137
+
138
+ `@Path(path, options?)`는 method를 React page route로 표시하고 `@Get(path)`와 동등한 HTTP `GET`
139
+ route metadata를 기록합니다. 또한 `getReactPathMetadata(...)`로 읽을 수 있는 React render
140
+ metadata를 저장합니다. 이 phase에서 optional `options` 객체는 metadata일 뿐이며 HTTP matching이나
141
+ dispatch를 변경하지 않습니다.
142
+
143
+ ```tsx
144
+ import { Router, Path } from '@fluojs/react';
145
+ import { FromPath, Optional, RequestDto, FromQuery } from '@fluojs/http';
146
+
147
+ class DashboardEditRequest {
148
+ @FromPath('id')
149
+ id = '';
150
+
151
+ @Optional()
152
+ @FromQuery('tab')
153
+ tab?: string;
154
+ }
155
+
156
+ @Router('/dashboard')
157
+ class DashboardRouter {
158
+ @Path('/:id/edit')
159
+ @RequestDto(DashboardEditRequest)
160
+ edit(input: DashboardEditRequest) {
161
+ return { page: 'dashboard-edit', input };
162
+ }
163
+ }
164
+ ```
165
+
166
+ React page path는 기존 `@fluojs/http` route grammar를 그대로 사용합니다. 지원 범위는 literal
167
+ segment와 full-segment `:param` placeholder뿐입니다. wildcard, catch-all route, optional segment,
168
+ regex-like token, `user-:id` 같은 mixed literal/parameter segment, `:id.json` 같은 suffix param은
169
+ 지원하지 않습니다.
170
+
171
+ [HTTP catch-all route grammar 결정](../../docs/architecture/http-catch-all-route-grammar.ko.md)은
172
+ wildcard 도입을 유예합니다. React는 자체 syntax를 추가하지 않습니다. Page handler는 명시적인 server
173
+ route를 유지해야 하며, 향후 catch-all은 먼저 승인된 `@fluojs/http` contract가 되어야 합니다.
174
+
175
+ ## Web Streams SSR
176
+
177
+ React page handler에서 `createReactServerEntry(...)`를 반환하면 기존 fluo HTTP dispatcher를 통해 HTML을
178
+ streaming합니다. Guard, interceptor, module middleware, route header, `@HttpCode(...)`, DTO binding,
179
+ request scope, duplicate route detection은 모두 `renderReactResponse(...)`가 HTML response를 finalize하기
180
+ 전에 실행됩니다.
181
+
182
+ ```tsx
183
+ import { HttpCode, RequestDto, FromPath } from '@fluojs/http';
184
+ import { Router, Path, createReactServerEntry } from '@fluojs/react';
185
+
186
+ class DashboardRequest {
187
+ @FromPath('id')
188
+ id = '';
189
+ }
190
+
191
+ @Router('/dashboard')
192
+ class DashboardRouter {
193
+ @HttpCode(206)
194
+ @Path('/:id')
195
+ @RequestDto(DashboardRequest)
196
+ show(input: DashboardRequest) {
197
+ return createReactServerEntry(<main>Dashboard {input.id}</main>, {
198
+ headers: { 'x-react-page': 'dashboard' },
199
+ onRecoverableError(error, context) {
200
+ // 애플리케이션 logger로 보고하세요. response status는 이미 committed 상태입니다.
201
+ void error;
202
+ void context;
203
+ },
204
+ });
205
+ }
206
+ }
207
+ ```
208
+
209
+ Renderer는 기본적으로 `react-dom/server`의 `renderToReadableStream(...)`을 사용하고, Suspense streaming을
210
+ 보존하며, `Content-Type: text/html; charset=utf-8`을 기록합니다. Adapter가 제공한
211
+ `RequestContext.request.signal`을 전달하고, shell render failure는 response byte가 commit되기 전에
212
+ throw합니다. Recoverable Suspense error는 `onRecoverableError`로 보고되며 이미 committed된 status를 다시
213
+ 쓰지 않습니다. Handler가 entry를 dispatcher에 반환하지 않고 직접 response를 finalize해야 할 때만
214
+ `renderReactResponse(entry, requestContext)`를 호출하세요.
215
+
216
+ ## Hydration Asset Contract
217
+
218
+ `createReactServerEntry(...)`는 명시적인 React DOM hydration asset option을 받아 Web Streams renderer에
219
+ 전달합니다.
220
+
221
+ - `bootstrapScripts`는 classic bootstrap `<script>` tag를 emit합니다. 같은 `src`를 가진 중복 entry는
222
+ React DOM에 전달되기 전에 제거됩니다.
223
+ - `bootstrapModules`는 module bootstrap `<script type="module">` tag를 emit하며 동일한 중복 제거 규칙을
224
+ 적용합니다.
225
+ - `bootstrapScriptContent`는 신뢰된 inline script content를 제공된 그대로 emit합니다. fluo는 임의 값을 이
226
+ 문자열로 serialize하지 않습니다. 신뢰된 build-time data 또는 애플리케이션이 승인된 safety contract로
227
+ escape한 content에만 사용하세요.
228
+ - `nonce`는 React DOM이 emit하는 bootstrap script에 붙일 CSP nonce입니다.
229
+ - `identifierPrefix`는 server-rendered `useId()` output과 client hydration이 같은 prefix를 쓰도록 React
230
+ DOM에 전달됩니다.
231
+ - `assetMap`은 신뢰된 build 산출 logical asset name과 public URL의 defensive snapshot입니다. fluo는 이
232
+ snapshot을 custom renderer에 전달하며, markup에 영향을 준다면 애플리케이션이 server root component와
233
+ client `hydrateRoot(...)` 호출에 같은 데이터를 넘겨야 합니다.
234
+
235
+ ```tsx
236
+ const assetMap = {
237
+ 'main.js': '/assets/main.123.js',
238
+ 'styles.css': '/assets/styles.123.css',
239
+ } as const;
240
+
241
+ return createReactServerEntry(<App assetMap={assetMap} />, {
242
+ assetMap,
243
+ bootstrapModules: [assetMap['main.js']],
244
+ bootstrapScriptContent: 'window.__FLUO_ASSET_MAP__ = {"main.js":"/assets/main.123.js"};',
245
+ identifierPrefix: 'fluo-',
246
+ nonce: cspNonce,
247
+ });
248
+ ```
249
+
250
+ 애플리케이션이 hydration option을 직접 넘기는 경우 CSS/JS ordering은 계속 caller가 소유합니다.
251
+ `@fluojs/react/vite`는 manifest stylesheet 순서를 app-rendered document head 안에서 hydration script보다 먼저
252
+ 보존하고, 그 다음 manifest JavaScript order와 `bootstrapScripts` 및 `bootstrapModules`의 caller order를 중복
253
+ 제거 후 보존합니다. 이 패키지는 filesystem에서 Vite manifest를 발견하거나, client bundle을 생성하거나, 신뢰할 수
254
+ 없는 user data를 inline script로 serialize하지 않습니다.
255
+
256
+ ## Vite Asset Manifest Integration
257
+
258
+ Vite로 빌드한 React 애플리케이션에서 이미 로드한 Vite manifest를 `createReactServerEntry(...)`가 받는 안정
259
+ hydration option으로 바꿔야 할 때 `@fluojs/react/vite`를 사용합니다.
260
+
261
+ ```tsx
262
+ import { createReactViteAssetManifest } from '@fluojs/react/vite';
263
+ import { createReactServerEntry } from '@fluojs/react';
264
+
265
+ const assets = createReactViteAssetManifest({
266
+ base: '/assets/',
267
+ entries: {
268
+ client: 'src/entry-client.tsx',
269
+ server: 'src/entry-server.tsx',
270
+ },
271
+ manifest: viteManifest,
272
+ nonce: cspNonce,
273
+ });
274
+
275
+ if (!assets.ok) {
276
+ throw new Error(assets.diagnostics.map((diagnostic) => diagnostic.message).join('\n'));
277
+ }
278
+
279
+ return createReactServerEntry(<App assetMap={assets.manifest.assetMap} />, {
280
+ ...assets.manifest.hydrationOptions,
281
+ });
282
+ ```
283
+
284
+ `createReactViteAssetManifest(...)`는 이미 로드된 manifest 값을 받습니다. Filesystem을 읽거나, Vite를
285
+ 실행하거나, client bundle을 만들거나, root package에 Vite dependency를 추가하지 않습니다. 파싱하는 manifest
286
+ schema는 Vite client manifest 형태와 호환됩니다.
287
+
288
+ | Field | Required | Meaning |
289
+ | --- | --- | --- |
290
+ | `file` | yes | chunk의 JavaScript output file입니다. 선택된 server/client entry는 `.js`, `.mjs`, `.cjs`로 끝나야 합니다. |
291
+ | `src` / `name` | no | 명시적인 `entries.server` 및 `entries.client` selector를 위한 secondary lookup key입니다. |
292
+ | `isEntry` / `isDynamicEntry` | no | diagnostics 및 future compatibility를 위해 보존되는 Vite entry marker입니다. |
293
+ | `imports` | no | static imported chunk id입니다. imported chunk는 client entry보다 먼저 정렬됩니다. |
294
+ | `css` | no | chunk가 emit한 stylesheet file입니다. 반환되는 `manifest.css`는 dependency order를 유지하고 중복을 제거합니다. |
295
+ | `assets` | no | 반환되는 `assetMap`에 복사되는 static asset file입니다. |
296
+
297
+ 성공 결과는 다음을 포함합니다.
298
+
299
+ - `manifest.hydrationOptions` — `createReactServerEntry(...)`에 바로 넘길 수 있는 `assetMap`,
300
+ `bootstrapModules`, `bootstrapScripts`, trusted `bootstrapScriptContent`, `identifierPrefix`, `nonce`입니다.
301
+ - `manifest.css` — hydration script보다 먼저 application-rendered document head에 넣을 stylesheet URL입니다.
302
+ - `manifest.js.modules` 및 `manifest.js.scripts` — Vite client import graph에서 나온 module script와 caller가
303
+ 제공한 classic script입니다.
304
+ - `manifest.assetMap` — manifest key, source name, emitted file, CSS, static asset을 public URL에 매핑한
305
+ defensive snapshot입니다.
306
+ - `manifest.serverEntry` 및 `manifest.clientEntry` — resolve된 React server/client entry입니다.
307
+
308
+ 예상 가능한 manifest failure는 throw하지 않고 diagnostics를 반환합니다. Stable diagnostic code는 다음입니다.
309
+
310
+ - `react-vite-manifest-missing-server-entry`
311
+ - `react-vite-manifest-missing-client-entry`
312
+ - `react-vite-manifest-malformed`
313
+ - `react-vite-manifest-unsupported-output-shape`
314
+
315
+ `@fluojs/react/vite`는 `@fluojs/vite`와 분리됩니다. `vite.config.ts`에서 fluo 애플리케이션의 TC39 decorator
316
+ transform이 필요하면 `@fluojs/vite`를 사용하세요. React SSR code에서 React build asset을 파싱해 기존 hydration
317
+ contract에 공급해야 하면 `@fluojs/react/vite`를 사용하세요. 두 패키지 모두 file route, React-only route grammar,
318
+ Next.js route segment convention, RSC bundler behavior, URL matching을 소유하지 않습니다.
319
+ 실행 가능한 `examples/react-vite-ssr/` 애플리케이션은 생성된 asset, streamed Suspense content,
320
+ 직접적인 React DOM hydration, client navigation subpath를 통해 이 경계를 보여줍니다.
321
+
322
+ ## Client Navigation Runtime
323
+
324
+ Client-owned route table을 추가하지 않고 hydrated React page에서 navigation control과 URL state가
325
+ 필요하면 `@fluojs/react/client`를 사용합니다. 활성 fluo HTTP request에서 initial snapshot을 만들고,
326
+ server rendering과 hydration에서 같은 snapshot을 `ReactClientRouterProvider`에 전달합니다. 이
327
+ request-scoped snapshot은 server/client URL state drift를 막고 기존 HTTP route match가 만든 path
328
+ param을 전달합니다. Client runtime은 route param을 직접 derive하거나 validate하지 않습니다.
329
+
330
+ ```tsx
331
+ import {
332
+ Link,
333
+ ReactClientRouterProvider,
334
+ createReactRouteSnapshot,
335
+ useNavigation,
336
+ usePathname,
337
+ useRouter,
338
+ useSearchParams,
339
+ } from '@fluojs/react/client';
340
+
341
+ function DashboardNav() {
342
+ const navigation = useNavigation();
343
+ const pathname = usePathname();
344
+ const router = useRouter();
345
+ const searchParams = useSearchParams();
346
+
347
+ return (
348
+ <nav aria-label="Dashboard">
349
+ <Link href="/dashboard/42/edit?tab=profile">Edit</Link>
350
+ <button type="button" onClick={() => router.push('/dashboard/42/edit?tab=profile')}>
351
+ Open editor
352
+ </button>
353
+ <output>{pathname} · {searchParams.get('tab')} · {navigation.status}</output>
354
+ </nav>
355
+ );
356
+ }
357
+
358
+ const initialSnapshot = createReactRouteSnapshot({
359
+ url: requestContext.request.url,
360
+ params: requestContext.request.params,
361
+ });
362
+
363
+ const app = (
364
+ <ReactClientRouterProvider initialSnapshot={initialSnapshot}>
365
+ <DashboardNav />
366
+ </ReactClientRouterProvider>
367
+ );
368
+ ```
369
+
370
+ Navigation contract는 의도적으로 HTTP-first입니다.
371
+
372
+ - `Link`는 항상 실제 `<a href>`를 렌더링하므로 pre-hydration click, JavaScript disabled 환경,
373
+ modified click, download, explicit target, cross-origin destination은 native browser behavior를
374
+ 유지합니다. Hydration 이후 same-origin HTTP(S) URL을 향한 unmodified primary click은
375
+ `router.push(...)`로 위임됩니다.
376
+ - `router.push(href)`는 `window.location.assign(...)`, `router.replace(href)`는
377
+ `window.location.replace(...)`를 사용합니다. 현재 URL의 pathname 또는 search(query string)를
378
+ 변경하는 same-origin destination은 full-document navigation을 수행하므로 fluo HTTP route matching,
379
+ `@RequestDto` binding/validation, guard, interceptor, redirect, not-found response, non-HTML response,
380
+ server failure가 계속 authoritative합니다.
381
+ - 현재 pathname과 search를 유지하고 fragment만 바꾸는 fragment-only destination은 same-document
382
+ 예외입니다. Browser는 새 HTTP request를 보내지 않고 `hashchange`를 발생시키며, 요청한 destination과
383
+ 일치하는 event가 route snapshot URL/hash를 갱신하면서 `push` 또는 `replace` lifecycle을 완료합니다.
384
+ Server request가 없으므로 해당 fragment 변경에서는 fluo HTTP route matching, `@RequestDto`
385
+ binding/validation, guard, interceptor가 실행되지 않습니다.
386
+ - 정규화된 destination이 현재 route snapshot과 같은 identical URL이면 router는
387
+ `window.location.assign(...)`이나 `window.location.replace(...)`를 호출하지 않습니다. 대신 요청한
388
+ navigation type과 destination을 포함한 `skipped` 상태를 노출합니다.
389
+ - `router.back()`은 `window.history.back()`에 위임합니다. `router.refresh()`는 문서화된 revalidation
390
+ mechanism으로 `window.location.reload()`를 사용하며 RSC, loader, client-data cache를 암시하지 않습니다.
391
+ - `usePathname()`, `useSearchParams()`, `useParams()`, `useRouterState()`는 provider의 immutable route
392
+ snapshot을 읽습니다. `popstate`와 `hashchange`는 URL-derived field를 갱신합니다. 새 server document 없이
393
+ history event가 pathname을 바꾸면 client route grammar로 추측하지 않고 stale path param을 비웁니다.
394
+ - `useNavigation()`은 `idle`, `navigating`, `refreshing`, `complete`, `error`, `skipped`를 노출합니다.
395
+ Full-document path/search transition은 일반적으로 현재 document를 `navigating` 또는 `refreshing`
396
+ 상태에서 떠나며, destination document는 server-owned `idle` snapshot으로 시작합니다. Fragment-only
397
+ transition은 일치하는 `hashchange` 이후 현재 document에서 `complete`가 됩니다.
398
+ - Router method는 cross-origin 또는 non-HTTP(S) destination을 `ReactClientNavigationError`로 거부합니다.
399
+ 이런 destination에는 일반 anchor를 사용하세요.
400
+
401
+ 이 phase는 `prefetch`를 의도적으로 제공하지 않습니다. 소유한 client data/render cache가 없으므로
402
+ prefetch를 제공하면 일관되게 revalidate하거나 consume할 수 없는 behavior를 약속하게 됩니다.
403
+
404
+ Client navigation에는 catch-all route가 필요하지 않습니다. `Link`는 실제 anchor로 남으므로 hydration
405
+ gap이나 JavaScript disabled 환경에서는 일반 full-document browser navigation으로 fallback합니다. 이후
406
+ server는 명시적인 `@Path(...)`/HTTP route를 match하거나 정상적인 not-found response를 반환합니다.
407
+ 의도적인 deployment-level document rewrite를 별도로 설정할 수 있지만, 이는 React route grammar를 만들거나
408
+ server DTO validation을 변경하지 않습니다.
409
+
410
+ ## Experimental RSC Prototype
411
+
412
+ > **Experimental contract:** `@fluojs/react/experimental/rsc`는 명시적인 graduation issue가 RSC API를
413
+ > 안정화하기 전까지 변경될 수 있습니다. 이 API를 root package에서 import하거나 semver-stable React
414
+ > framework internal로 간주하지 마세요.
415
+
416
+ Prototype은 **정확히** `react@19.2.6`, `react-dom@19.2.6`, 그리고 같은 버전의 Flight renderer를
417
+ 지원합니다. Version range와 canary version은 지원하지 않습니다. 애플리케이션이
418
+ `react-server-dom-webpack`을 선택한다면 같은 exact version으로 고정하세요. 이 package는 해당 renderer를
419
+ 설치하거나 import하거나 감싸지 않습니다. Experimental RSC subpath를 사용하지 않는 애플리케이션을 위한
420
+ stable root peer range는 더 넓게 유지됩니다.
421
+
422
+ RSC endpoint를 활성화하기 전에 `inspectReactRscEnvironment(...)`를 호출하세요. React version이 하나라도
423
+ 다르거나, runtime이 Web `ReadableStream`을 제공하지 않거나, application-owned build adapter가
424
+ client-reference manifest와 명시적인 server-to-client module map을 모두 제공하지 않으면 stable diagnostic을
425
+ 반환합니다.
426
+
427
+ ```ts
428
+ import {
429
+ REACT_RSC_SUPPORTED_VERSION,
430
+ inspectReactRscEnvironment,
431
+ } from '@fluojs/react/experimental/rsc';
432
+
433
+ const support = inspectReactRscEnvironment({
434
+ reactVersion: '19.2.6',
435
+ reactDomVersion: '19.2.6',
436
+ flightRendererVersion: REACT_RSC_SUPPORTED_VERSION,
437
+ runtime: { name: 'node', webStreams: typeof ReadableStream !== 'undefined' },
438
+ build: {
439
+ name: 'application-rsc-build',
440
+ clientReferenceManifest: true,
441
+ serverClientModuleMap: true,
442
+ },
443
+ });
444
+ ```
445
+
446
+ `createReactRscManifest(...)`는 초기 bundler-neutral module graph seam을 정의합니다. Client-reference key는
447
+ `{ id, chunks, name, async? }` metadata를 가리키고, 각 server module id는 export name을 해당
448
+ client-reference key에 매핑합니다. Helper는 defensive snapshot을 반환하고, 존재하지 않는 mapping target을
449
+ diagnostic으로 거부하며, file scan이나 bundle 생성을 수행하지 않습니다. 이 seam을 특정 React Flight
450
+ renderer manifest로 변환하는 일은 application build adapter의 책임입니다.
451
+
452
+ ```ts
453
+ import { createReactRscManifest } from '@fluojs/react/experimental/rsc';
454
+
455
+ const manifest = createReactRscManifest({
456
+ clientReferences: {
457
+ Counter: {
458
+ id: 'client:counter',
459
+ chunks: ['assets/counter.js'],
460
+ name: 'Counter',
461
+ },
462
+ },
463
+ serverClientModuleMap: {
464
+ 'server:dashboard': {
465
+ Counter: 'Counter',
466
+ },
467
+ },
468
+ });
469
+ ```
470
+
471
+ Flight encoding도 애플리케이션이 소유합니다. 선택한 renderer가 encoded text, byte 또는 Web
472
+ `ReadableStream<Uint8Array>`를 만든 뒤 일반 fluo HTTP controller나 React `@Path(...)` handler에서
473
+ `createReactFlightResponse(...)`를 반환하세요. 기존 dispatcher가 route metadata, middleware, guard,
474
+ interceptor, request scope, error, adapter response writing을 계속 소유합니다. Helper는 고정된
475
+ `text/x-component; charset=utf-8` content type만 추가하며 별도 router를 만들지 않습니다.
476
+
477
+ ```ts
478
+ import { Controller, Get } from '@fluojs/http';
479
+ import { createReactFlightResponse } from '@fluojs/react/experimental/rsc';
480
+
481
+ @Controller('/rsc')
482
+ class RscController {
483
+ @Get('/dashboard')
484
+ dashboard() {
485
+ const payload = applicationFlightRenderer.render({ page: 'dashboard' });
486
+ return createReactFlightResponse(payload);
487
+ }
488
+ }
489
+ ```
490
+
491
+ RSC manifest와 Flight response helper 자체는 Flight encoder/decoder, Webpack 또는 Vite RSC plugin,
492
+ automatic module graph discovery, client bundle generation, file route, route segment, React-owned URL
493
+ matcher를 제공하지 않습니다. 아래 Server Function transport는 같은 불안정 subpath에 별도로 opt-in하는
494
+ prototype입니다.
495
+
496
+ ## Experimental Server Functions
497
+
498
+ `createReactServerFunctionRegistry(...)`는 server-only action을 정의하고 HMAC-SHA-256 reference를 발급하며
499
+ 호출을 검증합니다. Router를 직접 만들지는 않습니다. 명시적인 일반 fluo `@Post(...)` route에
500
+ `registry.invoke(context)`를 mount해야 module middleware, route/controller guard, interceptor,
501
+ request-scoped provider, request observer, error envelope, adapter response writing이 기존 HTTP lifecycle에
502
+ 남습니다.
503
+
504
+ ```ts
505
+ import {
506
+ BadRequestException,
507
+ Controller,
508
+ Post,
509
+ type RequestContext,
510
+ UnauthorizedException,
511
+ } from '@fluojs/http';
512
+ import { createReactServerFunctionRegistry } from '@fluojs/react/experimental/rsc';
513
+
514
+ const actions = createReactServerFunctionRegistry({
515
+ actions: {
516
+ updateProfile(args, context) {
517
+ const subject = context.principal?.subject;
518
+ if (subject === undefined) {
519
+ throw new UnauthorizedException();
520
+ }
521
+ const name = args[0];
522
+ if (typeof name !== 'string') {
523
+ throw new BadRequestException('Profile name must be a string.');
524
+ }
525
+ return { name, subject, updated: true };
526
+ },
527
+ },
528
+ allowedOrigins: ['https://app.example.com'],
529
+ crypto: globalThis.crypto,
530
+ secret: serverFunctionSecret,
531
+ maxBodyBytes: 64 * 1024,
532
+ });
533
+
534
+ export const updateProfileReference = await actions.createReference('updateProfile');
535
+
536
+ @Controller('/_fluo')
537
+ class ReactActionController {
538
+ @Post('/actions')
539
+ invoke(_input: undefined, context: RequestContext) {
540
+ return actions.invoke(context);
541
+ }
542
+ }
543
+ ```
544
+
545
+ Server action module을 client bundle에서 import하지 말고 발급한 reference를 신뢰할 수 있는 client
546
+ bootstrap data로 전달하세요. 그다음 명시적인 endpoint와 application-owned fetch implementation으로
547
+ callable을 만듭니다.
548
+
549
+ ```ts
550
+ import { createReactServerFunctionClient } from '@fluojs/react/experimental/rsc';
551
+
552
+ const updateProfile = createReactServerFunctionClient({
553
+ endpoint: '/_fluo/actions',
554
+ fetch: globalThis.fetch,
555
+ reference: bootstrap.serverFunctions.updateProfile,
556
+ });
557
+
558
+ await updateProfile('Ada');
559
+ ```
560
+
561
+ 모든 argument를 신뢰할 수 없는 값으로 취급하세요. Authorization은 각 action 내부 또는 명시적인 endpoint를
562
+ 감싸는 guard에서 수행해야 하며, 유효한 reference 자체는 authorization이 아닙니다. Action handler는 활성
563
+ `RequestContext`를 받으므로 `context.container`에서 request-scoped provider를 resolve하고 request-local
564
+ state를 격리할 수 있습니다. Server Functions는 mutation 지향 integration point이며 query loader, client
565
+ data cache, DTO validation/guard/middleware/interceptor/request scope/observability 우회 수단이 아닙니다.
566
+
567
+ Transport security contract는 의도적으로 명시적입니다.
568
+
569
+ - Action id는 `[A-Za-z0-9_-]{1,128}`과 일치합니다. Reference는 id를 노출하지만 최소 32 byte의
570
+ application-owned secret을 사용한 HMAC-SHA-256으로 무결성을 보호합니다. Secret rotation은 기존
571
+ reference를 무효화하며 action name을 암호화하지는 않습니다.
572
+ - Request는 `application/json`, `x-fluo-react-action: 1`, `allowedOrigins`에 포함된 정확한 HTTP(S)
573
+ `Origin`을 사용해야 합니다. Custom header는 browser cross-origin 호출을 non-simple request로 만들고,
574
+ origin allowlist가 CSRF policy를 제공합니다.
575
+ - `maxBodyBytes` 기본값은 64 KiB입니다. Adapter가 `rawBody`를 노출하면 정확한 byte를 사용하고, 그렇지 않으면
576
+ normalized body를 측정합니다. Network body가 JSON parsing 전에 거부되도록 platform adapter의 pre-parse
577
+ body limit을 같거나 더 작은 값으로 설정하세요.
578
+ - Argument와 result는 `null`, boolean, finite number, string, dense array, plain object만 허용합니다.
579
+ Circular value, sparse array, symbol/non-enumerable property, prototype-sensitive key, class instance,
580
+ `undefined`, `bigint`, non-finite number는 거부합니다. Nesting 기본값은 32이고 128보다 크게 설정할 수 없습니다.
581
+ - Result serialized limit 기본값은 1 MiB이며 client response도 기본 1 MiB로 제한합니다.
582
+
583
+ 예상 가능한 실패는 기존 fluo HTTP error envelope와 stable code를 사용합니다.
584
+
585
+ | Condition | Status | Code |
586
+ | --- | ---: | --- |
587
+ | malformed request shape | 400 | `REACT_SERVER_FUNCTION_INVALID_REQUEST` |
588
+ | unsafe arguments | 400 | `REACT_SERVER_FUNCTION_ARGUMENT_SERIALIZATION_FAILED` |
589
+ | missing/invalid request marker | 403 | `REACT_SERVER_FUNCTION_CSRF_REJECTED` |
590
+ | missing/unapproved origin | 403 | `REACT_SERVER_FUNCTION_ORIGIN_REJECTED` |
591
+ | invalid, tampered, retired action reference | 404 | `REACT_SERVER_FUNCTION_ACTION_NOT_FOUND` |
592
+ | oversized request | 413 | `REACT_SERVER_FUNCTION_PAYLOAD_TOO_LARGE` |
593
+ | non-JSON content type | 415 | `REACT_SERVER_FUNCTION_UNSUPPORTED_MEDIA_TYPE` |
594
+ | unsafe action result | 500 | `REACT_SERVER_FUNCTION_RESULT_SERIALIZATION_FAILED` |
595
+ | oversized action result | 500 | `REACT_SERVER_FUNCTION_RESULT_TOO_LARGE` |
596
+
597
+ 이 prototype은 `"use server"` scan, module transform, export discovery, reference 자동 생성, action id 암호화,
598
+ database mutation convention을 제공하지 않습니다. Stable root와 `@fluojs/react/client`는 모든 Server
599
+ Function code에서 계속 격리됩니다.
600
+
601
+ ## RSC Graduation Policy
602
+
603
+ `@fluojs/react/rsc`는 현재 export되지 않습니다. Canonical
604
+ [React RSC graduation policy](../../docs/contracts/react-rsc-graduation.ko.md)는 experimental API를
605
+ 안정화하기 전에 필요한 evidence를 기록합니다. 여기에는 exact React/renderer compatibility, versioned
606
+ manifest 및 Server Function transport contract, browser/server bundle separation, SSR/CSR/prerendering
607
+ behavior, hydration mismatch 및 error recovery, private/auth/cookie-bearing data의 safe transfer rule,
608
+ HTTP route ownership, #2506 navigation isolation, executable runtime/bundler coverage, bilingual docs,
609
+ Changesets release evidence가 포함됩니다.
610
+
611
+ 모든 gate가 승인되면 `@fluojs/react/rsc`가 canonical implementation이 되고
612
+ `@fluojs/react/experimental/rsc`는 문서화된 deprecation window 동안 테스트되는 re-export로 남습니다.
613
+ Stable package root는 계속 RSC-free입니다. 현재 정책은 graduation을 blocked로 표시하므로 이 문서 변경은
614
+ stable subpath를 추가하지 않고 deprecation window도 시작하지 않습니다.
615
+
616
+ ## 현재 제한 사항
617
+
618
+ 현재 이 패키지가 제공하지 않는 것은 다음입니다.
619
+
620
+ - stable RSC root 또는 `@fluojs/react/rsc` subpath. RSC는 명시적으로 불안정한
621
+ `@fluojs/react/experimental/rsc` prototype에서만 제공합니다.
622
+ - 자동 `"use server"` transform/export discovery 또는 built-in Flight renderer/build plugin
623
+ - SPA document swapping, client data/loader cache, navigation prefetching
624
+ - Next.js App Router, TanStack route tree, Angular `Routes[]`, file-route scanner, React-owned
625
+ `routes: []` table
626
+ - 자동 client bundle 생성
627
+ - filesystem scanning 또는 자동 manifest file discovery. 이미 로드한 manifest 값을 `@fluojs/react/vite`에 넘기세요.
628
+ - `bootstrapScriptContent`로 임의 data를 자동 serialize하는 기능
629
+ - `renderToPipeableStream(...)` 같은 Node 전용 `react-dom/server` pipeable stream root API
630
+
631
+ ## Public API
632
+
633
+ - `Router` — HTTP controller metadata와 React router marker metadata를 함께 기록하는 class decorator입니다.
634
+ - `Path` — HTTP `GET` route metadata와 React render metadata를 함께 기록하는 method decorator입니다.
635
+ - `getReactRouterMetadata` — router class에서 React router marker metadata를 읽습니다.
636
+ - `getReactPathMetadata` — router method에서 React render metadata를 읽습니다.
637
+ - `ReactModule` — `forRoot(...)`가 기존 fluo module/controller metadata path를 통해 React router를
638
+ 등록하는 런타임 중립 module facade입니다.
639
+ - `createReactServerEntry` — page handler가 Web Streams SSR을 위해 반환하는 runtime-neutral React server
640
+ entry를 생성합니다.
641
+ - `renderReactResponse` — lazy `react-dom/server` loading으로 React server entry 하나를 fluo HTML
642
+ response에 렌더링합니다.
643
+ - `ReactAssetMap`, `ReactBootstrapAsset`, `ReactBootstrapScriptDescriptor` — build-produced asset map 및
644
+ React DOM bootstrap script/module entry를 위한 type-only contract입니다.
645
+ - `ReactModuleOptions` — `controllers`, `imports`, `providers`, `exports`, module-level `middleware`를 포함하는
646
+ `ReactModule.forRoot(...)` option입니다.
647
+ - `ReactServerEntry`, `ReactServerEntryOptions`, `ReactServerEntryHeaders`,
648
+ `ReactRecoverableErrorHandler`, `ReactRecoverableErrorContext`, `ReactRenderContext`,
649
+ `ReactReadableStream`, `ReactReadableStreamRenderer`, `ReactReadableStreamRenderOptions`,
650
+ `RenderReactResponseOptions` — streamed React SSR 및 hydration asset을 위한 type-only contract입니다.
651
+ - `ReactScaffoldPhase` — `0.1.0` scaffold surface를 위한 type-only planning marker입니다.
652
+ - `ReactRouterMetadata`, `ReactPathMetadata`, `ReactPathOptions` — diagnostics 및 향후 rendering
653
+ integration을 위한 type-only metadata contract입니다.
654
+ - `@fluojs/react/vite` subpath — Vite manifest를 root에서 Vite를 import하지 않고 안정 hydration asset contract로
655
+ 파싱하는 `createReactViteAssetManifest(...)`, `ReactViteBuildManifest`, `ReactViteBuildManifestChunk`,
656
+ `ReactViteManifestOptions`, `ReactViteManifestDiagnostic`, `ReactViteAssetManifest`,
657
+ `ReactViteHydrationOptions`, `ReactViteJavaScriptAssets`, `ReactViteBootstrapData`,
658
+ `ReactViteResolvedEntry`를 제공합니다.
659
+ - `@fluojs/react/client` subpath — root package를 넓히거나 client route grammar를 추가하지 않고
660
+ progressive HTTP-first browser navigation을 제공하는 `Link`, `ReactClientRouterProvider`,
661
+ `createReactRouteSnapshot(...)`, `useRouter()`, `usePathname()`, `useParams()`,
662
+ `useSearchParams()`, `useNavigation()`, `useRouterState()`를 제공합니다.
663
+ - `@fluojs/react/experimental/rsc` subpath — root re-export 없이 `inspectReactRscEnvironment(...)`,
664
+ `createReactRscManifest(...)`, `createReactFlightResponse(...)`, exact-version 및 Flight content type
665
+ constant, diagnostic, client-reference/server-module mapping type, signed
666
+ `createReactServerFunctionRegistry(...)`, 명시적인 `createReactServerFunctionClient(...)`, stable Server
667
+ Function error code와 관련 transport type을 제공하며 root나 stable-client에서 re-export하지 않습니다.
668
+
669
+ ## 관련 패키지
670
+
671
+ - `@fluojs/core`: 스캐폴드가 사용하는 standard `@Module` decorator를 제공합니다.
672
+ - `@fluojs/http`: `@Router(...)`와 `@Path(...)`가 재사용하는 controller, route, DTO, guard,
673
+ interceptor, header, version metadata pipeline을 제공합니다.
674
+ - `@fluojs/runtime`: 향후 React 통합 작업은 root import boundary를 넓히지 않고 runtime bootstrap contract와 합성될 예정입니다.
675
+ - `@fluojs/vite`: Vite TC39 decorator transform boundary를 소유합니다. React hydration manifest를 파싱하지
676
+ 않으므로 React server/client asset mapping에는 `@fluojs/react/vite`를 사용하세요.
677
+ - Application-selected Flight renderer: RSC payload를 encode하고 renderer-specific build manifest를
678
+ 소비합니다. Experimental subpath는 renderer package를 import하지 않고 compatibility, manifest, HTTP
679
+ response seam만 모델링합니다.
680
+
681
+ ## 예제 소스
682
+
683
+ - `packages/react/src/index.ts`
684
+ - `packages/react/src/vite.ts`
685
+ - `packages/react/src/client.ts`
686
+ - `packages/react/src/client.test.ts`
687
+ - `packages/react/src/experimental/rsc.ts`
688
+ - `packages/react/src/experimental/rsc.test.ts`
689
+ - `packages/react/src/experimental/rsc-diagnostics.test.ts`
690
+ - `packages/react/src/experimental/rsc-flight.test.ts`
691
+ - `packages/react/src/experimental/rsc-manifest.test.ts`
692
+ - `packages/react/src/experimental/server-functions-server.ts`
693
+ - `packages/react/src/experimental/server-functions-client.ts`
694
+ - `packages/react/src/experimental/server-functions-dispatch.test.ts`
695
+ - `packages/react/src/experimental/server-functions-security.test.ts`
696
+ - `packages/react/src/experimental/server-functions-client.test.ts`
697
+ - `packages/react/src/vite/create-asset-manifest.ts`
698
+ - `packages/react/src/decorators.ts`
699
+ - `packages/react/src/server-entry.ts`
700
+ - `packages/react/src/render.ts`
701
+ - `packages/react/src/module.ts`
702
+ - `packages/react/src/render.test.ts`
703
+ - `packages/react/src/dispatcher-ssr.test.ts`
704
+ - `packages/react/src/hydration-assets.test.ts`
705
+ - `packages/react/src/vite.test.ts`
706
+ - `packages/react/src/lifecycle-pipeline.test.ts`
707
+ - `packages/react/src/module.test.ts`
708
+ - `packages/react/src/decorators.test.ts`
709
+ - `packages/react/src/index.test.ts`
710
+ - `examples/react-stable-ssr/README.ko.md`
711
+ - `examples/react-stable-ssr/src/app.test.ts`
712
+ - `examples/react-vite-ssr/README.ko.md`
713
+ - `examples/react-vite-ssr/src/app.test.ts`
714
+ - `examples/react-vite-ssr/src/hydration.test.ts`
715
+ - `examples/react-vite-ssr/tests/production-hydration.spec.ts`