@real-router/react 0.24.1 → 0.26.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.md +128 -8
- package/dist/cjs/Await-whr-pcvo.d.ts +41 -0
- package/dist/cjs/Await-whr-pcvo.d.ts.map +1 -0
- package/dist/cjs/HttpStatusProvider-Dh6cMr95.d.ts +143 -0
- package/dist/cjs/HttpStatusProvider-Dh6cMr95.d.ts.map +1 -0
- package/dist/cjs/Link-tZAtICIG.js +2 -0
- package/dist/cjs/Link-tZAtICIG.js.map +1 -0
- package/dist/cjs/RouterErrorBoundary-BlJmaoyr.d.ts +47 -0
- package/dist/cjs/RouterErrorBoundary-BlJmaoyr.d.ts.map +1 -0
- package/dist/cjs/RouterProvider-DGrUfyLj.js +2 -0
- package/dist/cjs/RouterProvider-DGrUfyLj.js.map +1 -0
- package/dist/cjs/{RouterProvider-Bj1_NbpA.d.ts → RouterProvider-DvC9mViF.d.ts} +2 -2
- package/dist/cjs/RouterProvider-DvC9mViF.d.ts.map +1 -0
- package/dist/cjs/createHttpStatusSink-D_6IiR5E.js +2 -0
- package/dist/cjs/createHttpStatusSink-D_6IiR5E.js.map +1 -0
- package/dist/cjs/index.d.ts +6 -281
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.react-server.d.ts +5 -0
- package/dist/cjs/index.react-server.js +0 -0
- package/dist/cjs/ink.d.ts +2 -1
- package/dist/cjs/ink.d.ts.map +1 -1
- package/dist/cjs/ink.js +1 -1
- package/dist/cjs/ink.js.map +1 -1
- package/dist/cjs/legacy.d.ts +3 -2
- package/dist/cjs/legacy.js +1 -1
- package/dist/cjs/legacy.ssr.d.ts +3 -0
- package/dist/cjs/legacy.ssr.js +1 -0
- package/dist/cjs/ssr.d.ts +4 -0
- package/dist/cjs/ssr.js +2 -0
- package/dist/cjs/ssr.js.map +1 -0
- package/dist/cjs/ssr.react-server.d.ts +3 -0
- package/dist/cjs/ssr.react-server.js +0 -0
- package/dist/cjs/useDeferred-DE5YUsfa.d.ts +24 -0
- package/dist/cjs/useDeferred-DE5YUsfa.d.ts.map +1 -0
- package/dist/cjs/useRoute-Dudo2K_0.js +2 -0
- package/dist/cjs/useRoute-Dudo2K_0.js.map +1 -0
- package/dist/cjs/useRouteEnter-gfy65W0D.d.ts +279 -0
- package/dist/cjs/useRouteEnter-gfy65W0D.d.ts.map +1 -0
- package/dist/cjs/useRouterTransition-BfayPs0h.d.ts +56 -0
- package/dist/cjs/useRouterTransition-BfayPs0h.d.ts.map +1 -0
- package/dist/esm/Await-qSmJ-ZuN.d.mts +41 -0
- package/dist/esm/Await-qSmJ-ZuN.d.mts.map +1 -0
- package/dist/esm/HttpStatusProvider-Cy9GSANm.d.mts +143 -0
- package/dist/esm/HttpStatusProvider-Cy9GSANm.d.mts.map +1 -0
- package/dist/esm/Link-wVA25zEV.mjs +2 -0
- package/dist/esm/Link-wVA25zEV.mjs.map +1 -0
- package/dist/esm/RouterErrorBoundary-C-MH_yrP.d.mts +47 -0
- package/dist/esm/RouterErrorBoundary-C-MH_yrP.d.mts.map +1 -0
- package/dist/esm/RouterProvider-D4B0f6wZ.mjs +2 -0
- package/dist/esm/RouterProvider-D4B0f6wZ.mjs.map +1 -0
- package/dist/esm/{RouterProvider-Dr3X_vbc.d.mts → RouterProvider-D_ZlPVAC.d.mts} +2 -2
- package/dist/esm/RouterProvider-D_ZlPVAC.d.mts.map +1 -0
- package/dist/esm/createHttpStatusSink-BXWVamHE.mjs +2 -0
- package/dist/esm/createHttpStatusSink-BXWVamHE.mjs.map +1 -0
- package/dist/esm/index.d.mts +6 -281
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.react-server.d.mts +5 -0
- package/dist/esm/index.react-server.mjs +0 -0
- package/dist/esm/ink.d.mts +2 -1
- package/dist/esm/ink.d.mts.map +1 -1
- package/dist/esm/ink.mjs +1 -1
- package/dist/esm/ink.mjs.map +1 -1
- package/dist/esm/legacy.d.mts +3 -2
- package/dist/esm/legacy.mjs +1 -1
- package/dist/esm/legacy.ssr.d.mts +3 -0
- package/dist/esm/legacy.ssr.mjs +1 -0
- package/dist/esm/ssr.d.mts +4 -0
- package/dist/esm/ssr.mjs +2 -0
- package/dist/esm/ssr.mjs.map +1 -0
- package/dist/esm/ssr.react-server.d.mts +3 -0
- package/dist/esm/ssr.react-server.mjs +0 -0
- package/dist/esm/useDeferred-B5Qy7ttQ.d.mts +24 -0
- package/dist/esm/useDeferred-B5Qy7ttQ.d.mts.map +1 -0
- package/dist/esm/useRoute-Bta1jyl2.mjs +2 -0
- package/dist/esm/useRoute-Bta1jyl2.mjs.map +1 -0
- package/dist/esm/useRouteEnter-CxjYofFa.d.mts +279 -0
- package/dist/esm/useRouteEnter-CxjYofFa.d.mts.map +1 -0
- package/dist/esm/useRouterTransition-CI1P95ZQ.d.mts +56 -0
- package/dist/esm/useRouterTransition-CI1P95ZQ.d.mts.map +1 -0
- package/package.json +46 -4
- package/src/components/Await.tsx +46 -0
- package/src/components/ClientOnly.tsx +25 -0
- package/src/components/HttpStatusCode.tsx +66 -0
- package/src/components/HttpStatusProvider.tsx +26 -0
- package/src/components/InkLink.tsx +44 -22
- package/src/components/Link.tsx +32 -36
- package/src/components/RouterErrorBoundary.tsx +21 -6
- package/src/components/ServerOnly.tsx +26 -0
- package/src/components/Streamed.tsx +30 -0
- package/src/components/modern/RouteView/RouteView.tsx +9 -3
- package/src/components/modern/RouteView/helpers.tsx +26 -16
- package/src/constants.ts +7 -8
- package/src/context.ts +4 -2
- package/src/hooks/useDeferred.tsx +34 -0
- package/src/hooks/useIsActiveRoute.tsx +17 -9
- package/src/hooks/useNavigator.tsx +0 -2
- package/src/hooks/useRoute.tsx +10 -7
- package/src/hooks/useRouter.tsx +0 -2
- package/src/index.react-server.ts +33 -0
- package/src/index.ts +5 -0
- package/src/legacy.ssr.ts +35 -0
- package/src/legacy.ts +4 -0
- package/src/ssr.react-server.ts +21 -0
- package/src/ssr.ts +43 -0
- package/src/utils/createHttpStatusSink.ts +27 -0
- package/dist/cjs/Link-DYvAnJlk.js +0 -2
- package/dist/cjs/Link-DYvAnJlk.js.map +0 -1
- package/dist/cjs/RouterProvider-Bj1_NbpA.d.ts.map +0 -1
- package/dist/cjs/RouterProvider-CfBNR-k7.js +0 -2
- package/dist/cjs/RouterProvider-CfBNR-k7.js.map +0 -1
- package/dist/cjs/index.d.ts.map +0 -1
- package/dist/cjs/useRouterTransition-BYit_9Mt.d.ts +0 -92
- package/dist/cjs/useRouterTransition-BYit_9Mt.d.ts.map +0 -1
- package/dist/esm/Link-C7upxMc8.mjs +0 -2
- package/dist/esm/Link-C7upxMc8.mjs.map +0 -1
- package/dist/esm/RouterProvider-CS2ZoONm.mjs +0 -2
- package/dist/esm/RouterProvider-CS2ZoONm.mjs.map +0 -1
- package/dist/esm/RouterProvider-Dr3X_vbc.d.mts.map +0 -1
- package/dist/esm/index.d.mts.map +0 -1
- package/dist/esm/useRouterTransition-B65Wvngj.d.mts +0 -92
- package/dist/esm/useRouterTransition-B65Wvngj.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -17,13 +17,18 @@ npm install @real-router/react @real-router/core @real-router/browser-plugin
|
|
|
17
17
|
|
|
18
18
|
## Entry Points
|
|
19
19
|
|
|
20
|
-
| Import Path
|
|
21
|
-
|
|
|
22
|
-
| `@real-router/react`
|
|
23
|
-
| `@real-router/react/
|
|
24
|
-
| `@real-router/react/
|
|
20
|
+
| Import Path | React Version | Runtime | Includes |
|
|
21
|
+
| ---------------------------------- | ------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
22
|
+
| `@real-router/react` | 19.2+ | DOM | Full client API (hooks, `Link`, `RouteView` with `keepAlive`, `RouterErrorBoundary`). **No SSR-feature components** — those live at `/ssr`. |
|
|
23
|
+
| `@real-router/react/ssr` | 19.2+ | DOM (SSR-aware) | `<ClientOnly>`, `<ServerOnly>`, `<Await>`, `<Streamed>`, `useDeferred`, `<HttpStatusCode>`, `<HttpStatusProvider>`, `createHttpStatusSink` |
|
|
24
|
+
| `@real-router/react/legacy` | 18+ | DOM | Client API for React 18 (no `RouteView`, no SSR helpers) |
|
|
25
|
+
| `@real-router/react/legacy/ssr` | 18+ | DOM (SSR-aware) | SSR-feature subset for React 18 — same as `/ssr` minus `<Await>` (which depends on React 19's `use(promise)`) |
|
|
26
|
+
| `@real-router/react/ink` | 19.2+ | Terminal (Ink 7+) | Hooks, `InkRouterProvider`, `InkLink` — no `Link`, no `RouteView`, no `announceNavigation` |
|
|
27
|
+
| `@real-router/react` (`react-server` condition) | 19+ | RSC bundler | **Type-only** re-exports for Server Components — no client runtime. Same condition applies to `/ssr` for prop types. |
|
|
25
28
|
|
|
26
|
-
All entries share the same underlying hook code. `/legacy` excludes React 19.2 `<Activity>`; `/ink` excludes DOM-bound primitives (`<a>`-based `Link`, `announceNavigation`) and replaces them with keyboard-driven terminal equivalents.
|
|
29
|
+
All client entries share the same underlying hook code. `/legacy` excludes React 19.2 `<Activity>`; `/ink` excludes DOM-bound primitives (`<a>`-based `Link`, `announceNavigation`) and replaces them with keyboard-driven terminal equivalents. The `/ssr` split keeps server-only prop types out of the client TypeScript context for apps that don't touch SSR (bundle cost is ≈ 0 thanks to `"sideEffects": false`).
|
|
30
|
+
|
|
31
|
+
The root export resolves to a **type-only entry** when bundlers apply the `react-server` condition (Vite RSC, Webpack RSC, Turbopack, Parcel) — Server Components can import public API types without pulling client-only code into the server bundle. Per-request data fetching is handled by [@real-router/rsc-server-plugin](https://www.npmjs.com/package/@real-router/rsc-server-plugin), not this entry. See [RSC Integration wiki guide](https://github.com/greydragon888/real-router/wiki/RSC-Integration).
|
|
27
32
|
|
|
28
33
|
## Quick Start
|
|
29
34
|
|
|
@@ -202,6 +207,31 @@ const LazyDashboard = lazy(() => import("./Dashboard"));
|
|
|
202
207
|
|
|
203
208
|
`fallback` and `keepAlive` work together — `<Activity>` wraps the whole match including the `<Suspense>` boundary.
|
|
204
209
|
|
|
210
|
+
#### `RouteView.Self`
|
|
211
|
+
|
|
212
|
+
Renders when the active route name **exactly equals** the parent `<RouteView>`'s `nodeName`. Use it for leaf views where the parent route itself is the destination — e.g. `/users` rendering a directory page while `/users/:id` renders inside a nested `<RouteView nodeName="users">`.
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
<RouteView nodeName="users">
|
|
216
|
+
<RouteView.Self>
|
|
217
|
+
<UsersIndex /> {/* rendered for route name === "users" */}
|
|
218
|
+
</RouteView.Self>
|
|
219
|
+
<RouteView.Match segment="profile">
|
|
220
|
+
<UserProfile /> {/* rendered for "users.profile" and descendants */}
|
|
221
|
+
</RouteView.Match>
|
|
222
|
+
<RouteView.NotFound>
|
|
223
|
+
<NotFoundPage />
|
|
224
|
+
</RouteView.NotFound>
|
|
225
|
+
</RouteView>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
| Prop | Type | Description |
|
|
229
|
+
| ---------- | ----------- | -------------------------------------------------------------------------------------------- |
|
|
230
|
+
| `fallback` | `ReactNode` | Symmetric with `RouteView.Match` — wraps `children` in `<Suspense>` when defined. |
|
|
231
|
+
| `children` | `ReactNode` | Content to render when the active route name equals the parent `<RouteView>`'s `nodeName`. |
|
|
232
|
+
|
|
233
|
+
First-wins: if multiple `<RouteView.Self>` elements appear, only the first contributes to the rendered output (same precedence semantics as `<RouteView.NotFound>`). An activating `<RouteView.Match>` suppresses both `Self` and `NotFound`.
|
|
234
|
+
|
|
205
235
|
### `<RouterErrorBoundary>`
|
|
206
236
|
|
|
207
237
|
Declarative error handling for navigation errors. Shows a fallback **alongside** children (not instead of) when a guard rejects or a route is not found.
|
|
@@ -223,6 +253,94 @@ Auto-resets on next successful navigation. Works with both `<Link>` and imperati
|
|
|
223
253
|
|
|
224
254
|
Available from both `@real-router/react` and `@real-router/react/legacy`.
|
|
225
255
|
|
|
256
|
+
### `<ClientOnly>` / `<ServerOnly>`
|
|
257
|
+
|
|
258
|
+
Paired SSR-aware boundaries. `<ClientOnly>` renders `fallback` on the server (and on the client first paint, to match SSR HTML), then swaps in `children` after mount. `<ServerOnly>` is the symmetric inverse.
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
import { ClientOnly, ServerOnly } from "@real-router/react/ssr";
|
|
262
|
+
|
|
263
|
+
<ClientOnly fallback={<Skeleton />}>
|
|
264
|
+
<BrowserApiWidget />
|
|
265
|
+
</ClientOnly>
|
|
266
|
+
|
|
267
|
+
<ServerOnly>
|
|
268
|
+
<SeoMetaStrip />
|
|
269
|
+
</ServerOnly>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Implementation: `useState(false)` + `useEffect(() => setMounted(true), [])`. Server emits the SSR-side branch, client first paint matches it (no hydration mismatch), the post-mount effect triggers a single re-render that swaps the rendered branch.
|
|
273
|
+
|
|
274
|
+
Available from `@real-router/react/ssr` and `@real-router/react/legacy/ssr`. End-to-end dogfooding lives in [`examples/web/react/ssr-examples/ssr/`](../../examples/web/react/ssr-examples/ssr/) (see `e2e/ssr-boundaries.spec.ts`).
|
|
275
|
+
|
|
276
|
+
### `<Streamed>` / `<Await>` / `useDeferred`
|
|
277
|
+
|
|
278
|
+
Three pieces of the deferred-data pipeline (paired with [`@real-router/ssr-data-plugin`](https://www.npmjs.com/package/@real-router/ssr-data-plugin)'s `defer()` API). `<Streamed>` is a cross-adapter alias for `<Suspense>` so route bundles can use the same boundary name across Solid/Vue/Svelte/React. `<Await<T> name="key">` reads the deferred promise the loader published under that key and hands the resolved value to a render-prop. `useDeferred<T>(key)` returns the same promise for callers that want to compose with `use()` or a third-party suspense library.
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
import { Streamed, Await, useDeferred } from "@real-router/react/ssr";
|
|
282
|
+
|
|
283
|
+
// Render-prop form — works in React 19.2+ via internal `use(promise)`.
|
|
284
|
+
<Streamed fallback={<Spinner />}>
|
|
285
|
+
<Await<Review[]> name="reviews">
|
|
286
|
+
{(reviews) => <ReviewList items={reviews} />}
|
|
287
|
+
</Await>
|
|
288
|
+
</Streamed>;
|
|
289
|
+
|
|
290
|
+
// Manual form — works on React 18+ (`/legacy/ssr` entry).
|
|
291
|
+
function Reviews() {
|
|
292
|
+
const reviews = use(useDeferred<Review[]>("reviews"));
|
|
293
|
+
return <ReviewList items={reviews} />;
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`<Await>` is React 19.2+ only (depends on `use(promise)`); `<Streamed>` and `useDeferred` ship in both `/ssr` and `/legacy/ssr`. End-to-end example: [`examples/web/react/ssr-examples/ssr-streaming/`](../../examples/web/react/ssr-examples/ssr-streaming/).
|
|
298
|
+
|
|
299
|
+
### `<HttpStatusCode>` / `<HttpStatusProvider>` / `createHttpStatusSink`
|
|
300
|
+
|
|
301
|
+
Render-time HTTP status declaration for SSR responses. Mount `<HttpStatusCode code={N} />` inside a route component (typical use: a `<RouteView.NotFound>` glob page) — it writes `N` to the nearest `<HttpStatusProvider>`'s sink during render and returns `null`. After `renderToString` / `renderToReadableStream`, read `sink.code` and pass it to your response.
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
// app.tsx
|
|
305
|
+
import { HttpStatusCode } from "@real-router/react/ssr";
|
|
306
|
+
|
|
307
|
+
function NotFound() {
|
|
308
|
+
return (
|
|
309
|
+
<>
|
|
310
|
+
<HttpStatusCode code={404} />
|
|
311
|
+
<h1>Page not found</h1>
|
|
312
|
+
</>
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// entry-server.tsx
|
|
317
|
+
import { renderToString } from "react-dom/server";
|
|
318
|
+
import {
|
|
319
|
+
HttpStatusProvider,
|
|
320
|
+
createHttpStatusSink,
|
|
321
|
+
} from "@real-router/react/ssr";
|
|
322
|
+
|
|
323
|
+
const sink = createHttpStatusSink();
|
|
324
|
+
const html = renderToString(
|
|
325
|
+
<HttpStatusProvider sink={sink}>
|
|
326
|
+
<RouterProvider router={router}>
|
|
327
|
+
<App />
|
|
328
|
+
</RouterProvider>
|
|
329
|
+
</HttpStatusProvider>,
|
|
330
|
+
);
|
|
331
|
+
response.status(sink.code ?? 200).send(html);
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
| Export | Kind | Purpose |
|
|
335
|
+
| -------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
336
|
+
| `<HttpStatusCode code={N}/>` | component | Writes `code` to the optional context sink during render. Last write wins across multiple instances. No-op without a provider. |
|
|
337
|
+
| `<HttpStatusProvider sink={…}>` | component | Supplies an `HttpStatusSink` to descendant `<HttpStatusCode />` via React context. |
|
|
338
|
+
| `createHttpStatusSink()` | utility | Returns a fresh `{ code: number \| undefined }` sink — construct one per request on the server, read `sink.code` after rendering. |
|
|
339
|
+
|
|
340
|
+
Loader-driven errors (`LoaderNotFound` → 404, `LoaderRedirect` → 30x) keep working as before; this component covers render-time decisions only. **Streaming SSR caveat**: mount `<HttpStatusCode>` in the shell (above every `<Suspense>` that could delay it), or `await stream.allReady` before reading `sink.code` — once the response status flushes, later writes are lost.
|
|
341
|
+
|
|
342
|
+
Available from `@real-router/react/ssr` and `@real-router/react/legacy/ssr`.
|
|
343
|
+
|
|
226
344
|
## React 18 Migration
|
|
227
345
|
|
|
228
346
|
One import path change — all hooks and `Link` work identically:
|
|
@@ -234,6 +352,8 @@ One import path change — all hooks and `Link` work identically:
|
|
|
234
352
|
|
|
235
353
|
`RouteView` is not available from `/legacy`. Use `useRouteNode` with a switch/case pattern instead.
|
|
236
354
|
|
|
355
|
+
`useRouteExit` and `useRouteEnter` are also not available from `/legacy` — they depend on React 19's concurrent-mode scheduling guarantees. Use `router.subscribeLeave()` directly for exit guards, and `useEffect` for mount-time analytics on React 18.
|
|
356
|
+
|
|
237
357
|
## Ink (Terminal UI)
|
|
238
358
|
|
|
239
359
|
`@real-router/react/ink` lets you build terminal apps with the same hooks you use in the browser.
|
|
@@ -285,7 +405,7 @@ render(
|
|
|
285
405
|
);
|
|
286
406
|
```
|
|
287
407
|
|
|
288
|
-
**Navigation contract:** Tab moves focus between `InkLink`s, Enter calls `router.navigate(...)`. `RouteView` and the DOM `Link` are intentionally absent from this entry — compose routes with `useRouteNode("")` and a switch.
|
|
408
|
+
**Navigation contract:** Tab moves focus between `InkLink`s, Enter calls `router.navigate(...)`. `RouteView` and the DOM `Link` are intentionally absent from this entry — compose routes with `useRouteNode("")` and a switch. `RouterErrorBoundary` is available for terminal error handling.
|
|
289
409
|
|
|
290
410
|
**Install:**
|
|
291
411
|
|
|
@@ -329,7 +449,7 @@ Opt-in preservation of scroll position across navigations:
|
|
|
329
449
|
</RouterProvider>
|
|
330
450
|
```
|
|
331
451
|
|
|
332
|
-
Restores scroll on back/forward, scrolls to top (or `#hash`) on push. Three modes: `"restore"` (default), `"top"`, `"native"`. Custom containers via `scrollContainer: () => HTMLElement | null`. Lifecycle tied to the provider — created on mount, destroyed on unmount. See [Scroll Restoration guide](https://github.com/greydragon888/real-router/wiki/Scroll-Restoration) for
|
|
452
|
+
Restores scroll on back/forward, scrolls to top (or `#hash`) on push. Three modes: `"restore"` (default), `"top"`, `"native"`. Custom containers via `scrollContainer: () => HTMLElement | null`. Lifecycle tied to the provider — created on mount, destroyed on unmount. Under `@real-router/browser-plugin`, replace transitions now preserve scroll position and programmatic reloads restore from `sessionStorage` (portable via `state.transition.replace` / `state.transition.reload`). See [Scroll Restoration guide](https://github.com/greydragon888/real-router/wiki/Scroll-Restoration) for the full behaviour matrix.
|
|
333
453
|
|
|
334
454
|
## View Transitions
|
|
335
455
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/Await.d.ts
|
|
4
|
+
interface AwaitProps<T> {
|
|
5
|
+
/** Deferred key declared in the loader's `defer({ deferred: { <name>: ... } })`. */
|
|
6
|
+
readonly name: string;
|
|
7
|
+
/** Render the resolved value. Suspends while pending; throws inside the
|
|
8
|
+
* nearest Error Boundary on rejection. */
|
|
9
|
+
readonly children: (value: T) => ReactNode;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Ergonomic wrapper around `useDeferred(name)` + React 19's `use(promise)`.
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <Suspense fallback={<Spinner />}>
|
|
16
|
+
* <Await<Review[]> name="reviews">
|
|
17
|
+
* {(reviews) => <ReviewList items={reviews} />}
|
|
18
|
+
* </Await>
|
|
19
|
+
* </Suspense>
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Equivalent to:
|
|
23
|
+
*
|
|
24
|
+
* ```tsx
|
|
25
|
+
* function Inner() {
|
|
26
|
+
* const reviews = use(useDeferred<Review[]>("reviews"));
|
|
27
|
+
* return <ReviewList items={reviews} />;
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Pick `<Await>` for cross-adapter consistency with the SvelteKit
|
|
32
|
+
* `{#await}` / Solid `<Await/>` shape; pick the inline `use(useDeferred(...))`
|
|
33
|
+
* form if you prefer one fewer abstraction.
|
|
34
|
+
*/
|
|
35
|
+
declare function Await<T = unknown>({
|
|
36
|
+
name,
|
|
37
|
+
children
|
|
38
|
+
}: AwaitProps<T>): ReactNode;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { AwaitProps as n, Await as t };
|
|
41
|
+
//# sourceMappingURL=Await-whr-pcvo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Await-whr-pcvo.d.ts","names":[],"sources":["../../src/components/Await.tsx"],"mappings":";;;UAMiB,UAAA;;WAEN,IAAA;EAFgB;;EAAA,SAKhB,QAAA,GAAW,KAAA,EAAO,CAAA,KAAM,SAAS;AAAA;;;;;;;AAAA;AA2B5C;;;;;;;;;;;;;;;;;iBAAgB,KAAA,aAAA,CAAA;EACd,IAAA;EACA;AAAA,GACC,UAAA,CAAW,CAAA,IAAK,SAAA"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
//#region src/components/ClientOnly.d.ts
|
|
4
|
+
interface ClientOnlyProps {
|
|
5
|
+
readonly children: ReactNode;
|
|
6
|
+
readonly fallback?: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
declare function ClientOnly({
|
|
9
|
+
children,
|
|
10
|
+
fallback
|
|
11
|
+
}: ClientOnlyProps): ReactNode;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/components/ServerOnly.d.ts
|
|
14
|
+
interface ServerOnlyProps {
|
|
15
|
+
readonly children: ReactNode;
|
|
16
|
+
readonly fallback?: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
declare function ServerOnly({
|
|
19
|
+
children,
|
|
20
|
+
fallback
|
|
21
|
+
}: ServerOnlyProps): ReactNode;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/components/Streamed.d.ts
|
|
24
|
+
interface StreamedProps {
|
|
25
|
+
/** Shown while any descendant `use(promise)` / `<Await>` is pending. */
|
|
26
|
+
readonly fallback: ReactNode;
|
|
27
|
+
readonly children: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Cross-adapter alias for `<Suspense fallback={…}>`. Pairs with `<Await>`
|
|
31
|
+
* for symmetry with `<Streamed>` boundaries in the SvelteKit / Solid
|
|
32
|
+
* deferred-data conventions.
|
|
33
|
+
*
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <Streamed fallback={<Spinner />}>
|
|
36
|
+
* <Await<Review[]> name="reviews">
|
|
37
|
+
* {(reviews) => <ReviewList items={reviews} />}
|
|
38
|
+
* </Await>
|
|
39
|
+
* </Streamed>
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* The component is a thin wrapper around React's native `<Suspense>` — no
|
|
43
|
+
* additional behaviour. Use plain `<Suspense>` directly if you don't need
|
|
44
|
+
* the cross-framework naming alignment.
|
|
45
|
+
*/
|
|
46
|
+
declare function Streamed({
|
|
47
|
+
fallback,
|
|
48
|
+
children
|
|
49
|
+
}: StreamedProps): ReactNode;
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/components/HttpStatusCode.d.ts
|
|
52
|
+
interface HttpStatusCodeProps {
|
|
53
|
+
/** HTTP status to apply to the response. Common values: 404, 410, 451, 503. */
|
|
54
|
+
readonly code: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Render-time HTTP status declaration. Mount inside a route component (typical
|
|
58
|
+
* use case: a glob `*` route's NotFound page) when the status is decided by
|
|
59
|
+
* the rendered tree rather than a loader.
|
|
60
|
+
*
|
|
61
|
+
* Writes `code` to the nearest `<HttpStatusProvider>`'s sink during render and
|
|
62
|
+
* returns `null`. With no provider mounted (the standard client-side case)
|
|
63
|
+
* the component is a silent no-op — same component tree hydrates without
|
|
64
|
+
* touching the DOM or warning about mismatches.
|
|
65
|
+
*
|
|
66
|
+
* Loader-driven errors (`LoaderNotFound` → 404, `LoaderRedirect` → 30x) keep
|
|
67
|
+
* working as before; this component covers render-time decisions only.
|
|
68
|
+
*
|
|
69
|
+
* Last write wins when several `<HttpStatusCode />` instances mount in the
|
|
70
|
+
* same render pass — sink reflects the last component that ran.
|
|
71
|
+
*
|
|
72
|
+
* ```tsx
|
|
73
|
+
* // entry-server.tsx
|
|
74
|
+
* import { renderToString } from "react-dom/server";
|
|
75
|
+
* import { createHttpStatusSink, HttpStatusProvider } from "@real-router/react/ssr";
|
|
76
|
+
*
|
|
77
|
+
* const sink = createHttpStatusSink();
|
|
78
|
+
* const html = renderToString(
|
|
79
|
+
* <HttpStatusProvider sink={sink}>
|
|
80
|
+
* <RouterProvider router={router}>
|
|
81
|
+
* <App />
|
|
82
|
+
* </RouterProvider>
|
|
83
|
+
* </HttpStatusProvider>,
|
|
84
|
+
* );
|
|
85
|
+
* response.status(sink.code ?? 200).send(html);
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* **Streaming SSR (`renderToReadableStream`):** the response status MUST be
|
|
89
|
+
* sent before the first body byte flushes. If `<HttpStatusCode />` is mounted
|
|
90
|
+
* inside a late-resolving `<Suspense>` boundary, the sink write may happen
|
|
91
|
+
* AFTER the headers are already on the wire — the override is then lost.
|
|
92
|
+
* Either mount the component in the shell (above every `<Suspense>` that
|
|
93
|
+
* could delay it) or `await stream.allReady` before reading `sink.code`
|
|
94
|
+
* (which forfeits streaming benefits). For non-streaming SSR
|
|
95
|
+
* (`renderToString`) there is no such ordering concern.
|
|
96
|
+
*
|
|
97
|
+
* **Valid `code` range:** Node's `res.end()` throws `Invalid status code` on
|
|
98
|
+
* `NaN`, `0`, negative values, or values `> 999` — this surfaces as a 5xx /
|
|
99
|
+
* dropped connection, not silent corruption. Pass a real HTTP status integer
|
|
100
|
+
* (commonly 4xx/5xx; 100-999 is what Node accepts).
|
|
101
|
+
*/
|
|
102
|
+
declare function HttpStatusCode({
|
|
103
|
+
code
|
|
104
|
+
}: HttpStatusCodeProps): ReactNode;
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/utils/createHttpStatusSink.d.ts
|
|
107
|
+
/**
|
|
108
|
+
* Render-scoped HTTP status sink. Created per request on the server, passed to
|
|
109
|
+
* `<HttpStatusProvider sink={...}>`, and read after `renderToString` /
|
|
110
|
+
* `renderToReadableStream` to apply the value to the HTTP response.
|
|
111
|
+
*
|
|
112
|
+
* Last write wins: if the rendered tree mounts more than one
|
|
113
|
+
* `<HttpStatusCode />`, the value reflects the last component that ran during
|
|
114
|
+
* the render pass.
|
|
115
|
+
*
|
|
116
|
+
* No-op on the client — `<HttpStatusCode />` reads the optional context and
|
|
117
|
+
* skips the write when no provider is mounted, so the same component tree can
|
|
118
|
+
* be hydrated without changing behaviour.
|
|
119
|
+
*
|
|
120
|
+
* Constraints:
|
|
121
|
+
* - **Per-request only.** Don't share a sink across requests; the rendered
|
|
122
|
+
* tree mutates `code` in place. Module-level singletons leak status
|
|
123
|
+
* between concurrent requests.
|
|
124
|
+
* - **Don't `Object.freeze` the sink.** The component writes to `.code`;
|
|
125
|
+
* freezing makes the assignment throw under ESM strict mode.
|
|
126
|
+
*/
|
|
127
|
+
interface HttpStatusSink {
|
|
128
|
+
code: number | undefined;
|
|
129
|
+
}
|
|
130
|
+
declare function createHttpStatusSink(): HttpStatusSink;
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/components/HttpStatusProvider.d.ts
|
|
133
|
+
interface HttpStatusProviderProps {
|
|
134
|
+
readonly sink: HttpStatusSink;
|
|
135
|
+
readonly children: ReactNode;
|
|
136
|
+
}
|
|
137
|
+
declare function HttpStatusProvider({
|
|
138
|
+
sink,
|
|
139
|
+
children
|
|
140
|
+
}: HttpStatusProviderProps): ReactNode;
|
|
141
|
+
//#endregion
|
|
142
|
+
export { HttpStatusCode as a, StreamedProps as c, ClientOnly as d, ClientOnlyProps as f, createHttpStatusSink as i, ServerOnly as l, HttpStatusProviderProps as n, HttpStatusCodeProps as o, HttpStatusSink as r, Streamed as s, HttpStatusProvider as t, ServerOnlyProps as u };
|
|
143
|
+
//# sourceMappingURL=HttpStatusProvider-Dh6cMr95.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HttpStatusProvider-Dh6cMr95.d.ts","names":[],"sources":["../../src/components/ClientOnly.tsx","../../src/components/ServerOnly.tsx","../../src/components/Streamed.tsx","../../src/components/HttpStatusCode.tsx","../../src/utils/createHttpStatusSink.ts","../../src/components/HttpStatusProvider.tsx"],"mappings":";;;UAIiB,eAAA;EAAA,SACN,QAAA,EAAU,SAAA;EAAA,SACV,QAAA,GAAW,SAAS;AAAA;AAAA,iBAGf,UAAA,CAAA;EACd,QAAA;EACA;AAAA,GACC,eAAA,GAAkB,SAAA;;;UCRJ,eAAA;EAAA,SACN,QAAA,EAAU,SAAA;EAAA,SACV,QAAA,GAAW,SAAS;AAAA;AAAA,iBAGf,UAAA,CAAA;EACd,QAAA;EACA;AAAA,GACC,eAAA,GAAkB,SAAA;;;UCRJ,aAAA;;WAEN,QAAA,EAAU,SAAA;EAAA,SACV,QAAA,EAAU,SAAS;AAAA;;;;;;;AFDC;AAG/B;;;;;;;;;;iBEkBgB,QAAA,CAAA;EAAW,QAAA;EAAU;AAAA,GAAY,aAAA,GAAgB,SAAA;;;UCrBhD,mBAAA;;WAEN,IAAI;AAAA;;;;;;;;AHFgB;AAG/B;;;;;;;;;;;;;;;;AAG8B;;;;ACR9B;;;;;;;;;AAE+B;AAG/B;;;;;;;;iBEgDgB,cAAA,CAAA;EAAiB;AAAA,GAAQ,mBAAA,GAAsB,SAAA;;;;;;AHrD/D;;;;;;;;;AAE+B;AAG/B;;;;;;;UIWiB,cAAA;EACf,IAAI;AAAA;AAAA,iBAGU,oBAAA,CAAA,GAAwB,cAAc;;;UCjBrC,uBAAA;EAAA,SACN,IAAA,EAAM,cAAA;EAAA,SACN,QAAA,EAAU,SAAS;AAAA;AAAA,iBAGd,kBAAA,CAAA;EACd,IAAA;EACA;AAAA,GACC,uBAAA,GAA0B,SAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`./useRoute-Dudo2K_0.js`),t=require(`./RouterProvider-DGrUfyLj.js`);let n=require(`react`),r=require(`react/jsx-runtime`);function i(e,n){return e.routeName===n.routeName&&e.className===n.className&&e.activeClassName===n.activeClassName&&e.activeStrict===n.activeStrict&&e.ignoreQueryParams===n.ignoreQueryParams&&e.onClick===n.onClick&&e.target===n.target&&e.style===n.style&&e.children===n.children&&e.hash===n.hash&&t.u(e.routeParams,n.routeParams)&&t.u(e.routeOptions,n.routeOptions)}const a=(0,n.memo)(({routeName:i,routeParams:a=e.r,routeOptions:o=e.n,className:s,activeClassName:c=`active`,activeStrict:l=!1,ignoreQueryParams:u=!0,hash:d,onClick:f,target:p,children:m,...h})=>{let g=t.p(),_=t.o(i,a,l,u,d),v=t.c(g,i,a,d===void 0?void 0:{hash:d}),y=e=>{f&&(f(e),e.defaultPrevented)||!t.d(e.nativeEvent)||p===`_blank`||(e.preventDefault(),t.l(g,i,a,d,o).catch(()=>{}))},b=(0,n.useMemo)(()=>t.s(_,c,s),[_,c,s]);return(0,r.jsx)(`a`,{...h,href:v,className:b,onClick:y,children:m})},i);a.displayName=`Link`,Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return a}});
|
|
2
|
+
//# sourceMappingURL=Link-tZAtICIG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Link-tZAtICIG.js","names":["shallowEqual","EMPTY_PARAMS","EMPTY_OPTIONS","useRouter","useIsActiveRoute","buildHref","shouldNavigate","buildActiveClassName"],"sources":["../../src/components/Link.tsx"],"sourcesContent":["import { memo, useMemo } from \"react\";\n\nimport { EMPTY_PARAMS, EMPTY_OPTIONS } from \"../constants\";\nimport {\n shouldNavigate,\n buildHref,\n buildActiveClassName,\n navigateWithHash,\n shallowEqual,\n} from \"../dom-utils\";\nimport { useIsActiveRoute } from \"../hooks/useIsActiveRoute\";\nimport { useRouter } from \"../hooks/useRouter\";\n\nimport type { LinkProps } from \"../types\";\nimport type { FC, MouseEvent } from \"react\";\n\nfunction areLinkPropsEqual(\n prev: Readonly<LinkProps>,\n next: Readonly<LinkProps>,\n): boolean {\n return (\n prev.routeName === next.routeName &&\n prev.className === next.className &&\n prev.activeClassName === next.activeClassName &&\n prev.activeStrict === next.activeStrict &&\n prev.ignoreQueryParams === next.ignoreQueryParams &&\n prev.onClick === next.onClick &&\n prev.target === next.target &&\n prev.style === next.style &&\n prev.children === next.children &&\n prev.hash === next.hash &&\n shallowEqual(prev.routeParams, next.routeParams) &&\n shallowEqual(prev.routeOptions, next.routeOptions)\n );\n}\n\nconst LinkImpl: FC<LinkProps> = ({\n routeName,\n routeParams = EMPTY_PARAMS,\n routeOptions = EMPTY_OPTIONS,\n className,\n activeClassName = \"active\",\n activeStrict = false,\n ignoreQueryParams = true,\n hash,\n onClick,\n target,\n children,\n ...props\n}) => {\n // memo + areLinkPropsEqual guarantees that on bail-out the component does\n // not render; on render, routeParams/routeOptions either changed reference\n // (true change) or comparator failed (e.g., BigInt fallback to identity),\n // so they're safe to use directly in hook deps.\n\n const router = useRouter();\n\n // When `hash` prop is set, active state requires both route AND hash to\n // match (#532). Without this, three tab links sharing routeName=\"settings\"\n // would all be marked active by route-name alone, defeating tab semantics.\n const isActive = useIsActiveRoute(\n routeName,\n routeParams,\n activeStrict,\n ignoreQueryParams,\n hash,\n );\n\n // No useMemo: outer memo()+shallowEqual prevents Link re-render unless a\n // prop actually changed. When this body runs, either `hash` differs from\n // last render or another prop changed — in both cases the `{ hash }` alloc\n // is unavoidable. The useMemo wrapper added one closure + deps slot per\n // render without saving an allocation.\n const hashOption = hash === undefined ? undefined : { hash };\n const href = buildHref(router, routeName, routeParams, hashOption);\n\n // useCallback was wasteful: 7 deps recreated the closure on every meaningful\n // render anyway, and `<a onClick>` does not benefit from a stable function\n // identity (no child-memo-bail-out chain past it). Inline arrow function is\n // what React Compiler emits automatically for this shape.\n const handleClick = (evt: MouseEvent<HTMLAnchorElement>) => {\n if (onClick) {\n onClick(evt);\n\n if (evt.defaultPrevented) {\n return;\n }\n }\n\n if (!shouldNavigate(evt.nativeEvent) || target === \"_blank\") {\n return;\n }\n\n evt.preventDefault();\n navigateWithHash(router, routeName, routeParams, hash, routeOptions).catch(\n () => {},\n );\n };\n\n // Memoize the joined class string. parseTokens + Set + join on every render\n // adds up on pages with N Links navigating frequently; deps cover every\n // input the function reads so cache invalidation is exact.\n const finalClassName = useMemo(\n () => buildActiveClassName(isActive, activeClassName, className),\n [isActive, activeClassName, className],\n );\n\n return (\n <a {...props} href={href} className={finalClassName} onClick={handleClick}>\n {children}\n </a>\n );\n};\n\nexport const Link: FC<LinkProps> = memo(LinkImpl, areLinkPropsEqual);\n\nLink.displayName = \"Link\";\n"],"mappings":"0IAgBA,SAAS,EACP,EACA,EACS,CACT,OACE,EAAK,YAAc,EAAK,WACxB,EAAK,YAAc,EAAK,WACxB,EAAK,kBAAoB,EAAK,iBAC9B,EAAK,eAAiB,EAAK,cAC3B,EAAK,oBAAsB,EAAK,mBAChC,EAAK,UAAY,EAAK,SACtB,EAAK,SAAW,EAAK,QACrB,EAAK,QAAU,EAAK,OACpB,EAAK,WAAa,EAAK,UACvB,EAAK,OAAS,EAAK,MACnBA,EAAAA,EAAa,EAAK,YAAa,EAAK,WAAW,GAC/CA,EAAAA,EAAa,EAAK,aAAc,EAAK,YAAY,CAErD,CAgFA,MAAa,GAAA,EAAA,EAAA,OA9EoB,CAC/B,YACA,cAAcC,EAAAA,EACd,eAAeC,EAAAA,EACf,YACA,kBAAkB,SAClB,eAAe,GACf,oBAAoB,GACpB,OACA,UACA,SACA,WACA,GAAG,KACC,CAMJ,IAAM,EAASC,EAAAA,EAAU,EAKnB,EAAWC,EAAAA,EACf,EACA,EACA,EACA,EACA,CACF,EAQM,EAAOC,EAAAA,EAAU,EAAQ,EAAW,EADvB,IAAS,IAAA,GAAY,IAAA,GAAY,CAAE,MAAK,CACM,EAM3D,EAAe,GAAuC,CACtD,IACF,EAAQ,CAAG,EAEP,EAAI,mBAKN,CAACC,EAAAA,EAAe,EAAI,WAAW,GAAK,IAAW,WAInD,EAAI,eAAe,EACnB,EAAA,EAAiB,EAAQ,EAAW,EAAa,EAAM,CAAY,EAAE,UAC7D,CAAC,CACT,EACF,EAKM,GAAA,EAAA,EAAA,aACEC,EAAAA,EAAqB,EAAU,EAAiB,CAAS,EAC/D,CAAC,EAAU,EAAiB,CAAS,CACvC,EAEA,OACE,EAAA,EAAA,KAAC,IAAD,CAAG,GAAI,EAAa,OAAM,UAAW,EAAgB,QAAS,EAC3D,UACA,CAAA,CAEP,EAEkD,CAAiB,EAEnE,EAAK,YAAc"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { HTMLAttributes, MouseEventHandler, ReactElement, ReactNode } from "react";
|
|
2
|
+
import { NavigationOptions, Navigator, Params, RouterError, State } from "@real-router/core";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
interface RouteState<P extends Params = Params> {
|
|
6
|
+
route: State<P> | undefined;
|
|
7
|
+
previousRoute?: State | undefined;
|
|
8
|
+
}
|
|
9
|
+
type RouteContext<P extends Params = Params> = {
|
|
10
|
+
navigator: Navigator;
|
|
11
|
+
} & RouteState<P>;
|
|
12
|
+
interface LinkProps<P extends Params = Params> extends HTMLAttributes<HTMLAnchorElement> {
|
|
13
|
+
routeName: string;
|
|
14
|
+
routeParams?: P;
|
|
15
|
+
routeOptions?: NavigationOptions;
|
|
16
|
+
activeClassName?: string;
|
|
17
|
+
activeStrict?: boolean;
|
|
18
|
+
ignoreQueryParams?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* URL fragment (decoded form, no leading "#") (#532).
|
|
21
|
+
* - omitted/`undefined` → preserve current fragment on same-route navigation
|
|
22
|
+
* - `""` → clear fragment
|
|
23
|
+
* - non-empty → set fragment
|
|
24
|
+
*
|
|
25
|
+
* Requires a URL plugin (browser-plugin or navigation-plugin) for full
|
|
26
|
+
* round-trip; hash-plugin ignores the prop with a one-time dev warning.
|
|
27
|
+
*/
|
|
28
|
+
hash?: string;
|
|
29
|
+
target?: string;
|
|
30
|
+
onClick?: MouseEventHandler<HTMLAnchorElement>;
|
|
31
|
+
onMouseOver?: MouseEventHandler<HTMLAnchorElement>;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/components/RouterErrorBoundary.d.ts
|
|
35
|
+
interface RouterErrorBoundaryProps {
|
|
36
|
+
readonly children: ReactNode;
|
|
37
|
+
readonly fallback: (error: RouterError, resetError: () => void) => ReactNode;
|
|
38
|
+
readonly onError?: (error: RouterError, toRoute: State | null, fromRoute: State | null) => void;
|
|
39
|
+
}
|
|
40
|
+
declare function RouterErrorBoundary({
|
|
41
|
+
children,
|
|
42
|
+
fallback,
|
|
43
|
+
onError
|
|
44
|
+
}: RouterErrorBoundaryProps): ReactElement;
|
|
45
|
+
//#endregion
|
|
46
|
+
export { RouteContext as i, RouterErrorBoundaryProps as n, LinkProps as r, RouterErrorBoundary as t };
|
|
47
|
+
//# sourceMappingURL=RouterErrorBoundary-BlJmaoyr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RouterErrorBoundary-BlJmaoyr.d.ts","names":[],"sources":["../../src/types.ts","../../src/components/RouterErrorBoundary.tsx"],"mappings":";;;;UAQiB,UAAA,WAAqB,MAAA,GAAS,MAAA;EAC7C,KAAA,EAAO,KAAA,CAAM,CAAA;EACb,aAAA,GAAgB,KAAA;AAAA;AAAA,KAGN,YAAA,WAAuB,MAAA,GAAS,MAAA;EAC1C,SAAA,EAAW,SAAA;AAAA,IACT,UAAA,CAAW,CAAA;AAAA,UAEE,SAAA,WACL,MAAA,GAAS,MAAA,UACX,cAAA,CAAe,iBAAA;EACvB,SAAA;EACA,WAAA,GAAc,CAAA;EACd,YAAA,GAAe,iBAAA;EACf,eAAA;EACA,YAAA;EACA,iBAAA;EAjB6C;;;;;;;AAExB;AAGvB;EAsBE,IAAA;EACA,MAAA;EACA,OAAA,GAAU,iBAAA,CAAkB,iBAAA;EAC5B,WAAA,GAAc,iBAAA,CAAkB,iBAAA;AAAA;;;UCxBjB,wBAAA;EAAA,SACN,QAAA,EAAU,SAAA;EAAA,SACV,QAAA,GAAW,KAAA,EAAO,WAAA,EAAa,UAAA,iBAA2B,SAAA;EAAA,SAC1D,OAAA,IACP,KAAA,EAAO,WAAA,EACP,OAAA,EAAS,KAAA,SACT,SAAA,EAAW,KAAA;AAAA;AAAA,iBAIC,mBAAA,CAAA;EACd,QAAA;EACA,QAAA;EACA;AAAA,GACC,wBAAA,GAA2B,YAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`./useRoute-Dudo2K_0.js`);let t=require(`react`),n=require(`@real-router/core`),r=require(`@real-router/route-utils`),i=require(`react/jsx-runtime`),a=require(`@real-router/sources`),o=require(`@real-router/core/api`);const s=()=>{let n=(0,t.useContext)(e.s);if(!n)throw Error(`useRouter must be used within a RouterProvider`);return n};function c(e){let r=s(),i=(0,t.useMemo)(()=>(0,a.createRouteNodeSource)(r,e),[r,e]),o=(0,t.useSyncExternalStore)(i.subscribe,i.getSnapshot,i.getSnapshot),c=(0,n.getNavigator)(r);return(0,t.useMemo)(()=>({navigator:c,route:o.route,previousRoute:o.previousRoute}),[c,o])}const l=`data-real-router-announcer`,u=Object.freeze({destroy:()=>{}});function d(e,t){if(typeof document>`u`)return u;let n=t?.prefix??`Navigated to `,r=t?.getAnnouncementText,i=!0,a=!1,o=!1,s=``,c=null,l,d=f(),g=(e,t)=>{s=e,clearTimeout(l),d.textContent=e,l=setTimeout(()=>{d.textContent=``,s=``},7e3),h(t)},_=setTimeout(()=>{if(a=!0,c!==null&&!o){let e=c;c=null,g(e,document.querySelector(`h1`))}},100),v=e.subscribe(({route:e})=>{if(i){i=!1;return}requestAnimationFrame(()=>{requestAnimationFrame(()=>{if(o)return;let t=document.querySelector(`h1`),i=m(e,n,r,t);if(!(!i||i===s)){if(!a){c=i;return}g(i,t)}})})});return{destroy(){o=!0,v(),clearTimeout(l),clearTimeout(_),p()}}}function f(){let e=document.querySelector(`[${l}]`);if(e)return e;let t=document.createElement(`div`);return t.setAttribute(`style`,`position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);clip-path:inset(50%);white-space:nowrap;border:0`),t.setAttribute(`aria-live`,`assertive`),t.setAttribute(`aria-atomic`,`true`),t.setAttribute(l,``),(document.body??document.documentElement).prepend(t),t}function p(){document.querySelector(`[${l}]`)?.remove()}function m(e,t,n,r){if(n)try{let t=n(e);if(t)return t}catch(e){console.error(`[real-router] getAnnouncementText threw; falling back to default resolution.`,e)}let i=(r?.textContent??``).trim(),a=e.name.startsWith(`@@`)?``:e.name;return`${t}${i||document.title||a||globalThis.location.pathname}`}function h(e){e&&(e.hasAttribute(`tabindex`)||e.setAttribute(`tabindex`,`-1`),e.focus({preventScroll:!0}))}const g=Object.freeze({destroy:()=>{}});function _(e,t){if(globalThis.window===void 0)return g;let n=t?.mode??`restore`;if(n===`native`)return g;let r=t?.anchorScrolling??!0,i=t?.scrollContainer,a=t?.behavior??`auto`,o=t?.storageKey??`real-router:scroll`,s,c=()=>{if(s!==void 0)return s;try{let e=sessionStorage.getItem(o);s=e?JSON.parse(e):{}}catch{s={}}return s},l=(e,t)=>{try{let n=c();if(n[e]===t)return;n[e]=t,sessionStorage.setItem(o,JSON.stringify(n))}catch{}},u=history.scrollRestoration;try{history.scrollRestoration=`manual`}catch{}let d=()=>{let e=i?.();return e?e.scrollTop:globalThis.scrollY},f=e=>{let t=i?.();t?t.scrollTo({top:e,left:0,behavior:a}):globalThis.scrollTo({top:e,left:0,behavior:a})},p=e=>{let t=e.context?.url?.hash;if(t!==void 0){if(r&&t.length>0){let e=document.getElementById(t);if(e){e.scrollIntoView({behavior:a});return}}f(0);return}let n=globalThis.location.hash;if(r&&n.length>1){let e;try{e=decodeURIComponent(n.slice(1))}catch{e=n.slice(1)}let t=document.getElementById(e);if(t){t.scrollIntoView({behavior:a});return}}f(0)},m=!1,h=!1,_=e=>{try{return y(e)}catch{return h||(h=!0,console.error(`[real-router] scroll-restore: route "${e.name}" has params that cannot be canonicalized (e.g. BigInt or cyclic structure). Scroll position will not be captured or restored for this route.`)),null}},v=e.subscribe(({route:e,previousRoute:t})=>{let r=e.context.navigation;if(t){let e=_(t);e!==null&&l(e,d())}requestAnimationFrame(()=>{if(!m){if(n===`top`){p(e);return}if(!(e.transition.replace||r?.navigationType===`replace`)){if(e.transition.reload||r?.navigationType===`reload`){let t=_(e);f(t===null?0:c()[t]??0);return}if(r?.direction===`back`||r?.navigationType===`traverse`){let t=_(e);f(t===null?0:c()[t]??0);return}p(e)}}})}),b=()=>{let t=e.getState();if(t){let e=_(t);e!==null&&l(e,d())}};return globalThis.addEventListener(`pagehide`,b),{destroy:()=>{if(!m){m=!0,v(),globalThis.removeEventListener(`pagehide`,b);try{history.scrollRestoration=u}catch{}}}}}const v=new WeakMap;function y(e){let t=v.get(e);if(t!==void 0)return t;let n=`${e.name}:${b(e.params)}`;return v.set(e,n),n}function b(e){return JSON.stringify(e,x)}function x(e,t){if(typeof t==`function`)return`<fn>`;if(typeof t==`symbol`)return`<sym>`;if(typeof t==`object`&&t&&!Array.isArray(t)){let e=Object.create(null),n=Object.keys(t).sort((e,t)=>e.localeCompare(t));for(let r of n)e[r]=t[r];return e}return t}const S=Object.freeze({destroy:()=>{}});function C(e){if(typeof document>`u`||typeof document.startViewTransition!=`function`)return S;let t=null,n=null,r=!1,i=()=>{t?.(),t=null},a=e.subscribeLeave(({signal:e})=>{if(!e.aborted)return r=!1,i(),new Promise(a=>{let o=new Promise(e=>{t=e});e.addEventListener(`abort`,()=>{r||(i(),n?.skipTransition?.(),a())},{once:!0});try{n=document.startViewTransition(()=>(a(),o))}catch{i(),a()}})}),o=e.subscribe(()=>{let e=t;r=!0,t=null,e===null?n=null:setTimeout(()=>{e(),n=null},0)});return{destroy:()=>{a(),o(),n?.skipTransition?.(),n=null,i()}}}function w(e){return e.button===0&&!e.metaKey&&!e.altKey&&!e.ctrlKey&&!e.shiftKey}const T=/%[\dA-Fa-f]{2}/;function E(e){if(T.test(e))try{return encodeURI(decodeURIComponent(e)).replaceAll(`#`,`%23`)}catch{}return encodeURI(e).replaceAll(`#`,`%23`)}function D(e,t,n,r){try{let i=r?.hash,a;i!==void 0&&(a=i.startsWith(`#`)?i.slice(1):i);let o=e.buildUrl;if(o){let e=o(t,n,a===void 0?void 0:{hash:a});if(typeof e==`string`&&e.length>0)return e}let s=e.buildPath(t,n);if(typeof s!=`string`||s.length===0){console.error(`[real-router] Route "${t}" yielded an empty path. The element will render without an href attribute.`);return}return a?`${s}#${E(a)}`:s}catch{console.error(`[real-router] Route "${t}" is not defined. The element will render without an href attribute.`);return}}function O(e,t,n,r,i){let a={...i};r!==void 0&&(a.hash=r);let o=e.getState();if(o?.name===t&&N(o.params,n)){let e=o.context?.url?.hash??``;e!==(r??e)&&(a.force=!0,a.hashChange=!0)}return e.navigate(t,n,a)}const k=/\s/,A=/\S+/g;function j(e){return e?k.test(e)?e.match(A)??[]:[e]:[]}function M(e,t,n){if(e&&t){let e=j(t);if(e.length===0)return n??void 0;if(!n)return e.join(` `);let r=j(n),i=new Set(r);for(let t of e)i.has(t)||(i.add(t),r.push(t));return r.join(` `)}return n??void 0}function N(e,t){if(Object.is(e,t))return!0;if(!e||!t)return!1;let n=Object.keys(e);if(n.length!==Object.keys(t).length)return!1;let r=e,i=t;for(let e of n)if(!Object.prototype.hasOwnProperty.call(t,e)||!Object.is(r[e],i[e]))return!1;return!0}function P(e,n,r=!1,i=!0,o){let c=s(),l=(0,t.useMemo)(()=>(0,a.createActiveRouteSource)(c,e,n,{strict:r,ignoreQueryParams:i,...o!==void 0&&{hash:o}}),[c,e,n,r,i,o]);return(0,t.useSyncExternalStore)(l.subscribe,l.getSnapshot,l.getSnapshot)}function F({children:e,fallback:n,onError:r}){let o=s(),c=(0,t.useMemo)(()=>(0,a.createDismissableError)(o),[o]),l=(0,t.useSyncExternalStore)(c.subscribe,c.getSnapshot,c.getSnapshot),u=(0,t.useRef)(r);return(0,t.useLayoutEffect)(()=>{u.current=r}),(0,t.useEffect)(()=>{l.error&&u.current?.(l.error,l.toRoute,l.fromRoute)},[l.version]),(0,i.jsxs)(i.Fragment,{children:[e,l.error?n(l.error,l.resetError):null]})}const I=()=>{let n=(0,t.useContext)(e.a);if(!n)throw Error(`useNavigator must be used within a RouterProvider`);return n},L=()=>(0,r.getRouteUtils)((0,o.getPluginApi)(s()).getTree());function R(){let e=(0,a.getTransitionSource)(s());return(0,t.useSyncExternalStore)(e.subscribe,e.getSnapshot,e.getSnapshot)}const z=({router:r,children:o,announceNavigation:s,scrollRestoration:c,viewTransitions:l})=>{(0,t.useEffect)(()=>{if(!s)return;let e=d(r);return()=>{e.destroy()}},[s,r]);let u=c?.mode,f=c?.anchorScrolling,p=c?.behavior,m=c?.storageKey,h=c!==void 0;(0,t.useEffect)(()=>{if(!h)return;let e=_(r,{mode:u,anchorScrolling:f,behavior:p,storageKey:m,scrollContainer:c.scrollContainer});return()=>{e.destroy()}},[r,h,u,f,p,m]),(0,t.useEffect)(()=>{if(!l)return;let e=C(r);return()=>{e.destroy()}},[r,l]);let g=(0,t.useMemo)(()=>(0,n.getNavigator)(r),[r]),v=(0,t.useMemo)(()=>(0,a.createRouteSource)(r),[r]),y=(0,t.useSyncExternalStore)(v.subscribe,v.getSnapshot,v.getSnapshot),b=(0,t.useMemo)(()=>({navigator:g,route:y.route,previousRoute:y.previousRoute}),[g,y]);return(0,i.jsx)(e.s.Provider,{value:r,children:(0,i.jsx)(e.a.Provider,{value:g,children:(0,i.jsx)(e.o.Provider,{value:b,children:o})})})};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return F}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return P}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return L}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return z}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return N}});
|
|
2
|
+
//# sourceMappingURL=RouterProvider-DGrUfyLj.js.map
|